openvibers/source.openvibe.games Commits
hella changes
Changes
BBinary files a/game/openvibe.games/bin/client.dll and b/game/openvibe.games/bin/client.dll differBBinary files a/game/openvibe.games/bin/server.dll and b/game/openvibe.games/bin/server.dll differnnew file mode 100644BBinary files /dev/null and b/game/openvibe.games/bin/x64/client.dll differnnew file mode 100644BBinary files /dev/null and b/game/openvibe.games/bin/x64/server.dll differ@@@ -13,7 +13,7 @@ bind "e" "+use"bind "f" "impulse 100"bind "g" "dropprimary"bind "p" "headtrack_reset_home_pos"--bind "q" "lastinv"++bind "q" "say !q"bind "r" "+reload"bind "s" "+back"bind "t" "impulse 201"@@@ -25,7 +25,7 @@ bind "z" "+zoom"bind "[" "invprev"bind "]" "invnext"bind "'" "+moveup"--bind "`" "toggleconsole"++bind "`" "ov_console"bind "," "+moveleft"bind "." "+moveright"bind "/" "+movedown"@@@ -47,19 +47,34 @@ bind "UPARROW" "+forward"bind "LEFTARROW" "+left"bind "DOWNARROW" "+back"bind "RIGHTARROW" "+right"++bind "F1" "ov_menu_main"bind "F3" "askconnect_accept"bind "F5" "jpeg"bind "F6" "save quick"bind "F7" "load quick"bind "F9" "vr_toggle"--bind "F10" "quit prompt"++bind "F10" "ov_console"bind "MOUSE1" "+attack"bind "MOUSE2" "+attack2"bind "MWHEELUP" "invprev"bind "MWHEELDOWN" "invnext"++r_lightmap_bicubic_set "1"++cl_chatfilter_version "1"++m_pitch "0.022000"++m_rawinput_onetime_reset "1"++mat_software_aa_strength "0.000000"++mat_software_aa_strength_vgui "1.000000"++cl_playermodel "models/combine_soldier.mdl"++ov_join_token "local-dev-hub"++sv_skyname "sky_day01_01"++snd_surround_speakers "2"++snd_mute_losefocus "0"name "Goosely"--cl_logofile "materials/vgui/logos/spray.vtf"++cl_logofile "materials/vgui/logos/spray_canned.vtf"mat_colorcorrection "1"++con_enable "1"r_shadowrendertotexture "1"r_waterforceexpensive "1"mat_motion_blur_enabled "1"++mat_queue_mode "0"++mat_hdr_level "0"nnew file mode 100644BBinary files /dev/null and b/game/openvibe.games/maps/graphs/ov_hub.ain differ@@@ -10,16 +10,23 @@ fi"$ROOT/tools/dev-db-up.sh"--if tmux has-session -t ov-api 2>/dev/null; then-- tmux kill-session -t ov-api--fi--if tmux has-session -t ov-client-ui 2>/dev/null; then-- tmux kill-session -t ov-client-ui--fi++for session in ov-api ov-client-ui ov-runtime-server ov-runtime-client; do++ if tmux has-session -t "$session" 2>/dev/null; then++ tmux kill-session -t "$session"++ fi++donetmux new-session -d -s ov-api "$ROOT/tools/dev-api.sh"tmux new-session -d -s ov-client-ui "$ROOT/tools/run-client-ui.sh"++# GModJS Node runtimes: back the GUI console (SSE logs + eval + npm on++# 41997/41996) and serve as the game bridge when ov_js_backend is "node".++OV_RUNTIME_MODE="${OPENVIBE_RUNTIME_MODE:-hub}"++tmux new-session -d -s ov-runtime-server \++ "node '$ROOT/engine/openvibe-js-runtime/ov-runtime.js' --realm server --mode $OV_RUNTIME_MODE --root '$ROOT'"++tmux new-session -d -s ov-runtime-client \++ "node '$ROOT/engine/openvibe-js-runtime/ov-runtime.js' --realm client --mode $OV_RUNTIME_MODE --root '$ROOT'"++echo "[openvibe] waiting for api"for _ in {1..60}; doif curl -fsS http://127.0.0.1:3000/health >/dev/null 2>&1; thenddeleted file mode 100755@@@ -1,441 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"--STAMP="$(date +%Y%m%d-%H%M%S)"----cd "$ROOT"----backup_file() {-- local file="$1"-- [[ -f "$file" ]] && cp "$file" "$file.bak.$STAMP"--}----echo "[openvibe] fixing duplicate ov_js_cmd and canonicalizing embedded JS server bridge"----mkdir -p sdk/openvibe/server/hl2mp----backup_file sdk/openvibe/server/hl2mp/openvibe_js_server.cpp--backup_file sdk/openvibe/server/hl2mp/openvibe_server.cpp----cat > sdk/openvibe/server/hl2mp/openvibe_js_server.cpp <<'CPP'--#include "cbase.h"--#include "hl2mp_player.h"--#include "openvibe_js_server.h"--#include "openvibe/ov_js_runtime.h"--#include "openvibe/ov_js_player.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",-- "1",-- FCVAR_GAMEDLL,-- "Enable OpenVibe JavaScript runtime." );----static COpenVibeJSRuntime g_OVServerJS;--static bool g_OVServerJSStarted = false;----static bool OpenVibeJS_IsRunning()--{-- return ov_js_enabled.GetBool() && g_OVServerJS.Context() != nullptr;--}----static void OpenVibeJS_EnsureStarted()--{-- if ( g_OVServerJSStarted )-- return;---- g_OVServerJSStarted = true;---- if ( !ov_js_enabled.GetBool() )-- {-- Msg( "[OV JS] disabled by ov_js_enabled=0\n" );-- return;-- }---- if ( g_OVServerJS.Init( true, ov_mode.GetString() ) )-- {-- Msg( "[OV JS] server runtime initialized for mode '%s'\n", ov_mode.GetString() );-- g_OVServerJS.CallHookVoid( "Initialize" );---- JSContext *ctx = g_OVServerJS.Context();-- JSValue mapName = JS_NewString( ctx, gpGlobals ? STRING( gpGlobals->mapname ) : "" );-- JSValueConst argv[] = { mapName };-- g_OVServerJS.CallHookVoid( "MapInitialize", 1, argv );-- JS_FreeValue( ctx, mapName );-- }-- else-- {-- Warning( "[OV JS] server runtime failed to initialize\n" );-- }--}----static void OpenVibeJS_CallConsoleCommand( const char *pszCommand )--{-- OpenVibeJS_EnsureStarted();---- if ( !OpenVibeJS_IsRunning() )-- {-- Warning( "[OV JS] runtime is not running; command ignored: %s\n", pszCommand ? pszCommand : "" );-- return;-- }---- JSContext *ctx = g_OVServerJS.Context();-- JSValue command = JS_NewString( ctx, pszCommand ? pszCommand : "" );-- JSValueConst argv[] = { command };---- g_OVServerJS.CallHookVoid( "ConsoleCommand", 1, argv );---- JS_FreeValue( ctx, command );--}----void OpenVibeJS_ServerInit()--{-- OpenVibeJS_EnsureStarted();--}----void OpenVibeJS_ServerShutdown()--{-- if ( OpenVibeJS_IsRunning() )-- g_OVServerJS.CallHookVoid( "Shutdown" );---- g_OVServerJS.Shutdown();-- g_OVServerJSStarted = false;--}----void OpenVibeJS_ServerThink()--{-- OpenVibeJS_EnsureStarted();---- if ( !OpenVibeJS_IsRunning() )-- return;---- g_OVServerJS.CallHookVoid( "Think" );--}----void OpenVibeJS_Server_PlayerInitialSpawn( CHL2MP_Player *player )--{-- OpenVibeJS_EnsureStarted();---- if ( !OpenVibeJS_IsRunning() || !player )-- return;---- JSContext *ctx = g_OVServerJS.Context();-- JSValue ply = OVJS_NewPlayer( ctx, player );---- JSValueConst argv[] = { ply };-- g_OVServerJS.CallHookVoid( "PlayerInitialSpawn", 1, argv );---- JS_FreeValue( ctx, ply );--}----void OpenVibeJS_Server_PlayerSpawn( CHL2MP_Player *player )--{-- OpenVibeJS_EnsureStarted();---- if ( !OpenVibeJS_IsRunning() || !player )-- return;---- JSContext *ctx = g_OVServerJS.Context();-- JSValue ply = OVJS_NewPlayer( ctx, player );---- JSValueConst argv[] = { ply };-- g_OVServerJS.CallHookVoid( "PlayerSpawn", 1, argv );---- JS_FreeValue( ctx, ply );--}----void OpenVibeJS_Server_PlayerDeath( CHL2MP_Player *victim, CBaseEntity *attacker, CBaseEntity *inflictor )--{-- OpenVibeJS_EnsureStarted();---- if ( !OpenVibeJS_IsRunning() || !victim )-- return;---- JSContext *ctx = g_OVServerJS.Context();---- JSValue jsVictim = OVJS_NewPlayer( ctx, victim );-- JSValue jsAttacker = JS_NULL;---- if ( attacker && attacker->IsPlayer() )-- jsAttacker = OVJS_NewPlayer( ctx, ToHL2MPPlayer( static_cast<CBasePlayer *>( attacker ) ) );---- JSValueConst argv[] = { jsVictim, jsAttacker };-- g_OVServerJS.CallHookVoid( "PlayerDeath", 2, argv );---- JS_FreeValue( ctx, jsVictim );-- JS_FreeValue( ctx, jsAttacker );--}----void OpenVibeJS_Server_PlayerDisconnected( CHL2MP_Player *player )--{-- OpenVibeJS_EnsureStarted();---- if ( !OpenVibeJS_IsRunning() || !player )-- return;---- JSContext *ctx = g_OVServerJS.Context();-- JSValue ply = OVJS_NewPlayer( ctx, player );---- JSValueConst argv[] = { ply };-- g_OVServerJS.CallHookVoid( "PlayerDisconnected", 1, argv );---- JS_FreeValue( ctx, ply );--}----bool OpenVibeJS_Server_PlayerSay( CHL2MP_Player *player, const char *text )--{-- OpenVibeJS_EnsureStarted();---- if ( !OpenVibeJS_IsRunning() || !player || !text )-- return false;---- JSContext *ctx = g_OVServerJS.Context();---- JSValue ply = OVJS_NewPlayer( ctx, player );-- JSValue msg = JS_NewString( ctx, text );---- JSValueConst argv[] = { ply, msg };---- bool value = false;-- bool returned = g_OVServerJS.CallHookBool( "PlayerSay", &value, 2, argv );---- JS_FreeValue( ctx, ply );-- JS_FreeValue( ctx, msg );---- return returned && value == false;--}----static void OV_JSReload_f()--{-- OpenVibeJS_ServerShutdown();-- OpenVibeJS_ServerInit();-- Msg( "[OV JS] reloaded\n" );--}----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=%d running=%d mode=%s\n",-- ov_js_enabled.GetBool() ? 1 : 0,-- g_OVServerJSStarted ? 1 : 0,-- OpenVibeJS_IsRunning() ? 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 )--{-- if ( args.ArgC() < 2 )-- {-- Msg( "Usage: ov_js_fire <HookName>\n" );-- return;-- }---- OpenVibeJS_EnsureStarted();---- if ( !OpenVibeJS_IsRunning() )-- {-- Warning( "[OV JS] runtime is not running\n" );-- return;-- }---- g_OVServerJS.CallHookVoid( args[1] );--}----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 )--{-- if ( args.ArgC() < 2 )-- {-- Msg( "Usage: ov_js_cmd <text>\n" );-- return;-- }---- OpenVibeJS_CallConsoleCommand( 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--);--CPP----echo "[openvibe] canonical openvibe_js_server.cpp written"----echo "[openvibe] removing stale legacy Node bridge remnants from openvibe_server.cpp"----python3 <<'PY'--from pathlib import Path--import re----p = Path("sdk/openvibe/server/hl2mp/openvibe_server.cpp")--s = p.read_text()----# Remove legacy COpenVibeJsBridge class, if present.--s = re.sub(-- r'\n// ={10,}\n// OpenVibe\.JS - Scripting Engine C\+\+ Bridge\n// ={10,}\n\nclass COpenVibeJsBridge\b.*?\nstatic COpenVibeJsBridge g_OpenVibeJsBridge;\n',-- '\n',-- s,-- flags=re.S,--)----# More tolerant fallback if banner text differs.--s = re.sub(-- r'\nclass COpenVibeJsBridge\b.*?\nstatic COpenVibeJsBridge g_OpenVibeJsBridge;\n',-- '\n',-- s,-- flags=re.S,--)----# Remove old Node-bridge ov_js_cmd command if it survived.--s = re.sub(-- r'\nstatic void OV_JsCmd_f\s*\([^)]*\)\s*\{.*?\n\}\n\nstatic ConCommand ov_js_cmd\s*\(.*?\);\n',-- '\n',-- s,-- flags=re.S,--)----# Remove obvious unused Node/socket includes from our tracked patch file.--for inc in [-- '#include <thread>\n',-- '#include <mutex>\n',-- '#include <queue>\n',-- '#include <string>\n',-- '#include <sstream>\n',-- '#include <sys/socket.h>\n',-- '#include <sys/un.h>\n',-- '#include <unistd.h>\n',--]:-- s = s.replace(inc, '')----# Ensure embedded JS include exists.--if '#include "openvibe_js_server.h"' not in s:-- s = re.sub(-- r'(#include\s+"hl2mp_player\.h"\s*\n)',-- r'\1#include "openvibe_js_server.h"\n',-- s,-- count=1,-- )----p.write_text(s)--PY----echo "[openvibe] removing duplicate ov_js_cmd from already-applied SDK file if present"----SDK_SERVER_JS="$SDK/src/game/server/hl2mp/openvibe_js_server.cpp"--if [[ -f "$SDK_SERVER_JS" ]]; then-- cp "$SDK_SERVER_JS" "$SDK_SERVER_JS.bak.$STAMP"-- cp sdk/openvibe/server/hl2mp/openvibe_js_server.cpp "$SDK_SERVER_JS"--fi----echo "[openvibe] ensure JS ConsoleCommand handlers are present"----mkdir -p game/openvibe.games/js/gamemodes/base game/openvibe.games/js/gamemodes/hub----python3 <<'PY'--from pathlib import Path----def ensure_console_handler(path: str, marker: str, handler_src: str):-- p = Path(path)-- s = p.read_text() if p.exists() else "const GM = {\n};\n\ngamemode.set(GM);\n"-- if marker in s:-- return-- idx = s.rfind("};")-- if idx == -1:-- s += "\n" + handler_src + "\n"-- else:-- before = s[:idx].rstrip()-- after = s[idx:]-- if before.endswith("{"):-- before = before + "\n" + handler_src.strip()-- else:-- before = before + ",\n\n" + handler_src.strip()-- s = before + "\n" + after-- p.write_text(s)----ensure_console_handler(-- "game/openvibe.games/js/gamemodes/base/server.js",-- "Base ConsoleCommand",-- '''ConsoleCommand(text) {-- OV.log(`Base ConsoleCommand: ${text}`);-- }'''--)----ensure_console_handler(-- "game/openvibe.games/js/gamemodes/hub/server.js",-- "Hub ConsoleCommand",-- '''ConsoleCommand(text) {-- OV.log(`Hub ConsoleCommand: ${text}`);-- if (text === "smoke") {-- OV.broadcast("Embedded JS smoke command worked.");-- }-- }'''--)--PY----echo "[openvibe] apply SDK patch"--tools/apply-openvibe-sdk.sh----echo "[openvibe] verify only one ov_js_cmd exists in SDK copied source"--grep -n "ConCommand ov_js_cmd" "$SDK/src/game/server/hl2mp/openvibe_js_server.cpp" || true--count="$(grep -c "ConCommand ov_js_cmd" "$SDK/src/game/server/hl2mp/openvibe_js_server.cpp" || true)"--if [[ "$count" != "1" ]]; then-- echo "[openvibe] ERROR: expected exactly one ov_js_cmd, got $count" >&2-- exit 1--fi----echo "[openvibe] build SDK"--tools/build-sdk-linux.sh 2>&1 | tee "$HOME/ov-build.log"----echo "[openvibe] setup OpenVibe bin"--if [[ -x tools/setup-openvibe-bin.sh ]]; then-- tools/setup-openvibe-bin.sh--else-- echo "[openvibe] WARNING: tools/setup-openvibe-bin.sh not found/executable; skipping"--fi----echo--echo "[openvibe] phase build complete."--echo--echo "Next runtime test:"--echo " OPENVIBE_SRCDS_MAP_DELAY=3 tools/dev-up.sh"--echo--echo "Server console tests:"--echo " ov_js_status"--echo " ov_js_fire Initialize"--echo " ov_js_cmd smoke"--echo--echo "Client tests:"--echo " connect 127.0.0.1:27015"--echo " say !js"--echo " say !hp"--echo " say !players"ddeleted file mode 100755@@@ -1,245 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----say() { echo "[openvibe] $*"; }--warn() { echo "[openvibe warn] $*" >&2; }----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--cd "$ROOT"--BRANCH="$(git rev-parse --abbrev-ref HEAD)"--WORKFLOW="windows-source-sdk-dlls.yml"----say "fix GitHub repo slug + clean committed diagnostics + rerun Windows DLL build"--say "root=$ROOT"--say "branch=$BRANCH"----mkdir -p tools game/openvibe.games/bin----cat > tools/openvibe-gh-repo.sh <<'EOS'--#!/usr/bin/env bash--set -euo pipefail--url="${1:-$(git config --get remote.origin.url)}"--url="${url#[email protected]:}"--url="${url#ssh://[email protected]/}"--url="${url#https://github.com/}"--url="${url#http://github.com/}"--url="${url%.git}"--url="${url%/}"--if [[ ! "$url" =~ ^[^/]+/[^/]+$ ]]; then-- echo "Could not normalize GitHub repo from remote.origin.url: ${url}" >&2-- exit 1--fi--echo "$url"--EOS--chmod +x tools/openvibe-gh-repo.sh----REPO="$(tools/openvibe-gh-repo.sh)"--say "repo=$REPO"----# Make sure debug outputs never get committed again.--touch .gitignore--python3 - <<'PY'--from pathlib import Path--p = Path('.gitignore')--lines = p.read_text().splitlines() if p.exists() else []--want = [-- '',-- '# OpenVibe local/generated build diagnostics',-- 'artifacts/',-- '_deps/',-- '*.tmp',-- '*.log',--]--existing = set(lines)--changed = False--for line in want:-- if line and line not in existing:-- lines.append(line)-- existing.add(line)-- changed = True-- elif line == '' and (not lines or lines[-1] != ''):-- lines.append(line)-- changed = True--if changed:-- p.write_text('\n'.join(lines).rstrip() + '\n')--PY----# Remove previously committed workflow debug artifacts from git, but keep local files if present.--if git ls-files artifacts | grep -q .; then-- say "removing committed artifacts/ from git index"-- git rm -r --cached artifacts >/dev/null || true--fi----cat > tools/windows-workflow-debug-and-install.sh <<'EOS'--#!/usr/bin/env bash--set -euo pipefail--say() { echo "[openvibe] $*"; }--warn() { echo "[openvibe warn] $*" >&2; }--ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--cd "$ROOT"--WORKFLOW="${OPENVIBE_WORKFLOW:-windows-source-sdk-dlls.yml}"--BRANCH="${OPENVIBE_BRANCH:-$(git rev-parse --abbrev-ref HEAD)}"--REPO="${OPENVIBE_REPO:-$(tools/openvibe-gh-repo.sh)}"--RUN_ID="${1:-}"----if ! command -v gh >/dev/null 2>&1; then-- echo "GitHub CLI gh is missing. Install/auth it first: sudo apt install gh && gh auth login" >&2-- exit 1--fi----if [[ -z "$RUN_ID" ]]; then-- RUN_ID="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"--fi--if [[ -z "$RUN_ID" || "$RUN_ID" == "null" ]]; then-- echo "Could not find a workflow run for $REPO $WORKFLOW on $BRANCH" >&2-- exit 1--fi----OUT="$ROOT/artifacts/windows-workflow-debug/run-${RUN_ID}-artifacts"--rm -rf "$OUT"--mkdir -p "$OUT"----say "repo=$REPO"--say "run=$RUN_ID"--say "downloading logs/artifacts into $OUT"----gh run view "$RUN_ID" --repo "$REPO" --log > "$OUT/full-run.log" 2>&1 || true--gh run download "$RUN_ID" --repo "$REPO" --dir "$OUT" >/dev/null 2>&1 || true--find "$OUT" -type f | sort > "$OUT/file-list.txt"----say "likely errors:"--if ! grep -RInE "error |error:|fatal:|failed|Exception|No patched|MSB[0-9]+|LNK[0-9]+|C[0-9]{4}" "$OUT" | head -n 80; then-- warn "no obvious errors found in downloaded logs"--fi----say "artifact files:"--cat "$OUT/file-list.txt"----CLIENT=""--SERVER=""--while IFS= read -r f; do-- case "$(basename "$f")" in-- client.dll) CLIENT="$f" ;;-- server.dll) SERVER="$f" ;;-- esac--done < <(find "$OUT" -type f \( -iname 'client.dll' -o -iname 'server.dll' \) | sort)----if [[ -n "$CLIENT" && -n "$SERVER" ]]; then-- say "candidate client=$CLIENT"-- say "candidate server=$SERVER"-- if strings "$CLIENT" | grep -Eq 'ov_join|ov_menu|OpenVibe'; then-- mkdir -p game/openvibe.games/bin-- cp -f "$CLIENT" game/openvibe.games/bin/client.dll-- cp -f "$SERVER" game/openvibe.games/bin/server.dll-- say "installed patched Windows DLLs"-- if [[ -x tools/verify-openvibe-dll-content.sh ]]; then-- tools/verify-openvibe-dll-content.sh || true-- else-- strings game/openvibe.games/bin/client.dll | grep -E 'ov_join|ov_menu|OpenVibe' | head || true-- fi-- exit 0-- else-- warn "DLLs were present but do not contain OpenVibe strings; not installing stale/stock DLLs"-- fi--else-- warn "no client.dll/server.dll artifact available yet"--fi----say "next useful tails:"--for f in \-- "$OUT/full-run.log" \-- "$OUT/openvibe-windows-build-debug/bootstrap-source-sdk-2013.log" \-- "$OUT/openvibe-windows-build-debug/bootstrap-curl-master-zip.log" \-- "$OUT/openvibe-windows-build-debug/bootstrap-git-clone-master.log" \-- "$OUT/openvibe-windows-build-debug/build-sdk-windows.log"; do-- if [[ -f "$f" ]]; then-- echo "----- $f -----"-- tail -n 80 "$f" || true-- fi--done--exit 2--EOS--chmod +x tools/windows-workflow-debug-and-install.sh----cat > tools/trigger-windows-dll-build-clean.sh <<'EOS'--#!/usr/bin/env bash--set -euo pipefail--say() { echo "[openvibe] $*"; }--ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--cd "$ROOT"--WORKFLOW="${OPENVIBE_WORKFLOW:-windows-source-sdk-dlls.yml}"--BRANCH="${OPENVIBE_BRANCH:-$(git rev-parse --abbrev-ref HEAD)}"--REPO="${OPENVIBE_REPO:-$(tools/openvibe-gh-repo.sh)}"----if ! command -v gh >/dev/null 2>&1; then-- echo "GitHub CLI gh is missing. Install/auth it first: sudo apt install gh && gh auth login" >&2-- exit 1--fi----gh auth status >/dev/null--say "repo=$REPO"--say "workflow=$WORKFLOW"--say "branch=$BRANCH"----git push--say "triggering workflow"--gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$BRANCH" -f diagnostics=1----say "waiting for new run to appear"--sleep 8--RUN_ID="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"--say "watching run $RUN_ID"--if gh run watch "$RUN_ID" --repo "$REPO" --exit-status; then-- say "workflow passed"--else-- warn_msg="workflow failed; downloading diagnostics anyway"-- echo "[openvibe warn] $warn_msg" >&2--fi----tools/windows-workflow-debug-and-install.sh "$RUN_ID" || true--EOS--chmod +x tools/trigger-windows-dll-build-clean.sh----# Try to patch older helper scripts so the .git suffix never breaks gh API calls again.--for f in \-- tools/request-windows-dll-build.sh \-- tools/gh-windows-build-and-install.sh \-- tools/next-phase-platform-build-and-fetch.sh \-- tools/fix-windows-bootstrap-download-master-and-rerun.sh \-- tools/fix-windows-bootstrap-mp-branch-and-rerun.sh \-- tools/fix-windows-sdk-bootstrap-and-real-dlls.sh \-- tools/fix-windows-workflow-diagnostics-and-rerun.sh; do-- [[ -f "$f" ]] || continue-- python3 - "$f" <<'PY'--from pathlib import Path--import re, sys--p = Path(sys.argv[1])--s = p.read_text()--# Add a harmless normalization immediately after simple REPO assignments derived from remote.origin.url.--s = re.sub(r'(REPO="\$\([^\n]*remote\.origin\.url[^\n]*\)"\n)', r'\1REPO="${REPO%.git}"\n', s)--s = re.sub(r"(REPO='\$\([^\n]*remote\.origin\.url[^\n]*\)'\n)", r"\1REPO=\"${REPO%.git}\"\n", s)--# Also strip .git after any existing REPO assignment block if not already nearby.--if 'REPO="${REPO%.git}"' not in s and 'gh workflow' in s:-- s = s.replace('WORKFLOW=', 'REPO="${REPO%.git}"\nWORKFLOW=', 1)--p.write_text(s)--PY-- chmod +x "$f" 2>/dev/null || true--done----say "git status after cleanup/patch"--git status --short----if ! git diff --quiet || ! git diff --cached --quiet; then-- git add .gitignore tools/openvibe-gh-repo.sh tools/windows-workflow-debug-and-install.sh tools/trigger-windows-dll-build-clean.sh tools/*.sh 2>/dev/null || true-- if git diff --cached --quiet; then-- say "nothing staged to commit"-- else-- git commit -m "Fix GitHub workflow repo slug and ignore diagnostics"-- fi--else-- say "no cleanup changes to commit"--fi----git push--say "running clean Windows DLL workflow trigger"--tools/trigger-windows-dll-build-clean.shddeleted file mode 100755@@@ -1,713 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"--STAMP="$(date +%Y%m%d-%H%M%S)"----cd "$ROOT"----echo "[openvibe] fix JS global-scope redeclare and continue command/timer phase"--echo "[openvibe] root=$ROOT"--echo "[openvibe] sdk=$SDK"----backup_file() {-- local file="$1"-- [[ -f "$file" ]] && cp "$file" "$file.bak.$STAMP"--}----mkdir -p \-- game/openvibe.games/js/core \-- game/openvibe.games/js/gamemodes/base \-- game/openvibe.games/js/gamemodes/hub \-- game/openvibe.games/js/gamemodes/prophunt \-- game/openvibe.games/js/gamemodes/deathrun \-- game/openvibe.games/js/gamemodes/fortwars \-- game/openvibe.games/js/gamemodes/traitortown \-- tools----echo "[openvibe] writing canonical command.js"--backup_file game/openvibe.games/js/core/command.js--cat > game/openvibe.games/js/core/command.js <<'JS'--(function () {-- const commands = new Map();---- function say(target, message) {-- const text = String(message);-- if (target && typeof target.chat === "function") {-- target.chat(text);-- } else {-- OV.log(text);-- }-- }---- function normalizeName(name) {-- return String(name || "").trim().replace(/^[!/]+/, "").toLowerCase();-- }---- function parse(text) {-- const raw = String(text || "").trim();-- const withoutPrefix = raw.replace(/^[!/]+/, "");-- const parts = withoutPrefix.length ? withoutPrefix.split(/\s+/) : [];-- const name = normalizeName(parts.shift() || "");-- return { raw, name, args: parts };-- }---- globalThis.command = {-- add(name, descriptionOrFn, maybeFn) {-- const key = normalizeName(name);-- if (!key) throw new Error("command.add requires command name");---- let description = "";-- let fn = maybeFn;---- if (typeof descriptionOrFn === "function") {-- fn = descriptionOrFn;-- } else {-- description = String(descriptionOrFn || "");-- }---- if (typeof fn !== "function") throw new Error(`command '${key}' requires function`);-- commands.set(key, { name: key, description, fn });-- },---- remove(name) {-- return commands.delete(normalizeName(name));-- },---- has(name) {-- return commands.has(normalizeName(name));-- },---- list() {-- return Array.from(commands.values()).map((entry) => ({-- name: entry.name,-- description: entry.description-- }));-- },---- run(source, text, ...extra) {-- const parsed = parse(text);-- if (!parsed.name) return undefined;---- const entry = commands.get(parsed.name);-- if (!entry) {-- say(source, `Unknown command: ${parsed.name}. Try !help.`);-- return source ? false : undefined;-- }---- try {-- const result = entry.fn(source, parsed.args, parsed.raw, ...extra);-- return result === undefined ? false : result;-- } catch (err) {-- OV.error(`[command:${parsed.name}] ${err && err.stack ? err.stack : err}`);-- say(source, `Command failed: ${parsed.name}`);-- return false;-- }-- }-- };---- hook.add("ConsoleCommand", "OpenVibeCommandConsole", function (text) {-- return command.run(null, text);-- });---- hook.add("PlayerSay", "OpenVibeCommandChat", function (ply, text) {-- const msg = String(text || "").trim();-- if (!msg.startsWith("!")) return undefined;-- return command.run(ply, msg.slice(1));-- });--})();--JS----echo "[openvibe] writing canonical timer.js"--backup_file game/openvibe.games/js/core/timer.js--cat > game/openvibe.games/js/core/timer.js <<'JS'--(function () {-- const timers = new Map();-- let nextId = 1;---- function now() {-- if (typeof OV !== "undefined" && typeof OV.time === "function") return Number(OV.time()) || 0;-- return Date.now() / 1000;-- }---- function makeId() {-- return `timer_${nextId++}`;-- }---- globalThis.timer = {-- create(id, delay, repetitions, fn) {-- if (typeof id !== "string" || !id) throw new Error("timer.create requires id");-- if (typeof fn !== "function") throw new Error("timer.create requires callback");---- const safeDelay = Math.max(0, Number(delay) || 0);-- const reps = repetitions === undefined ? 1 : Number(repetitions);---- timers.set(id, {-- id,-- delay: safeDelay,-- repetitions: reps,-- fn,-- next: now() + safeDelay,-- running: true-- });---- return id;-- },---- simple(delay, fn) {-- return timer.create(makeId(), delay, 1, fn);-- },---- remove(id) {-- return timers.delete(String(id));-- },---- exists(id) {-- return timers.has(String(id));-- },---- count() {-- return timers.size;-- },---- tick() {-- const t = now();---- for (const [id, item] of Array.from(timers.entries())) {-- if (!item.running || t < item.next) continue;---- try {-- item.fn();-- } catch (err) {-- OV.error(`[timer:${id}] ${err && err.stack ? err.stack : err}`);-- }---- if (item.repetitions > 0) item.repetitions -= 1;---- if (item.repetitions === 0) {-- timers.delete(id);-- } else {-- item.next = t + item.delay;-- }-- }-- },---- clear() {-- timers.clear();-- }-- };---- hook.add("Think", "OpenVibeTimerThink", function () {-- timer.tick();-- return undefined;-- });--})();--JS----echo "[openvibe] writing scoped base gamemode"--backup_file game/openvibe.games/js/gamemodes/base/server.js--cat > game/openvibe.games/js/gamemodes/base/server.js <<'JS'--(function () {-- function say(target, message) {-- if (target && typeof target.chat === "function") target.chat(String(message));-- else OV.log(String(message));-- }---- command.add("help", "List available OpenVibe commands.", function (source) {-- const names = command.list().map((entry) => `!${entry.name}`).sort().join(", ");-- say(source, `Commands: ${names}`);-- return false;-- });---- command.add("smoke", "Run embedded JS smoke test.", function (source) {-- const message = "OpenVibe embedded JS smoke test passed.";-- if (source) say(source, message);-- OV.broadcast(message);-- return false;-- });---- command.add("timer_smoke", "Run timer smoke test.", function (source) {-- timer.simple(0, function () {-- OV.broadcast("OpenVibe timer smoke fired.");-- });-- timer.tick();-- say(source, "Timer smoke scheduled.");-- return false;-- });---- const GM = {-- mode: "base",-- name: "OpenVibe Base",---- Initialize() {-- OV.log("Base Initialize fired");-- },---- MapInitialize(mapName) {-- OV.log(`Base MapInitialize: ${mapName}`);-- },---- PlayerInitialSpawn(ply) {-- ply.chat("Welcome to OpenVibe: Source.");-- },---- PlayerSpawn(_ply) {},---- PlayerDeath(_victim, _attacker) {},---- PlayerDisconnected(_ply) {},---- PlayerSay(ply, text) {-- const msg = String(text || "").trim();-- if (msg.startsWith("!")) return command.run(ply, msg.slice(1));-- return undefined;-- },---- ConsoleCommand(text) {-- return command.run(null, text);-- },---- Think() {-- timer.tick();-- }-- };---- gamemode.set(GM);--})();--JS----echo "[openvibe] writing scoped hub gamemode"--backup_file game/openvibe.games/js/gamemodes/hub/server.js--cat > game/openvibe.games/js/gamemodes/hub/server.js <<'JS'--(function () {-- command.add("hub_status", "Print hub runtime status.", function (source) {-- const message = `Hub mode online. Map=${OV.getMapName()} Players=${OV.players().length}`;-- if (source && typeof source.chat === "function") source.chat(message);-- else OV.log(message);-- return false;-- });---- command.add("where", "Show current map/mode.", function (source) {-- const message = `You are on ${OV.getMapName()} in ${OV.getMode()} mode.`;-- if (source && typeof source.chat === "function") source.chat(message);-- else OV.log(message);-- return false;-- });---- const GM = {-- mode: "hub",-- name: "OpenVibe Hub",---- Initialize() {-- OV.log("Hub Initialize fired");-- },---- MapInitialize(mapName) {-- OV.log(`Map initialized: ${mapName}`);-- },---- PlayerInitialSpawn(ply) {-- ply.chat("Welcome to OpenVibe: Source JS runtime.");-- OV.broadcast(`${ply.name()} joined the hub.`);-- },---- PlayerSpawn(_ply) {},---- PlayerSay(ply, text) {-- const msg = String(text || "").trim();---- if (msg === "!js") {-- ply.chat("JavaScript hooks are working.");-- return false;-- }---- if (msg === "!hp") {-- ply.chat(`Health: ${ply.health()}`);-- return false;-- }---- if (msg === "!players") {-- ply.chat(`Players online: ${OV.players().length}`);-- return false;-- }---- if (msg.startsWith("!")) return command.run(ply, msg.slice(1));-- return undefined;-- },---- ConsoleCommand(text) {-- OV.log(`Hub ConsoleCommand: ${text}`);-- return command.run(null, text);-- },---- Think() {-- timer.tick();-- }-- };---- gamemode.set(GM);--})();--JS----echo "[openvibe] writing scoped prototype gamemodes"--cat > game/openvibe.games/js/gamemodes/prophunt/server.js <<'JS'--(function () {-- const props = ["crate", "barrel", "chair", "bucket"];---- function randomProp() {-- return props[Math.floor(Math.random() * props.length)];-- }---- command.add("prop", "Prop Hunt disguise command.", function (ply, args) {-- if (!ply) {-- OV.log("prop command requires player");-- return false;-- }---- const prop = args[0] || randomProp();-- ply.runCommand(`ov_prophunt_disguise ${prop}`);-- return false;-- });---- const GM = {-- mode: "prophunt",-- name: "OpenVibe Prop Hunt",---- Initialize() {-- OV.log("Prop Hunt Initialize fired");-- },---- PlayerInitialSpawn(ply) {-- ply.chat("Prop Hunt: hide as props or hunt them down.");-- },---- PlayerSay(ply, text) {-- const msg = String(text || "").trim();-- if (msg.startsWith("!")) return command.run(ply, msg.slice(1));-- return undefined;-- },---- Think() {-- timer.tick();-- }-- };---- gamemode.set(GM);--})();--JS----cat > game/openvibe.games/js/gamemodes/deathrun/server.js <<'JS'--(function () {-- command.add("finish", "Deathrun finish smoke command.", function (ply) {-- if (ply) ply.chat("Deathrun finish test.");-- return false;-- });---- const GM = {-- mode: "deathrun",-- name: "OpenVibe Deathrun",---- Initialize() {-- OV.log("Deathrun Initialize fired");-- },---- PlayerInitialSpawn(ply) {-- ply.chat("Deathrun: survive the traps and reach the finish.");-- },---- PlayerSay(ply, text) {-- const msg = String(text || "").trim();-- if (msg.startsWith("!")) return command.run(ply, msg.slice(1));-- return undefined;-- },---- Think() {-- timer.tick();-- }-- };---- gamemode.set(GM);--})();--JS----cat > game/openvibe.games/js/gamemodes/fortwars/server.js <<'JS'--(function () {-- const allowed = new Set(["crate", "barrel", "pallet", "fence", "sheet"]);---- command.add("build", "Fort Wars build command.", function (ply, args) {-- if (!ply) {-- OV.log("build command requires player");-- return false;-- }---- const part = args[0] || "crate";-- if (!allowed.has(part)) {-- ply.chat("Allowed: crate, barrel, pallet, fence, sheet");-- return false;-- }---- ply.runCommand(`ov_fortwars_spawn ${part}`);-- return false;-- });---- const GM = {-- mode: "fortwars",-- name: "OpenVibe Fort Wars",---- Initialize() {-- OV.log("Fort Wars Initialize fired");-- },---- PlayerInitialSpawn(ply) {-- ply.chat("Fort Wars: build first, fight second.");-- },---- PlayerSay(ply, text) {-- const msg = String(text || "").trim();-- if (msg.startsWith("!")) return command.run(ply, msg.slice(1));-- return undefined;-- },---- Think() {-- timer.tick();-- }-- };---- gamemode.set(GM);--})();--JS----cat > game/openvibe.games/js/gamemodes/traitortown/server.js <<'JS'--(function () {-- const roles = new Map();---- command.add("role", "Show your Traitor Town role.", function (ply) {-- if (!ply) {-- OV.log("role command requires player");-- return false;-- }---- ply.chat(`Your role: ${roles.get(ply.userId()) || "innocent"}`);-- return false;-- });---- const GM = {-- mode: "traitortown",-- name: "OpenVibe Traitor Town",---- Initialize() {-- OV.log("Traitor Town Initialize fired");-- },---- PlayerInitialSpawn(ply) {-- ply.chat("Traitor Town: find the traitors before they find you.");-- roles.set(ply.userId(), "innocent");-- },---- PlayerSay(ply, text) {-- const msg = String(text || "").trim();-- if (msg.startsWith("!")) return command.run(ply, msg.slice(1));-- return undefined;-- },---- PlayerDeath(victim, _attacker) {-- OV.broadcast(`${victim.name()} died.`);-- },---- Think() {-- timer.tick();-- }-- };---- gamemode.set(GM);--})();--JS----echo "[openvibe] patching runtime core load order"--backup_file sdk/openvibe/shared/ov_js_runtime.cpp--python3 <<'PY'--from pathlib import Path--import re----p = Path("sdk/openvibe/shared/ov_js_runtime.cpp")--s = p.read_text()----new_func = r'''bool COpenVibeJSRuntime::LoadCoreFiles()--{-- if (!LoadFile("js/core/hook.js"))-- return false;---- if (!LoadFile("js/core/gamemode.js"))-- return false;---- if (!LoadFile("js/bridge.js"))-- return false;---- if (!LoadFile("js/core/command.js"))-- return false;---- if (!LoadFile("js/core/timer.js"))-- return false;---- return true;--}'''----s2 = re.sub(-- r'bool\s+COpenVibeJSRuntime::LoadCoreFiles\s*\(\s*\)\s*\{.*?\n\}',-- new_func,-- s,-- count=1,-- flags=re.S,--)----if s2 == s:-- raise SystemExit("Could not patch COpenVibeJSRuntime::LoadCoreFiles")----p.write_text(s2)--PY----echo "[openvibe] writing robust Node JS core smoke test"--backup_file tools/smoke-js-core-node.mjs--cat > tools/smoke-js-core-node.mjs <<'MJS'--import fs from "node:fs";--import path from "node:path";--import vm from "node:vm";----const root = process.env.OPENVIBE_ROOT || process.cwd();--let fakeTime = 0;--const logs = [];--const broadcasts = [];--const chats = [];--const serverCommands = [];----const context = {-- console,-- globalThis: null,-- OV: {-- log: (msg) => logs.push(String(msg)),-- warn: (msg) => logs.push(`WARN ${String(msg)}`),-- error: (msg) => logs.push(`ERROR ${String(msg)}`),-- getMode: () => "hub",-- getMapName: () => "ov_hub",-- time: () => fakeTime,-- broadcast: (msg) => broadcasts.push(String(msg)),-- players: () => [mockPlayer],-- playerByUserId: (id) => Number(id) === 1 ? mockPlayer : null,-- serverCommand: (cmd) => serverCommands.push(String(cmd)),-- fireHook: (...args) => context.gamemode.call(...args),-- reward: () => undefined,-- endMatch: () => undefined-- }--};----const mockPlayer = {-- userId: () => 1,-- entIndex: () => 1,-- steamId: () => "STEAM_0:1:1",-- name: () => "SmokePlayer",-- health: () => 100,-- setHealth: () => undefined,-- team: () => 0,-- setTeam: () => undefined,-- chat: (msg) => chats.push(String(msg)),-- runCommand: (cmd) => serverCommands.push(String(cmd))--};----context.globalThis = context;--vm.createContext(context);----function load(relativePath) {-- const full = path.join(root, relativePath);-- const source = fs.readFileSync(full, "utf8");-- vm.runInContext(source, context, { filename: relativePath });--}----for (const file of [-- "game/openvibe.games/js/core/hook.js",-- "game/openvibe.games/js/core/gamemode.js",-- "game/openvibe.games/js/bridge.js",-- "game/openvibe.games/js/core/command.js",-- "game/openvibe.games/js/core/timer.js",-- "game/openvibe.games/js/gamemodes/base/server.js",-- "game/openvibe.games/js/gamemodes/hub/server.js"--]) {-- load(file);--}----if (!context.hook) throw new Error("hook global missing");--if (!context.gamemode) throw new Error("gamemode global missing");--if (!context.command) throw new Error("command global missing");--if (!context.timer) throw new Error("timer global missing");--if (!context.gamemode.get()) throw new Error("gamemode was not set");----context.gamemode.call("Initialize");--context.gamemode.call("MapInitialize", "ov_hub");--context.gamemode.call("ConsoleCommand", "help");--context.gamemode.call("ConsoleCommand", "smoke");--context.gamemode.call("ConsoleCommand", "timer_smoke");--context.gamemode.call("ConsoleCommand", "hub_status");--context.gamemode.call("PlayerInitialSpawn", mockPlayer);--context.gamemode.call("PlayerSay", mockPlayer, "!help");--context.gamemode.call("PlayerSay", mockPlayer, "!where");--context.gamemode.call("PlayerSay", mockPlayer, "!js");--context.gamemode.call("PlayerSay", mockPlayer, "!hp");--context.gamemode.call("PlayerSay", mockPlayer, "!players");--fakeTime += 1;--context.gamemode.call("Think");--context.timer.tick();----const combined = [...logs, ...broadcasts, ...chats, ...serverCommands].join("\n");----for (const expected of [-- "Hub Initialize fired",-- "OpenVibe embedded JS smoke test passed.",-- "OpenVibe timer smoke fired.",-- "JavaScript hooks are working.",-- "Health: 100",-- "Players online: 1",-- "You are on ov_hub in hub mode."--]) {-- if (!combined.includes(expected)) {-- console.error(combined);-- throw new Error(`missing expected smoke output: ${expected}`);-- }--}----console.log("[openvibe-smoke] JS core command/timer smoke passed");--console.log(`[openvibe-smoke] logs=${logs.length} broadcasts=${broadcasts.length} chats=${chats.length}`);--MJS----echo "[openvibe] run JS core smoke test"--node tools/smoke-js-core-node.mjs----echo "[openvibe] apply SDK patch"--tools/apply-openvibe-sdk.sh----echo "[openvibe] build SDK"--tools/build-sdk-linux.sh 2>&1 | tee "$HOME/ov-build.log"----echo "[openvibe] setup OpenVibe bin"--tools/setup-openvibe-bin.sh----echo--printf '%s\n' "[openvibe] JS command/timer phase complete."--printf '%s\n' ""--printf '%s\n' "Runtime test:"--printf '%s\n' " OPENVIBE_SRCDS_MAP_DELAY=3 tools/dev-up.sh"--printf '%s\n' ""--printf '%s\n' "Server console tests:"--printf '%s\n' " ov_js_status"--printf '%s\n' " ov_js_fire Initialize"--printf '%s\n' " ov_js_cmd help"--printf '%s\n' " ov_js_cmd smoke"--printf '%s\n' " ov_js_cmd timer_smoke"--printf '%s\n' " ov_js_cmd hub_status"--printf '%s\n' ""--printf '%s\n' "Client tests:"--printf '%s\n' " connect 127.0.0.1:27015"--printf '%s\n' " say !help"--printf '%s\n' " say !js"--printf '%s\n' " say !hp"--printf '%s\n' " say !players"--printf '%s\n' " say !where"ddeleted file mode 100755@@@ -1,344 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--cd "$ROOT"----echo "[openvibe] next phase: launcher-aware Source loading + client DLL reality guard"--echo "[openvibe] root=$ROOT"----backup() {-- local f="$1"-- [[ -f "$f" ]] && cp "$f" "$f.bak.$(date +%Y%m%d-%H%M%S)"--}----backup launcher/main.js--backup launcher/preload.js--backup launcher/renderer.js--backup launcher/index.html--backup launcher/styles.css--backup tools/run-client-proton.sh----mkdir -p docs game/openvibe.games/resource game/openvibe.games/cfg launcher/assets----cat > docs/CLIENT_DLL_AND_LOADING.md <<'DOC'--# OpenVibe client DLL + loading screen notes----The Proton launcher runs Windows `hl2.exe`. That Windows executable will not load the Linux--`game/openvibe.games/bin/linux64/client.so` produced by the current Source SDK Linux build.----That means native client commands such as `ov_join`, `ov_menu`, `ov_ui`, and the in-game--CEF/HTML menu only exist when a matching client DLL is actually loaded by the game client.--Until we build a Windows `client.dll` or switch to a native Linux client path, the Electron--launcher is the reliable custom Chromium shell.----This phase keeps the Electron launcher visible during Source startup, shows a clear launch--state, and focuses the Source window only after it has appeared and stayed stable for a few--seconds. It also installs best-effort Source resource overrides for the classic loading dialog.--DOC----cat > game/openvibe.games/cfg/autoexec.cfg <<'CFG'--// OpenVibe: Source client defaults--con_enable 1--developer 1--con_timestamp 1--cl_showfps 0----// These are server-side commands unless a matching client DLL is loaded.--echo "[OpenVibe] autoexec loaded. If ov_join/ov_menu are unknown, the custom client DLL is not loaded."--CFG----cat > game/openvibe.games/resource/LoadingDialog.res <<'RES'--"Resource/LoadingDialog.res"--{-- "LoadingDialog"-- {-- "ControlName" "Frame"-- "fieldName" "LoadingDialog"-- "xpos" "0"-- "ypos" "0"-- "wide" "f0"-- "tall" "f0"-- "autoResize" "0"-- "pinCorner" "0"-- "visible" "1"-- "enabled" "1"-- "tabPosition" "0"-- "settitlebarvisible" "0"-- "PaintBackgroundType" "2"-- "bgcolor_override" "7 8 14 255"-- }---- "OpenVibeTitle"-- {-- "ControlName" "Label"-- "fieldName" "OpenVibeTitle"-- "xpos" "c-260"-- "ypos" "c-72"-- "wide" "520"-- "tall" "42"-- "visible" "1"-- "enabled" "1"-- "labelText" "OPENVIBE: SOURCE"-- "textAlignment" "center"-- "font" "DefaultVeryLarge"-- "fgcolor_override" "0 220 255 255"-- }---- "OpenVibeStatus"-- {-- "ControlName" "Label"-- "fieldName" "OpenVibeStatus"-- "xpos" "c-300"-- "ypos" "c-20"-- "wide" "600"-- "tall" "36"-- "visible" "1"-- "enabled" "1"-- "labelText" "Loading OpenVibe shell, servers, and scripts..."-- "textAlignment" "center"-- "font" "DefaultLarge"-- "fgcolor_override" "220 235 255 255"-- }--}--RES----cat > game/openvibe.games/resource/LoadingProgress.res <<'RES'--"Resource/LoadingProgress.res"--{-- "LoadingProgress"-- {-- "ControlName" "Frame"-- "fieldName" "LoadingProgress"-- "xpos" "0"-- "ypos" "0"-- "wide" "f0"-- "tall" "f0"-- "visible" "1"-- "enabled" "1"-- "settitlebarvisible" "0"-- "PaintBackgroundType" "2"-- "bgcolor_override" "7 8 14 255"-- }--}--RES----python3 <<'PY'--from pathlib import Path--import re----p = Path('launcher/main.js')--s = p.read_text()----if 'OPENVIBE_GAME_WINDOW_READY_TIMEOUT_MS' not in s:-- marker = "let appIsQuitting = false;\n"-- helpers = r'''----const GAME_WINDOW_TITLE = process.env.OPENVIBE_GAME_WINDOW_TITLE || 'OpenVibe: Source';--const GAME_READY_TIMEOUT_MS = Number(process.env.OPENVIBE_GAME_WINDOW_READY_TIMEOUT_MS || 45000);--const GAME_STABLE_MS = Number(process.env.OPENVIBE_GAME_WINDOW_STABLE_MS || 8000);--const HIDE_LAUNCHER_ON_GAME_READY = process.env.OPENVIBE_HIDE_LAUNCHER_ON_GAME_READY === '1';----function sleep(ms) {-- return new Promise((resolve) => setTimeout(resolve, ms));--}----function emitLaunchPhase(phase, message, extra = {}) {-- if (mainWindow && !mainWindow.isDestroyed()) {-- mainWindow.webContents.send('game-launch-phase', { phase, message, ...extra });-- }--}----function safeExec(command) {-- try {-- return execSync(command, { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();-- } catch {-- return '';-- }--}----function findGameWindowId() {-- // Linux desktop helper. If xdotool is not installed, the launcher simply stays visible.-- const escaped = GAME_WINDOW_TITLE.replace(/"/g, '\\"');-- const out = safeExec(`xdotool search --name "${escaped}" 2>/dev/null | tail -n 1`);-- return out || null;--}----function focusGameWindow() {-- const win = findGameWindowId();-- if (!win) return false;-- safeExec(`xdotool windowactivate ${win} 2>/dev/null || true`);-- return true;--}----async function waitForGameWindowReady(timeoutMs = GAME_READY_TIMEOUT_MS) {-- const started = Date.now();-- let firstSeen = 0;---- while (Date.now() - started < timeoutMs) {-- const win = findGameWindowId();-- if (win) {-- if (!firstSeen) {-- firstSeen = Date.now();-- emitLaunchPhase('window-found', 'Source window found. Waiting for it to stop thrashing before switching focus...', { windowId: win });-- }---- if (Date.now() - firstSeen >= GAME_STABLE_MS) {-- return { ready: true, windowId: win, reason: 'window-stable' };-- }-- }---- await sleep(500);-- }---- return { ready: false, windowId: findGameWindowId(), reason: 'timeout' };--}--'''-- s = s.replace(marker, marker + helpers)----old = """ mainWindow?.webContents.send('game-started', gameProcess.pid);\n setTimeout(() => {\n if (gameProcess && mainWindow && !mainWindow.isDestroyed()) mainWindow.hide();\n }, 750);\n"""--new = """ emitLaunchPhase(\n 'spawned',\n 'Source has started. OpenVibe will keep this launcher visible until the game window is detected and stable.'\n );\n mainWindow?.webContents.send('game-started', gameProcess.pid);\n\n waitForGameWindowReady().then((state) => {\n if (!gameProcess) return;\n\n if (state.ready) {\n emitLaunchPhase(\n 'ready',\n HIDE_LAUNCHER_ON_GAME_READY\n ? 'Source window looks ready. Focusing game and hiding launcher.'\n : 'Source window looks ready. Launcher will stay open so you are not stuck behind a frozen loading screen.',\n state\n );\n focusGameWindow();\n\n if (HIDE_LAUNCHER_ON_GAME_READY) {\n if (mainWindow && !mainWindow.isDestroyed()) mainWindow.hide();\n } else {\n if (mainWindow && !mainWindow.isDestroyed()) {\n mainWindow.show();\n mainWindow.focus();\n }\n }\n } else {\n emitLaunchPhase(\n 'slow',\n 'Source is taking longer than expected. The launcher is staying visible; use Focus Game when the window appears.',\n state\n );\n if (mainWindow && !mainWindow.isDestroyed()) {\n mainWindow.show();\n mainWindow.focus();\n }\n }\n });\n"""--if old in s:-- s = s.replace(old, new)--elif "setTimeout(() =>" in s and "mainWindow.hide" in s:-- s = re.sub(r"\s*mainWindow\?\.webContents\.send\('game-started', gameProcess\.pid\);\s*setTimeout\(\(\) => \{\s*if \(gameProcess && mainWindow && !mainWindow\.isDestroyed\(\)\) mainWindow\.hide\(\);\s*\}, 750\);", "\n" + new, s, count=1, flags=re.S)----if "ipcMain.handle('game:focus'" not in s:-- insert_after = "ipcMain.handle('game:status', () => ({\n running: gameProcess !== null,\n pid: gameProcess?.pid ?? null,\n}));\n"-- addition = """----ipcMain.handle('game:focus', async () => {-- return focusGameWindow();--});----ipcMain.handle('launcher:show', async () => {-- if (mainWindow && !mainWindow.isDestroyed()) {-- mainWindow.show();-- mainWindow.focus();-- }-- return true;--});--"""-- if insert_after in s:-- s = s.replace(insert_after, insert_after + addition)-- else:-- s += addition----p.write_text(s)----p = Path('launcher/preload.js')--s = p.read_text()--if 'focusGame:' not in s:-- s = s.replace(" gameStatus: () => ipcRenderer.invoke('game:status'),", " gameStatus: () => ipcRenderer.invoke('game:status'),\n focusGame: () => ipcRenderer.invoke('game:focus'),")--if 'showLauncher:' not in s:-- s = s.replace(" focusGame: () => ipcRenderer.invoke('game:focus'),", " focusGame: () => ipcRenderer.invoke('game:focus'),\n showLauncher: () => ipcRenderer.invoke('launcher:show'),")--if 'onLaunchPhase:' not in s:-- s = s.replace(" onGameStart: (cb) => ipcRenderer.on('game-started', (_e, pid) => cb(pid)),", " onGameStart: (cb) => ipcRenderer.on('game-started', (_e, pid) => cb(pid)),\n onLaunchPhase: (cb) => ipcRenderer.on('game-launch-phase', (_e, info) => cb(info)),")--p.write_text(s)----p = Path('launcher/index.html')--s = p.read_text()--if 'launch-help' not in s:-- s = s.replace(-- '<div class="launch-label" id="launch-label">Launching…</div>\n <button class="launch-cancel" id="launch-cancel">Cancel</button>',-- '<div class="launch-label" id="launch-label">Launching…</div>\n <div class="launch-help" id="launch-help">OpenVibe will keep this launcher visible until Source is actually ready.</div>\n <div class="launch-actions">\n <button class="launch-cancel" id="launch-focus-game">Focus Game Window</button>\n <button class="launch-cancel" id="launch-cancel">Dismiss</button>\n </div>'-- )--p.write_text(s)----p = Path('launcher/styles.css')--s = p.read_text()--if '.launch-help' not in s:-- s += r'''----/* OpenVibe launch guard */--.launch-help {-- margin-top: 10px;-- max-width: 620px;-- color: rgba(220, 235, 255, 0.78);-- font-size: 14px;-- line-height: 1.35;-- text-align: center;--}--.launch-actions {-- display: flex;-- gap: 10px;-- justify-content: center;-- flex-wrap: wrap;-- margin-top: 18px;--}--'''--p.write_text(s)----p = Path('launcher/renderer.js')--s = p.read_text()--if 'focusGame: () =>' not in s:-- s = s.replace(" gameStatus: () => isElectron ? window.OV.gameStatus() : Promise.resolve({ running: true, pid: null }),", " gameStatus: () => isElectron ? window.OV.gameStatus() : Promise.resolve({ running: true, pid: null }),\n focusGame: () => isElectron ? window.OV.focusGame() : Promise.resolve(false),")----if "launch-focus-game" not in s:-- s = s.replace("document.getElementById('launch-cancel')?.addEventListener('click', () => {\n launchOverlay.classList.remove('show');\n});", "document.getElementById('launch-cancel')?.addEventListener('click', () => {\n launchOverlay.classList.remove('show');\n});\n\ndocument.getElementById('launch-focus-game')?.addEventListener('click', async () => {\n const ok = await Bridge.focusGame();\n toast(ok ? 'Focused Source window.' : 'Source window not found yet.', !ok);\n});")----if 'onLaunchPhase' not in s:-- s = s.replace(" window.OV.onGameStart?.(() => {\n updateGameStatus(true);\n });", " window.OV.onGameStart?.(() => {\n updateGameStatus(true);\n });\n window.OV.onLaunchPhase?.((info) => {\n const label = document.getElementById('launch-label');\n const help = document.getElementById('launch-help');\n if (label && info?.message) label.textContent = info.message;\n if (help) {\n if (info?.phase === 'ready') {\n help.textContent = 'The game window is available. Because the Proton client may not load the custom client DLL, keep this launcher open as the reliable OpenVibe UI.';\n } else if (info?.phase === 'slow') {\n help.textContent = 'The game may still be loading or frozen. The launcher is staying visible so you can retry, focus the game, or inspect logs.';\n } else {\n help.textContent = 'OpenVibe is waiting for the Source window to appear and stabilize before switching focus.';\n }\n }\n launchOverlay?.classList.add('show');\n });")--p.write_text(s)--PY----python3 <<'PY'--from pathlib import Path--p = Path('tools/run-client-proton.sh')--s = p.read_text()--if '-condebug' not in s:-- s = s.replace('-console -dev -novid -sw -w 1280 -h 720 \\\n', '-console -condebug -dev -novid -sw -w 1280 -h 720 \\\n')--if '+con_enable 1' not in s:-- s = s.replace('-port 27115 -clientport 27105 \\\n', '-port 27115 -clientport 27105 +con_enable 1 +developer 1 \\\n')--if 'client.so' not in s and 'Windows hl2.exe will not load Linux client.so' not in s:-- s = s.replace('echo "Launching OpenVibe: Source..."', 'echo "Launching OpenVibe: Source..."\necho "NOTE: Proton Windows hl2.exe will not load Linux client.so; Electron launcher remains the reliable custom UI."')--p.write_text(s)--PY----cat > tools/check-openvibe-client-mode.sh <<'CHK'--#!/usr/bin/env bash--set -euo pipefail--ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"----echo "[openvibe] client mode diagnostic"--echo "Windows/Proton launcher: $ROOT/tools/run-client-proton.sh"--echo "Linux client module expected at: $ROOT/game/openvibe.games/bin/linux64/client.so"----if [[ -f "$ROOT/game/openvibe.games/bin/linux64/client.so" ]]; then-- echo "[ok] Linux client.so exists"--else-- echo "[warn] Linux client.so missing; run tools/build-sdk-linux.sh and tools/setup-openvibe-bin.sh"--fi----cat <<'MSG'----If the in-game console says unknown command "ov_join" or "ov_menu", the OpenVibe client DLL is not loaded.--That is expected when launching Windows hl2.exe through Proton while only Linux client.so exists.----Current reliable UI path:-- - Electron launcher / local Chromium UI-- - server-side GameDLL commands----Full in-game HTML menu requires one of:-- 1. Run a native Linux Source client that loads bin/linux64/client.so, or-- 2. Build a Windows client.dll/server.dll and place them in game/openvibe.games/bin/ for Proton hl2.exe.--MSG--CHK--chmod +x tools/check-openvibe-client-mode.sh----if [[ -x tools/setup-openvibe-bin.sh ]]; then-- tools/setup-openvibe-bin.sh || true--fi----echo--node -c launcher/main.js--node -c launcher/preload.js--node -c launcher/renderer.js----echo--echo "[openvibe] done. Run diagnostics:"--echo " tools/check-openvibe-client-mode.sh"--echo--echo "[openvibe] restart Electron/game. By default the launcher will stay visible."--echo "[openvibe] To auto-hide only after Source window is stable:"--echo " OPENVIBE_HIDE_LAUNCHER_ON_GAME_READY=1 npm run dev"ddeleted file mode 100755@@@ -1,676 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"--STAMP="$(date +%Y%m%d-%H%M%S)"----cd "$ROOT"----echo "[openvibe] fix LoadCoreFiles patch + finish JS command/timer phase"--echo "[openvibe] root=$ROOT"--echo "[openvibe] sdk=$SDK"----backup_file() {-- local f="$1"-- [[ -f "$f" ]] && cp "$f" "$f.bak.$STAMP"--}----mkdir -p game/openvibe.games/js/core--mkdir -p game/openvibe.games/js/gamemodes/base--mkdir -p game/openvibe.games/js/gamemodes/hub--mkdir -p game/openvibe.games/js/gamemodes/prophunt--mkdir -p game/openvibe.games/js/gamemodes/deathrun--mkdir -p game/openvibe.games/js/gamemodes/fortwars--mkdir -p game/openvibe.games/js/gamemodes/traitortown--mkdir -p tools----echo "[openvibe] writing canonical JS core files"----cat > game/openvibe.games/js/core/hook.js <<'JS'--(function () {-- const buckets = Object.create(null);---- function ensure(name) {-- name = String(name || "");-- if (!buckets[name]) buckets[name] = [];-- return buckets[name];-- }---- globalThis.hook = {-- add(name, id, fn) {-- if (!name || !id || typeof fn !== "function") {-- throw new Error("hook.add(name, id, fn) requires a hook name, id, and function");-- }---- const list = ensure(name);-- const sid = String(id);-- for (let i = list.length - 1; i >= 0; --i) {-- if (list[i].id === sid) list.splice(i, 1);-- }-- list.push({ id: sid, fn });-- },---- remove(name, id) {-- const list = buckets[String(name || "")];-- if (!list) return;-- const sid = String(id || "");-- for (let i = list.length - 1; i >= 0; --i) {-- if (list[i].id === sid) list.splice(i, 1);-- }-- },---- run(name, ...args) {-- const list = buckets[String(name || "")];-- if (!list) return undefined;---- const snapshot = list.slice();-- for (const item of snapshot) {-- const result = item.fn(...args);-- if (result !== undefined) return result;-- }---- return undefined;-- },---- list(name) {-- if (name) return (buckets[String(name)] || []).map((item) => item.id);-- const out = Object.create(null);-- for (const key of Object.keys(buckets)) out[key] = buckets[key].map((item) => item.id);-- return out;-- }-- };--})();--JS----cat > game/openvibe.games/js/core/gamemode.js <<'JS'--(function () {-- let current = null;---- globalThis.gamemode = {-- set(gm) {-- if (!gm || typeof gm !== "object") throw new Error("gamemode.set(gm) requires an object");-- current = gm;-- return gm;-- },---- get() {-- return current;-- },---- call(name, ...args) {-- const hookResult = hook.run(name, ...args);-- if (hookResult !== undefined) return hookResult;---- if (current && typeof current[name] === "function") {-- return current[name](...args);-- }---- return undefined;-- }-- };--})();--JS----cat > game/openvibe.games/js/bridge.js <<'JS'--(function () {-- if (globalThis.OV && typeof OV.log === "function") {-- OV.log("bridge.js loaded");-- }--})();--JS----cat > game/openvibe.games/js/core/command.js <<'JS'--(function () {-- const commands = Object.create(null);---- function normalize(name) {-- return String(name || "").trim().replace(/^!+/, "").toLowerCase();-- }---- function split(text) {-- const raw = String(text || "").trim();-- if (!raw) return { name: "", args: [], raw: "" };-- const parts = raw.split(/\s+/g);-- const name = normalize(parts.shift());-- return { name, args: parts, raw };-- }---- function reply(ply, msg) {-- if (ply && typeof ply.chat === "function") ply.chat(String(msg));-- else if (globalThis.OV && typeof OV.broadcast === "function") OV.broadcast(String(msg));-- else if (globalThis.OV && typeof OV.log === "function") OV.log(String(msg));-- }---- function listNames() {-- return Object.keys(commands).sort();-- }---- globalThis.command = {-- add(name, helpOrFn, maybeFn) {-- const key = normalize(name);-- if (!key) throw new Error("command.add requires a command name");---- let help = "";-- let fn = helpOrFn;-- if (typeof helpOrFn === "string") {-- help = helpOrFn;-- fn = maybeFn;-- }---- if (typeof fn !== "function") throw new Error("command.add requires a function");-- commands[key] = { help, fn };-- },---- remove(name) {-- delete commands[normalize(name)];-- },---- has(name) {-- return !!commands[normalize(name)];-- },---- list: listNames,---- run(ply, text) {-- const parsed = split(text);-- if (!parsed.name) return undefined;---- const entry = commands[parsed.name];-- if (!entry) {-- reply(ply, `Unknown command: ${parsed.name}. Try !help`);-- return false;-- }---- const result = entry.fn({ ply, name: parsed.name, args: parsed.args, raw: parsed.raw, reply });-- return result === undefined ? false : result;-- },---- dispatchConsole(text) {-- return this.run(null, text);-- }-- };---- hook.add("PlayerSay", "OpenVibeCommandRegistry", function (ply, text) {-- const msg = String(text || "").trim();-- if (!msg.startsWith("!")) return undefined;-- return command.run(ply, msg.slice(1));-- });---- hook.add("ConsoleCommand", "OpenVibeConsoleCommandRegistry", function (text) {-- return command.dispatchConsole(text);-- });---- command.add("help", "List OpenVibe JS commands", function ({ reply, ply }) {-- reply(ply, `Commands: ${listNames().map((name) => `!${name}`).join(", ")}`);-- return false;-- });---- command.add("smoke", "Run the embedded JS smoke test", function ({ reply, ply }) {-- reply(ply, "OpenVibe embedded JS smoke test passed.");-- return false;-- });---- command.add("timer_smoke", "Run the JS timer smoke test", function ({ reply, ply }) {-- reply(ply, "Timer smoke scheduled.");-- if (globalThis.timer && typeof timer.simple === "function") {-- timer.simple(0.05, function () {-- if (globalThis.OV && typeof OV.broadcast === "function") {-- OV.broadcast("OpenVibe JS timer smoke fired.");-- }-- });-- }-- return false;-- });--})();--JS----cat > game/openvibe.games/js/core/timer.js <<'JS'--(function () {-- const timers = Object.create(null);-- let nextAutoId = 1;---- function now() {-- if (globalThis.OV && typeof OV.time === "function") return Number(OV.time()) || 0;-- return Date.now() / 1000;-- }---- function create(id, delay, reps, fn) {-- if (!id) id = `timer_${nextAutoId++}`;-- if (typeof fn !== "function") throw new Error("timer.create requires a callback");---- delay = Math.max(0, Number(delay) || 0);-- reps = Number(reps);-- if (!Number.isFinite(reps)) reps = 1;---- timers[String(id)] = {-- id: String(id),-- delay,-- reps,-- count: 0,-- next: now() + delay,-- fn-- };---- return String(id);-- }---- function remove(id) {-- delete timers[String(id || "")];-- }---- function tick() {-- const t = now();-- for (const id of Object.keys(timers)) {-- const item = timers[id];-- if (!item || t < item.next) continue;---- item.count += 1;-- try {-- item.fn(item.count, id);-- } catch (err) {-- if (globalThis.OV && typeof OV.error === "function") OV.error(`timer ${id}: ${err && err.message ? err.message : err}`);-- delete timers[id];-- continue;-- }---- if (item.reps > 0 && item.count >= item.reps) {-- delete timers[id];-- } else {-- item.next = t + item.delay;-- }-- }-- }---- globalThis.timer = {-- create,-- remove,-- exists(id) {-- return !!timers[String(id || "")];-- },-- simple(delay, fn) {-- return create(`simple_${nextAutoId++}`, delay, 1, fn);-- },-- tick,-- list() {-- return Object.keys(timers).sort();-- }-- };---- hook.add("Think", "OpenVibeTimerThink", tick);--})();--JS----echo "[openvibe] writing scoped gamemode scripts"----cat > game/openvibe.games/js/gamemodes/base/server.js <<'JS'--(function () {-- const BaseServerGM = {-- mode: "base",-- name: "OpenVibe Base",---- Initialize() {-- OV.log("Base Initialize fired");-- },---- MapInitialize(mapName) {-- OV.log(`Base MapInitialize: ${mapName}`);-- },---- PlayerInitialSpawn(ply) {-- ply.chat("Welcome to OpenVibe: Source.");-- },---- PlayerSpawn(_ply) {},-- PlayerDeath(_victim, _attacker) {},-- PlayerDisconnected(_ply) {},-- Think() {}-- };---- gamemode.set(BaseServerGM);--})();--JS----cat > game/openvibe.games/js/gamemodes/hub/server.js <<'JS'--(function () {-- function registerHubCommands() {-- if (!globalThis.command) return;---- command.add("js", "Confirm JavaScript hooks are working", function ({ ply, reply }) {-- reply(ply, "JavaScript hooks are working.");-- return false;-- });---- command.add("hp", "Show current health", function ({ ply, reply }) {-- if (!ply) return false;-- reply(ply, `Health: ${ply.health()}`);-- return false;-- });---- command.add("players", "Show player count", function ({ ply, reply }) {-- reply(ply, `Players online: ${OV.players().length}`);-- return false;-- });---- command.add("where", "Show current mode and map", function ({ ply, reply }) {-- reply(ply, `Mode=${OV.getMode()} map=${OV.getMapName()}`);-- return false;-- });---- command.add("hub_status", "Broadcast hub status", function ({ ply, reply }) {-- reply(ply, `Hub OK. mode=${OV.getMode()} map=${OV.getMapName()} players=${OV.players().length}`);-- return false;-- });-- }---- const HubServerGM = {-- mode: "hub",-- name: "OpenVibe Hub",---- Initialize() {-- OV.log("Hub Initialize fired");-- registerHubCommands();-- },---- MapInitialize(mapName) {-- OV.log(`Hub MapInitialize: ${mapName}`);-- },---- PlayerInitialSpawn(ply) {-- ply.chat("Welcome to OpenVibe: Source JS runtime.");-- OV.broadcast(`${ply.name()} joined the hub.`);-- },---- PlayerSpawn(ply) {-- ply.chat("PlayerSpawn hook fired.");-- },---- PlayerSay(_ply, _text) {-- return undefined;-- },---- ConsoleCommand(text) {-- OV.log(`Hub ConsoleCommand fallback: ${text}`);-- return undefined;-- },---- Think() {}-- };---- gamemode.set(HubServerGM);--})();--JS----cat > game/openvibe.games/js/gamemodes/prophunt/server.js <<'JS'--(function () {-- function registerCommands() {-- if (!globalThis.command) return;-- command.add("ph_status", "Show Prop Hunt status", function ({ ply, reply }) {-- reply(ply, `Prop Hunt JS online. map=${OV.getMapName()} players=${OV.players().length}`);-- return false;-- });-- command.add("disguise", "Disguise as an allowlisted prop", function ({ args, ply, reply }) {-- const choice = args[0] || "crate";-- if (!ply) return false;-- ply.runCommand(`ov_prophunt_disguise ${choice}`);-- reply(ply, `Trying prop disguise: ${choice}`);-- return false;-- });-- }---- const PropHuntGM = {-- mode: "prophunt",-- name: "OpenVibe Prop Hunt",-- Initialize() { OV.log("Prop Hunt Initialize fired"); registerCommands(); },-- PlayerInitialSpawn(ply) { ply.chat("Prop Hunt JS loaded. Try !ph_status or !disguise crate"); },-- Think() {}-- };-- gamemode.set(PropHuntGM);--})();--JS----cat > game/openvibe.games/js/gamemodes/deathrun/server.js <<'JS'--(function () {-- function registerCommands() {-- if (!globalThis.command) return;-- command.add("dr_status", "Show Deathrun status", function ({ ply, reply }) {-- reply(ply, `Deathrun JS online. map=${OV.getMapName()} players=${OV.players().length}`);-- return false;-- });-- }---- const DeathrunGM = {-- mode: "deathrun",-- name: "OpenVibe Deathrun",-- Initialize() { OV.log("Deathrun Initialize fired"); registerCommands(); },-- PlayerInitialSpawn(ply) { ply.chat("Deathrun JS loaded. Try !dr_status"); },-- Think() {}-- };-- gamemode.set(DeathrunGM);--})();--JS----cat > game/openvibe.games/js/gamemodes/fortwars/server.js <<'JS'--(function () {-- function registerCommands() {-- if (!globalThis.command) return;-- command.add("fw_status", "Show Fort Wars status", function ({ ply, reply }) {-- reply(ply, `Fort Wars JS online. map=${OV.getMapName()} players=${OV.players().length}`);-- return false;-- });-- command.add("build", "Spawn an allowlisted Fort Wars prop", function ({ args, ply, reply }) {-- const choice = args[0] || "crate";-- if (!ply) return false;-- ply.runCommand(`ov_fortwars_spawn ${choice}`);-- reply(ply, `Trying Fort Wars prop: ${choice}`);-- return false;-- });-- }---- const FortWarsGM = {-- mode: "fortwars",-- name: "OpenVibe Fort Wars",-- Initialize() { OV.log("Fort Wars Initialize fired"); registerCommands(); },-- PlayerInitialSpawn(ply) { ply.chat("Fort Wars JS loaded. Try !fw_status or !build crate"); },-- Think() {}-- };-- gamemode.set(FortWarsGM);--})();--JS----cat > game/openvibe.games/js/gamemodes/traitortown/server.js <<'JS'--(function () {-- function registerCommands() {-- if (!globalThis.command) return;-- command.add("ttt_status", "Show Traitor Town status", function ({ ply, reply }) {-- reply(ply, `Traitor Town JS online. map=${OV.getMapName()} players=${OV.players().length}`);-- return false;-- });-- }---- const TraitorTownGM = {-- mode: "traitortown",-- name: "OpenVibe Traitor Town",-- Initialize() { OV.log("Traitor Town Initialize fired"); registerCommands(); },-- PlayerInitialSpawn(ply) { ply.chat("Traitor Town JS loaded. Try !ttt_status"); },-- Think() {}-- };-- gamemode.set(TraitorTownGM);--})();--JS----echo "[openvibe] robustly replacing COpenVibeJSRuntime::LoadCoreFiles"----backup_file sdk/openvibe/shared/ov_js_runtime.cpp----python3 <<'PY'--from pathlib import Path----p = Path("sdk/openvibe/shared/ov_js_runtime.cpp")--s = p.read_text()----sig = "bool COpenVibeJSRuntime::LoadCoreFiles()"--idx = s.find(sig)--if idx < 0:-- raise SystemExit("Could not find COpenVibeJSRuntime::LoadCoreFiles signature")----brace = s.find("{", idx)--if brace < 0:-- raise SystemExit("Could not find COpenVibeJSRuntime::LoadCoreFiles opening brace")----depth = 0--end = None--for i in range(brace, len(s)):-- ch = s[i]-- if ch == "{":-- depth += 1-- elif ch == "}":-- depth -= 1-- if depth == 0:-- end = i + 1-- break----if end is None:-- raise SystemExit("Could not find COpenVibeJSRuntime::LoadCoreFiles closing brace")----replacement = '''bool COpenVibeJSRuntime::LoadCoreFiles()--{-- if (!LoadFile("js/core/hook.js"))-- return false;---- if (!LoadFile("js/core/gamemode.js"))-- return false;---- if (!LoadFile("js/bridge.js"))-- return false;---- if (!LoadFile("js/core/command.js"))-- return false;---- if (!LoadFile("js/core/timer.js"))-- return false;---- return true;--}'''----s = s[:idx] + replacement + s[end:]--p.write_text(s)--print("[openvibe] LoadCoreFiles replaced")--PY----echo "[openvibe] writing Node JS smoke test"----cat > tools/smoke-js-core-node.mjs <<'JS'--import fs from "node:fs";--import vm from "node:vm";----let fakeTime = 0;--const messages = [];----function player() {-- return {-- userId: 1,-- entIndex: 1,-- steamId: () => "STEAM_0:1:1",-- name: () => "SmokePlayer",-- health: () => 87,-- setHealth: () => undefined,-- chat: (msg) => messages.push(`[chat] ${msg}`),-- team: () => 0,-- setTeam: () => undefined,-- runCommand: (cmd) => messages.push(`[runCommand] ${cmd}`)-- };--}----const context = vm.createContext({-- console,-- globalThis: {},-- OV: {-- log: (msg) => messages.push(`[log] ${msg}`),-- warn: (msg) => messages.push(`[warn] ${msg}`),-- error: (msg) => messages.push(`[error] ${msg}`),-- getMode: () => "hub",-- getMapName: () => "ov_hub",-- time: () => fakeTime,-- broadcast: (msg) => messages.push(`[broadcast] ${msg}`),-- players: () => [player()],-- playerByUserId: () => player(),-- serverCommand: (cmd) => messages.push(`[serverCommand] ${cmd}`),-- reward: () => undefined,-- endMatch: () => undefined-- }--});--context.globalThis = context;----const files = [-- "game/openvibe.games/js/core/hook.js",-- "game/openvibe.games/js/core/gamemode.js",-- "game/openvibe.games/js/bridge.js",-- "game/openvibe.games/js/core/command.js",-- "game/openvibe.games/js/core/timer.js",-- "game/openvibe.games/js/gamemodes/base/server.js",-- "game/openvibe.games/js/gamemodes/hub/server.js"--];----for (const file of files) {-- const code = fs.readFileSync(file, "utf8");-- vm.runInContext(code, context, { filename: file });--}----context.gamemode.call("Initialize");--context.gamemode.call("MapInitialize", "ov_hub");--context.gamemode.call("PlayerInitialSpawn", player());----const blocked = context.gamemode.call("PlayerSay", player(), "!js");--if (blocked !== false) throw new Error("!js did not block default chat");----context.gamemode.call("ConsoleCommand", "smoke");--context.gamemode.call("ConsoleCommand", "hub_status");--context.gamemode.call("ConsoleCommand", "timer_smoke");--fakeTime += 1;--context.gamemode.call("Think");----if (!messages.some((line) => line.includes("JavaScript hooks are working"))) throw new Error("missing !js response");--if (!messages.some((line) => line.includes("OpenVibe embedded JS smoke test passed"))) throw new Error("missing smoke response");--if (!messages.some((line) => line.includes("OpenVibe JS timer smoke fired"))) throw new Error("missing timer smoke response");----console.log(messages.join("\n"));--console.log("[openvibe-smoke] JS command/timer smoke passed");--JS----echo "[openvibe] run JS smoke test"--node tools/smoke-js-core-node.mjs----echo "[openvibe] apply SDK patch"--tools/apply-openvibe-sdk.sh----echo "[openvibe] build SDK"--if [[ "${RUN_BUILD:-1}" = "1" ]]; then-- tools/build-sdk-linux.sh 2>&1 | tee "$HOME/ov-build.log"--fi----echo "[openvibe] setup OpenVibe bin"--tools/setup-openvibe-bin.sh----echo----echo "[openvibe] JS command/timer phase complete."--echo----echo "Runtime test:"--echo " OPENVIBE_SRCDS_MAP_DELAY=3 tools/dev-up.sh"--echo--echo "Server console tests:"--echo " ov_js_status"--echo " ov_js_cmd help"--echo " ov_js_cmd smoke"--echo " ov_js_cmd timer_smoke"--echo " ov_js_cmd hub_status"--echo--echo "Client tests:"--echo " connect 127.0.0.1:27015"--echo " say !help"--echo " say !js"--echo " say !hp"--echo " say !players"--echo " say !where"ddeleted file mode 100755@@@ -1,396 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--cd "$ROOT"----echo "[openvibe] next phase: Proton-aware loading UX + fallback menu aliases"--echo "[openvibe] root=$ROOT"----mkdir -p game/openvibe.games/cfg game/openvibe.games/resource game/openvibe.games/materialsrc/console game/openvibe.games/materials/console launcher----# -------------------------------------------------------------------------------# 1) Proton/client fallback aliases.--# Windows hl2.exe cannot load the Linux client.so, so these aliases prevent--# confusing Unknown command spam and keep raw connect flows usable.--# -------------------------------------------------------------------------------cat > game/openvibe.games/cfg/openvibe_proton_client.cfg <<'CFG'--// OpenVibe Proton client fallback aliases.--// This file is executed by tools/run-client-proton.sh.--// Proton Windows hl2.exe does not load Linux bin/linux64/client.so, so the--// native OpenVibe client DLL commands are not available in this mode.----echo "[OpenVibe] Proton fallback cfg loaded. Electron is the custom menu in Proton mode."----alias ov_help "echo OpenVibe Proton fallback: use ov_join_hub / ov_join_prophunt / ov_join_deathrun / ov_join_fortwars / ov_join_traitortown. Electron remains the full HTML/CSS/JS menu."--alias ov_menu "echo OpenVibe HTML menu is hosted by Electron in Proton mode. Alt-tab to the launcher or run npm run dev."--alias ov_ui "ov_menu"--alias ov_main_menu "ov_menu"--alias ov_menu_main "ov_menu"--alias ov_menu_servers "echo Server browser is in Electron. Quick connects: ov_join_hub, ov_join_prophunt, ov_join_deathrun, ov_join_fortwars, ov_join_traitortown."--alias ov_menu_leaderboard "echo Leaderboard UI is in Electron in Proton mode."--alias ov_menu_inventory "echo Inventory UI is in Electron in Proton mode."--alias ov_menu_shop "echo Shop UI is in Electron in Proton mode."--alias ov_menu_settings "echo Settings UI is in Electron in Proton mode."--alias ov_menu_reload "echo Reload the Electron window with Ctrl+R or restart npm run dev."--alias ov_menu_close "echo Close/minimize the Electron launcher window."----alias ov_join "echo Usage in Proton fallback: ov_join_hub / ov_join_prophunt / ov_join_deathrun / ov_join_fortwars / ov_join_traitortown. The argument-taking ov_join command needs OpenVibe client.dll/client.so."--alias ov_join_hub "connect 127.0.0.1:27015"--alias ov_join_prophunt "connect 127.0.0.1:27016"--alias ov_join_deathrun "connect 127.0.0.1:27017"--alias ov_join_fortwars "connect 127.0.0.1:27018"--alias ov_join_traitortown "connect 127.0.0.1:27019"----alias ov_js_status "cmd ov_js_status"--alias ov_js_reload "cmd ov_js_reload"--alias ov_js_cmd "cmd ov_js_cmd"--alias ov_js_fire "cmd ov_js_fire"----alias ov_prophunt_disguise "cmd ov_prophunt_disguise"--alias ov_prophunt_reset_disguise "cmd ov_prophunt_reset_disguise"--alias ov_fortwars_spawn "cmd ov_fortwars_spawn"--CFG----cat > game/openvibe.games/cfg/autoexec.cfg <<'CFG'--// OpenVibe autoexec. Safe in both native and Proton modes.--exec openvibe_proton_client.cfg--CFG----# Patch Proton launcher to exec aliases early.--python3 <<'PY'--from pathlib import Path--p = Path('tools/run-client-proton.sh')--s = p.read_text()--if '+exec openvibe_proton_client.cfg' not in s:-- s = s.replace(-- '-console -dev -novid -sw -w 1280 -h 720 \\\n',-- '-console -dev -novid -sw -w 1280 -h 720 +exec openvibe_proton_client.cfg \\\n'-- )--if 'OPENVIBE_PROTON_FALLBACK' not in s:-- s = s.replace('export PROTON_LOG=0\n', 'export PROTON_LOG=0\nexport OPENVIBE_PROTON_FALLBACK=1\n')--p.write_text(s)--PY--chmod +x tools/run-client-proton.sh----# -------------------------------------------------------------------------------# 2) Best-effort loading dialog override resources.--# This won't replace the HL2 background material until VTF files are supplied,--# but it customizes text/layout and adds docs + source art placeholders.--# -------------------------------------------------------------------------------cat > game/openvibe.games/resource/LoadingDialog.res <<'RES'--"Resource/LoadingDialog.res"--{-- "LoadingDialog"-- {-- "ControlName" "Frame"-- "fieldName" "LoadingDialog"-- "xpos" "0"-- "ypos" "0"-- "wide" "f0"-- "tall" "f0"-- "visible" "1"-- "enabled" "1"-- "title" "OpenVibe: Source"-- "proportionaltoparent" "1"-- "PaintBackgroundType" "2"-- "bgcolor_override" "7 8 14 245"-- }-- "LoadingLabel"-- {-- "ControlName" "Label"-- "fieldName" "LoadingLabel"-- "xpos" "cs-260"-- "ypos" "rs1-92"-- "wide" "520"-- "tall" "28"-- "visible" "1"-- "enabled" "1"-- "labelText" "OPENVIBE: SOURCE"-- "textAlignment" "center"-- "font" "DefaultLarge"-- "fgcolor_override" "0 216 255 255"-- }-- "ProgressLabel"-- {-- "ControlName" "Label"-- "fieldName" "ProgressLabel"-- "xpos" "cs-260"-- "ypos" "rs1-58"-- "wide" "520"-- "tall" "24"-- "visible" "1"-- "enabled" "1"-- "labelText" "Loading OpenVibe session..."-- "textAlignment" "center"-- "font" "Default"-- "fgcolor_override" "230 236 255 255"-- }--}--RES----cat > game/openvibe.games/materialsrc/console/openvibe-loading.svg <<'SVG'--<svg xmlns="http://www.w3.org/2000/svg" width="1920" height="1080" viewBox="0 0 1920 1080">-- <defs>-- <radialGradient id="g" cx="50%" cy="45%" r="75%">-- <stop offset="0" stop-color="#16334a"/>-- <stop offset="0.45" stop-color="#0b1020"/>-- <stop offset="1" stop-color="#05060b"/>-- </radialGradient>-- <linearGradient id="line" x1="0" x2="1">-- <stop offset="0" stop-color="#00d8ff" stop-opacity="0"/>-- <stop offset="0.5" stop-color="#00d8ff" stop-opacity="1"/>-- <stop offset="1" stop-color="#a855f7" stop-opacity="0"/>-- </linearGradient>-- </defs>-- <rect width="1920" height="1080" fill="url(#g)"/>-- <g opacity="0.18" stroke="#00d8ff" fill="none">-- <path d="M0 770 C300 630 520 890 850 730 C1190 560 1390 810 1920 610"/>-- <path d="M0 830 C330 700 560 960 910 780 C1240 610 1480 860 1920 680"/>-- </g>-- <circle cx="960" cy="450" r="155" fill="none" stroke="#00d8ff" stroke-width="8" opacity="0.8"/>-- <circle cx="960" cy="450" r="112" fill="none" stroke="#a855f7" stroke-width="4" opacity="0.7"/>-- <text x="960" y="465" text-anchor="middle" font-family="Orbitron, Rajdhani, sans-serif" font-size="84" fill="#eaf8ff" font-weight="900">OPENVIBE</text>-- <text x="960" y="530" text-anchor="middle" font-family="Rajdhani, sans-serif" font-size="38" fill="#00d8ff" font-weight="700">SOURCE</text>-- <rect x="660" y="620" width="600" height="6" rx="3" fill="url(#line)"/>-- <text x="960" y="680" text-anchor="middle" font-family="Inter, sans-serif" font-size="28" fill="#b9c7e8">Loading connected game session...</text>--</svg>--SVG----cat > game/openvibe.games/materials/console/README.md <<'MD'--# OpenVibe loading background----Source's full-screen loading background is a VTF material, normally:----- `materials/console/background01.vtf`--- `materials/console/background01_widescreen.vtf`----This repo includes source art at:----- `materialsrc/console/openvibe-loading.svg`----To fully replace the HL2 loading image, convert it to the two VTF filenames above using VTFEdit/VTFCmd or Valve's `vtex`, then place the VTFs in this folder.----Until those VTF files exist, `resource/LoadingDialog.res` can restyle text/layout, but the old HL2 image may still appear.--MD----# -------------------------------------------------------------------------------# 3) Electron launch UX: keep launcher visible, expose focus button, and only--# hide when Source window is stable if env var opts in.--# -------------------------------------------------------------------------------python3 <<'PY'--from pathlib import Path--p = Path('launcher/main.js')--s = p.read_text()----if 'function sourceWindowVisible' not in s:-- insert_after = "let appIsQuitting = false;\n"-- block = r'''--let lastLaunchState = { phase: 'idle', message: 'Ready', pid: null };----function broadcastLaunchState(state) {-- lastLaunchState = { ...lastLaunchState, ...state, at: Date.now() };-- if (mainWindow && !mainWindow.isDestroyed()) {-- mainWindow.webContents.send('launch-state', lastLaunchState);-- }--}----function sourceWindowVisible() {-- try {-- const out = execSync('wmctrl -lx 2>/dev/null || true', { encoding: 'utf8' });-- return /OpenVibe: Source|Source SDK Base 2013|hl2\.exe|hl2/i.test(out);-- } catch {-- return false;-- }--}----function focusSourceWindow() {-- const commands = [-- "wmctrl -a 'OpenVibe: Source'",-- "wmctrl -a 'Source SDK Base 2013'",-- "wmctrl -a 'hl2.exe'",-- ];-- for (const cmd of commands) {-- try {-- execSync(cmd, { stdio: 'ignore' });-- return true;-- } catch {}-- }-- return false;--}----function waitForStableSourceWindow(timeoutMs = 60000) {-- const started = Date.now();-- let stableTicks = 0;-- return new Promise((resolve) => {-- const tick = () => {-- if (sourceWindowVisible()) stableTicks += 1;-- else stableTicks = 0;---- broadcastLaunchState({-- phase: stableTicks > 0 ? 'window-detected' : 'starting',-- message: stableTicks > 0-- ? `Source window detected (${stableTicks}/5 stable checks)...`-- : 'Starting Source through Proton...',-- });---- if (stableTicks >= 5) return resolve(true);-- if (Date.now() - started >= timeoutMs) return resolve(false);-- setTimeout(tick, 1000);-- };-- tick();-- });--}--'''-- s = s.replace(insert_after, insert_after + block + "\n")----old = """ mainWindow?.webContents.send('game-started', gameProcess.pid);\n setTimeout(() => {\n if (gameProcess && mainWindow && !mainWindow.isDestroyed()) mainWindow.hide();\n }, 750);\n"""--new = """ mainWindow?.webContents.send('game-started', gameProcess.pid);\n broadcastLaunchState({\n phase: 'spawned',\n pid: gameProcess.pid,\n message: 'Source spawned. Keeping launcher visible until the game window is stable.',\n });\n\n waitForStableSourceWindow().then((ready) => {\n broadcastLaunchState({\n phase: ready ? 'ready' : 'timeout',\n pid: gameProcess ? gameProcess.pid : null,\n message: ready\n ? 'Source window appears stable. You can focus the game now.'\n : 'Source is still not confirmed stable. Launcher stayed visible to avoid confusion.',\n });\n\n const shouldAutoHide = process.env.OPENVIBE_HIDE_LAUNCHER_ON_GAME_READY === '1';\n if (ready && shouldAutoHide && gameProcess && mainWindow && !mainWindow.isDestroyed()) {\n mainWindow.hide();\n focusSourceWindow();\n }\n });\n"""--if old in s:-- s = s.replace(old, new)----if "ipcMain.handle('game:focus'" not in s:-- s = s.replace("ipcMain.handle('game:status', () => ({\n running: gameProcess !== null,\n pid: gameProcess?.pid ?? null,\n}));\n",-- "ipcMain.handle('game:status', () => ({\n running: gameProcess !== null,\n pid: gameProcess?.pid ?? null,\n launchState: lastLaunchState,\n}));\n\nipcMain.handle('game:focus', async () => focusSourceWindow());\n")----p.write_text(s)--PY----python3 <<'PY'--from pathlib import Path--p = Path('launcher/preload.js')--s = p.read_text()--if "focusGame" not in s:-- s = s.replace("gameStatus: () => ipcRenderer.invoke('game:status'),",-- "gameStatus: () => ipcRenderer.invoke('game:status'),\n focusGame: () => ipcRenderer.invoke('game:focus'),")--if "onLaunchState" not in s:-- s = s.replace("onGameStart: (cb) => ipcRenderer.on('game-started', (_e, pid) => cb(pid)),",-- "onGameStart: (cb) => ipcRenderer.on('game-started', (_e, pid) => cb(pid)),\n onLaunchState: (cb) => ipcRenderer.on('launch-state', (_e, state) => cb(state)),")--p.write_text(s)--PY----python3 <<'PY'--from pathlib import Path--p = Path('launcher/renderer.js')--s = p.read_text()--if 'focusGame:' not in s:-- s = s.replace("gameStatus: () => isElectron ? window.OV.gameStatus() : Promise.resolve({ running: true, pid: null }),",-- "gameStatus: () => isElectron ? window.OV.gameStatus() : Promise.resolve({ running: true, pid: null }),\n focusGame: () => isElectron ? window.OV.focusGame() : Promise.resolve(false),")--if 'launch-focus' not in s:-- s = s.replace("document.getElementById('launch-cancel')?.addEventListener('click', () => {\n launchOverlay.classList.remove('show');\n});",-- "document.getElementById('launch-cancel')?.addEventListener('click', () => {\n launchOverlay.classList.remove('show');\n});\n\ndocument.getElementById('launch-focus')?.addEventListener('click', async () => {\n const ok = await Bridge.focusGame();\n toast(ok ? 'Focused Source window.' : 'Could not find Source window yet.', !ok);\n});")--if 'onLaunchState' not in s:-- s += r'''----// Launcher-aware Source startup status. This keeps Electron useful while Proton--// shows the default Source loading window and before a native client DLL exists.--if (isElectron && window.OV.onLaunchState) {-- window.OV.onLaunchState((state) => {-- const label = document.getElementById('launch-label');-- const sub = document.getElementById('launch-sub');-- if (label && state?.message) label.textContent = state.message;-- if (sub) {-- sub.textContent = state?.phase === 'ready'-- ? 'Game window is ready. Use Focus Game Window, or keep Electron open as the custom menu.'-- : 'Electron remains open so you are not left staring at a frozen/default Source loading screen.';-- }-- if (state?.phase && state.phase !== 'idle') launchOverlay?.classList.add('show');-- });--}--'''--p.write_text(s)--PY----python3 <<'PY'--from pathlib import Path--p = Path('launcher/index.html')--s = p.read_text()--if 'id="launch-focus"' not in s:-- s = s.replace(' <button class="launch-cancel" id="launch-cancel">Cancel</button>\n',-- ' <button class="launch-cancel" id="launch-cancel">Cancel</button>\n <button class="launch-focus" id="launch-focus">Focus Game Window</button>\n <div class="launch-sub" id="launch-sub">Electron stays visible until Source is stable.</div>\n')--p.write_text(s)--PY----cat >> launcher/styles.css <<'CSS'----/* OpenVibe launcher-aware Source loading UX */--.launch-focus {-- margin-top: 10px;-- border: 1px solid rgba(0, 216, 255, 0.55);-- background: rgba(0, 216, 255, 0.14);-- color: #eaf8ff;-- border-radius: 10px;-- padding: 9px 16px;-- font-weight: 700;-- cursor: pointer;--}--.launch-focus:hover {-- background: rgba(0, 216, 255, 0.22);--}--.launch-sub {-- margin-top: 10px;-- max-width: 560px;-- color: rgba(230, 236, 255, 0.76);-- font-size: 13px;-- line-height: 1.35;-- text-align: center;--}--CSS----# -------------------------------------------------------------------------------# 4) Diagnostic.--# -------------------------------------------------------------------------------cat > tools/check-openvibe-client-mode.sh <<'SH'--#!/usr/bin/env bash--set -euo pipefail--ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--cd "$ROOT"----echo "[openvibe] client mode diagnostic"--echo "Windows/Proton launcher: $ROOT/tools/run-client-proton.sh"--echo "Linux client module: $ROOT/game/openvibe.games/bin/linux64/client.so"--echo "Fallback aliases: $ROOT/game/openvibe.games/cfg/openvibe_proton_client.cfg"--echo--[[ -f game/openvibe.games/bin/linux64/client.so ]] && echo "[ok] Linux client.so exists" || echo "[missing] Linux client.so missing"--[[ -f game/openvibe.games/cfg/openvibe_proton_client.cfg ]] && echo "[ok] Proton fallback aliases exist" || echo "[missing] Proton fallback aliases missing"----echo--cat <<'TXT'--Important:-- Proton Windows hl2.exe loads Windows client.dll, not Linux bin/linux64/client.so.-- If the console says unknown command ov_join/ov_menu, the OpenVibe client DLL is not loaded.----Current reliable custom UI:-- Electron launcher + local Chromium UI.----Current in-game fallback:-- autoexec.cfg loads openvibe_proton_client.cfg aliases.-- Try: ov_help, ov_join_hub, ov_join_prophunt, ov_join_deathrun.----Full in-game HTML/CSS/JS menu requires:-- 1) native Linux Source client loading bin/linux64/client.so, or-- 2) a Windows client.dll build for Proton hl2.exe.----Full loading image replacement:-- Convert materialsrc/console/openvibe-loading.svg to:-- materials/console/background01.vtf-- materials/console/background01_widescreen.vtf--TXT--SH--chmod +x tools/check-openvibe-client-mode.sh----# Keep setup links current, if build exists.--if [[ -x tools/setup-openvibe-bin.sh ]]; then-- tools/setup-openvibe-bin.sh || true--fi----echo--echo "[openvibe] done. Re-test:"--echo " tools/check-openvibe-client-mode.sh"--echo " cd launcher && npm run dev"--echo--echo "In Proton client console, try:"--echo " exec openvibe_proton_client.cfg"--echo " ov_help"--echo " ov_join_hub"--echo " ov_menu_servers"ddeleted file mode 100755@@@ -1,146 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"----cd "$ROOT"----echo "[openvibe] replacing tools/build-quickjs-lib.sh"----cat > tools/build-quickjs-lib.sh <<'BUILDQJS'--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"----SRC_QJS="$ROOT/sdk/openvibe/third_party/quickjs"--SDK_QJS="$SDK/src/game/shared/openvibe/third_party/quickjs"--OUT="$SDK_QJS/build"----if [[ ! -f "$SRC_QJS/quickjs.c" ]]; then-- echo "Missing $SRC_QJS/quickjs.c" >&2-- echo "Run: tools/vendor-quickjs.sh" >&2-- exit 1--fi----mkdir -p "$SDK_QJS"----# Important: exclude build/ so rsync does not delete our object output dir.--rsync -a --delete --exclude='build/' "$SRC_QJS/" "$SDK_QJS/"----mkdir -p "$OUT"----sources=(-- quickjs.c-- libregexp.c-- libunicode.c-- cutils.c--)----[[ -f "$SDK_QJS/dtoa.c" ]] && sources+=(dtoa.c)--[[ -f "$SDK_QJS/libbf.c" ]] && sources+=(libbf.c)----rm -f "$OUT"/*.o "$OUT"/libquickjs_openvibe.a----for src in "${sources[@]}"; do-- obj="$OUT/${src%.c}.o"-- echo "[openvibe-qjs] cc $src -> $obj"---- cc \-- -std=gnu11 \-- -O2 \-- -fPIC \-- -D_GNU_SOURCE \-- -DCONFIG_VERSION=\"openvibe\" \-- -I"$SDK_QJS" \-- -c "$SDK_QJS/$src" \-- -o "$obj"--done----ar rcs "$OUT/libquickjs_openvibe.a" "$OUT"/*.o----echo "[openvibe-qjs] built $OUT/libquickjs_openvibe.a"--ls -lh "$OUT/libquickjs_openvibe.a"--BUILDQJS----chmod +x tools/build-quickjs-lib.sh----echo "[openvibe] removing direct QuickJS .c files from server_hl2mp.vpc"----SERVER_VPC="$SDK/src/game/server/server_hl2mp.vpc"----if [[ -f "$SERVER_VPC" ]]; then-- cp "$SERVER_VPC" "$SERVER_VPC.bak.$(date +%Y%m%d-%H%M%S)"---- perl -0pi -e '-- s/^.*quickjs\\quickjs\.c.*\n//mg;-- s/^.*quickjs\\libregexp\.c.*\n//mg;-- s/^.*quickjs\\libunicode\.c.*\n//mg;-- s/^.*quickjs\\cutils\.c.*\n//mg;-- s/^.*quickjs\\dtoa\.c.*\n//mg;-- s/^.*quickjs\\libbf\.c.*\n//mg;-- ' "$SERVER_VPC"---- if ! grep -q 'libquickjs_openvibe.a' "$SERVER_VPC"; then-- perl -0pi -e 's/(\$File\s+"hl2mp\\openvibe_js_server\.cpp"\n)/$1\t\t\t\$Lib\t"..\\shared\\openvibe\\third_party\\quickjs\\build\\libquickjs_openvibe.a"\n/s' "$SERVER_VPC"-- fi--fi----echo "[openvibe] patching apply-openvibe-sdk.sh so future applies keep this fix"----python3 <<'PY'--from pathlib import Path--import re----p = Path("tools/apply-openvibe-sdk.sh")--s = p.read_text()----# Make sure apply script calls static library builder after copying QuickJS.--if '"$ROOT/tools/build-quickjs-lib.sh"' not in s:-- needle = 'copy_tree "$ROOT/sdk/openvibe/third_party/quickjs" \\\n "$SDK/src/game/shared/openvibe/third_party/quickjs"'-- if needle in s:-- s = s.replace(needle, needle + '\n\n"$ROOT/tools/build-quickjs-lib.sh"')-- else:-- s += '\n\n"$ROOT/tools/build-quickjs-lib.sh"\n'----# Add a strong cleanup block near the end.--marker = "OPENVIBE_REMOVE_DIRECT_QUICKJS_C"--if marker not in s:-- s += r'''----# OPENVIBE_REMOVE_DIRECT_QUICKJS_C--if [[ -f "$SERVER_VPC" ]]; then-- perl -0pi -e '-- s/^.*quickjs\\quickjs\.c.*\n//mg;-- s/^.*quickjs\\libregexp\.c.*\n//mg;-- s/^.*quickjs\\libunicode\.c.*\n//mg;-- s/^.*quickjs\\cutils\.c.*\n//mg;-- s/^.*quickjs\\dtoa\.c.*\n//mg;-- s/^.*quickjs\\libbf\.c.*\n//mg;-- ' "$SERVER_VPC"---- if ! grep -q 'libquickjs_openvibe.a' "$SERVER_VPC"; then-- perl -0pi -e 's/(\$File\s+"hl2mp\\openvibe_js_server\.cpp"\n)/$1\t\t\t\$Lib\t"..\\shared\\openvibe\\third_party\\quickjs\\build\\libquickjs_openvibe.a"\n/s' "$SERVER_VPC"-- fi--fi--'''----p.write_text(s)--PY----echo "[openvibe] building QuickJS static lib"--tools/build-quickjs-lib.sh----echo--echo "[openvibe] checking VPC for bad direct QuickJS C entries"--if [[ -f "$SERVER_VPC" ]]; then-- grep -nE 'quickjs\\(quickjs|libregexp|libunicode|cutils|dtoa|libbf)\.c' "$SERVER_VPC" || true-- grep -n 'libquickjs_openvibe.a' "$SERVER_VPC" || true--fi----echo--echo "[openvibe] fixed. Now run:"--echo " tools/apply-openvibe-sdk.sh"--echo " tools/build-sdk-linux.sh 2>&1 | tee ~/ov-build.log"ddeleted file mode 100755@@@ -1,174 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"--STAMP="$(date +%Y%m%d-%H%M%S)"----cd "$ROOT"----backup_file() {-- local file="$1"-- [[ -f "$file" ]] && cp "$file" "$file.bak.$STAMP"--}----echo "[openvibe] writing QuickJS static library builder"----cat > tools/build-quickjs-lib.sh <<'BUILDQJS'--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"----SRC_QJS="$ROOT/sdk/openvibe/third_party/quickjs"--SDK_QJS="$SDK/src/game/shared/openvibe/third_party/quickjs"--OUT="$SDK_QJS/build"----mkdir -p "$OUT"----if [[ ! -f "$SRC_QJS/quickjs.c" ]]; then-- echo "Missing $SRC_QJS/quickjs.c" >&2-- echo "Run: tools/vendor-quickjs.sh" >&2-- exit 1--fi----# Make sure SDK copy exists. apply-openvibe-sdk.sh also does this,--# but this makes the script usable directly.--mkdir -p "$SDK_QJS"--rsync -a --delete "$SRC_QJS/" "$SDK_QJS/"----sources=(-- quickjs.c-- libregexp.c-- libunicode.c-- cutils.c--)----[[ -f "$SDK_QJS/dtoa.c" ]] && sources+=(dtoa.c)--[[ -f "$SDK_QJS/libbf.c" ]] && sources+=(libbf.c)----rm -f "$OUT"/*.o "$OUT"/libquickjs_openvibe.a----for src in "${sources[@]}"; do-- obj="$OUT/${src%.c}.o"-- echo "[openvibe-qjs] cc $src"-- cc \-- -std=gnu11 \-- -O2 \-- -fPIC \-- -D_GNU_SOURCE \-- -DCONFIG_VERSION=\"openvibe\" \-- -I"$SDK_QJS" \-- -c "$SDK_QJS/$src" \-- -o "$obj"--done----ar rcs "$OUT/libquickjs_openvibe.a" "$OUT"/*.o----echo "[openvibe-qjs] built $OUT/libquickjs_openvibe.a"--ls -lh "$OUT/libquickjs_openvibe.a"--BUILDQJS----chmod +x tools/build-quickjs-lib.sh----echo "[openvibe] patching apply-openvibe-sdk.sh to link libquickjs_openvibe.a instead of compiling QuickJS C files"----backup_file tools/apply-openvibe-sdk.sh----python3 <<'PY'--from pathlib import Path--import re----p = Path("tools/apply-openvibe-sdk.sh")--s = p.read_text()----# Remove QuickJS .c cleanup patterns from perl patch block if they exist.--for name in ["quickjs", "libregexp", "libunicode", "cutils", "dtoa", "libbf"]:-- s = re.sub(-- rf'\s*s/\^\.\*openvibe\\\\third_party\\\\quickjs\\\\{name}\\\.c\.\*\\n//mg;\n?',-- '\n',-- s,-- )-- s = re.sub(-- rf'\s*s/\^\.\*openvibe\\\\\\\\third_party\\\\\\\\quickjs\\\\\\\\{name}\\\\\.c\.\*\\n//mg;\n?',-- '\n',-- s,-- )----# Remove inserted QuickJS .c $File entries from replacement text.--for name in ["quickjs", "libregexp", "libunicode", "cutils", "dtoa", "libbf"]:-- s = s.replace(-- f'\\n\\t\\t\\t\\$File\\t"..\\\\shared\\\\openvibe\\\\third_party\\\\quickjs\\\\{name}.c"',-- ''-- )-- s = s.replace(-- f'\n\t\t\t\\$File\t"..\\\\shared\\\\openvibe\\\\third_party\\\\quickjs\\\\{name}.c"',-- ''-- )-- s = s.replace(-- f'\n\t\t\t$File\t"..\\\\shared\\\\openvibe\\\\third_party\\\\quickjs\\\\{name}.c"',-- ''-- )----# Remove any actual already-written lines from the script text.--s = re.sub(r'^.*quickjs\\\\(quickjs|libregexp|libunicode|cutils|dtoa|libbf)\.c.*\n', '', s, flags=re.M)--s = re.sub(r'^.*quickjs\\(quickjs|libregexp|libunicode|cutils|dtoa|libbf)\.c.*\n', '', s, flags=re.M)----# Ensure build-quickjs-lib is called after copy_tree.--if "tools/build-quickjs-lib.sh" not in s:-- needle = 'copy_tree "$ROOT/sdk/openvibe/third_party/quickjs" \\\n "$SDK/src/game/shared/openvibe/third_party/quickjs"'-- replacement = needle + '\n\n"$ROOT/tools/build-quickjs-lib.sh"'-- if needle in s:-- s = s.replace(needle, replacement)-- else:-- s += '\n\n"$ROOT/tools/build-quickjs-lib.sh"\n'----# Add static lib to server_hl2mp.vpc patch.--# Try to insert a $Lib line near the openvibe source files, only once.--lib_line = '\t\t\t$Lib\t"..\\\\shared\\\\openvibe\\\\third_party\\\\quickjs\\\\build\\\\libquickjs_openvibe.a"'--if 'libquickjs_openvibe.a' not in s:-- # Add after openvibe_js_server.cpp if present in replacement text.-- s = s.replace(-- '\t\t\t\\$File\t"hl2mp\\\\openvibe_js_server.cpp"',-- '\t\t\t\\$File\t"hl2mp\\\\openvibe_js_server.cpp"\\n' + lib_line.replace('$', '\\$')-- )-- # If that escaped replacement did not hit, add a direct post-patch insertion block.-- if 'libquickjs_openvibe.a' not in s:-- s += r'''----# Link prebuilt QuickJS C static library. QuickJS must be compiled as C, not C++.--if ! grep -q 'libquickjs_openvibe.a' "$SERVER_VPC"; then-- perl -0pi -e 's/(\$File\s+"hl2mp\\openvibe_js_server\.cpp"\n)/$1\t\t\t\$Lib\t"..\\shared\\openvibe\\third_party\\quickjs\\build\\libquickjs_openvibe.a"\n/s' "$SERVER_VPC"-- echo "[openvibe-sdk] linked QuickJS static library"--fi--'''----p.write_text(s)--PY----echo "[openvibe] also patching already-applied SDK VPC now, if it exists"----SERVER_VPC="$SDK/src/game/server/server_hl2mp.vpc"----if [[ -f "$SERVER_VPC" ]]; then-- backup_file "$SERVER_VPC"---- # Remove direct QuickJS C files from VPC.-- perl -0pi -e 's/^.*openvibe\\\\third_party\\\\quickjs\\\\(quickjs|libregexp|libunicode|cutils|dtoa|libbf)\.c.*\n//mg; s/^.*openvibe\\third_party\\quickjs\\(quickjs|libregexp|libunicode|cutils|dtoa|libbf)\.c.*\n//mg' "$SERVER_VPC"---- if ! grep -q 'libquickjs_openvibe.a' "$SERVER_VPC"; then-- perl -0pi -e 's/(\$File\s+"hl2mp\\openvibe_js_server\.cpp"\n)/$1\t\t\t\$Lib\t"..\\shared\\openvibe\\third_party\\quickjs\\build\\libquickjs_openvibe.a"\n/s' "$SERVER_VPC"-- fi--fi----echo "[openvibe] building QuickJS C static library"--tools/build-quickjs-lib.sh----echo--echo "[openvibe] fixed QuickJS C/C++ build split."--echo--echo "Now run:"--echo " tools/apply-openvibe-sdk.sh"--echo " tools/build-sdk-linux.sh 2>&1 | tee ~/ov-build.log"--echo " tail -160 ~/ov-build.log"ddeleted file mode 100755@@@ -1,327 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--cd "$ROOT"--BRANCH="$(git branch --show-current 2>/dev/null || echo codex/openvibe-next-steps)"----say(){ echo "[openvibe] $*"; }----say "fix QuickJS MSVC GNU builtins/attribute/pthread compat + rerun Windows DLL workflow"--say "root=$ROOT"--say "branch=$BRANCH"----mkdir -p tools docs----cat > tools/build-quickjs-lib-windows.ps1 <<'PS1'--$ErrorActionPreference = 'Stop'----$Root = if ($env:OPENVIBE_ROOT) { (Resolve-Path $env:OPENVIBE_ROOT).Path } else { (Resolve-Path (Join-Path $PSScriptRoot '..')).Path }--$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root 'engine/source-sdk-2013' }----$SrcQjs = Join-Path $Root 'sdk/openvibe/third_party/quickjs'--$SdkQjs = Join-Path $Sdk 'src/game/shared/openvibe/third_party/quickjs'--$Out = Join-Path $SdkQjs 'build'--$LogDir = Join-Path $Root 'artifacts/windows-build-debug'----function Say($m) { Write-Host "[openvibe-qjs-win] $m" }----if (!(Test-Path (Join-Path $SrcQjs 'quickjs.c'))) {-- throw "Missing $SrcQjs/quickjs.c. Run tools/vendor-quickjs.sh first."--}--if (!(Get-Command cl.exe -ErrorAction SilentlyContinue)) {-- throw "cl.exe not found. Run this from a Visual Studio Developer PowerShell or Developer Command Prompt."--}--if (!(Get-Command lib.exe -ErrorAction SilentlyContinue)) {-- throw "lib.exe not found. Run this from a Visual Studio Developer PowerShell or Developer Command Prompt."--}----New-Item -ItemType Directory -Force -Path $SdkQjs, $Out, $LogDir | Out-Null--Copy-Item -Path (Join-Path $SrcQjs '*') -Destination $SdkQjs -Recurse -Force----$CompatInclude = Join-Path $Out 'compat/include'--$CompatSys = Join-Path $CompatInclude 'sys'--New-Item -ItemType Directory -Force -Path $CompatInclude, $CompatSys | Out-Null----# Upstream QuickJS is mostly GCC/clang oriented. MSVC needs a forced include for:--# - GCC __attribute__((...)) syntax used in cutils.h--# - GCC __builtin_* helpers used in cutils.h--# - a few libc aliases--$MsvcCompat = Join-Path $CompatInclude 'openvibe_qjs_msvc_compat.h'--@'--#ifndef OPENVIBE_QJS_MSVC_COMPAT_H--#define OPENVIBE_QJS_MSVC_COMPAT_H----#if defined(_MSC_VER)--#ifndef WIN32_LEAN_AND_MEAN--#define WIN32_LEAN_AND_MEAN--#endif--#ifndef NOMINMAX--#define NOMINMAX--#endif--#include <stdint.h>--#include <stdlib.h>--#include <intrin.h>----#ifndef __attribute__--#define __attribute__(x)--#endif--#ifndef __builtin_expect--#define __builtin_expect(x, y) (x)--#endif----static __inline int openvibe_qjs_clz32(unsigned int v)--{-- unsigned long idx;-- if (_BitScanReverse(&idx, v)) return 31 - (int)idx;-- return 32;--}----static __inline int openvibe_qjs_ctz32(unsigned int v)--{-- unsigned long idx;-- if (_BitScanForward(&idx, v)) return (int)idx;-- return 32;--}----static __inline int openvibe_qjs_clz64(uint64_t v)--{-- uint32_t hi = (uint32_t)(v >> 32);-- uint32_t lo = (uint32_t)v;-- if (hi) return openvibe_qjs_clz32(hi);-- return 32 + openvibe_qjs_clz32(lo);--}----static __inline int openvibe_qjs_ctz64(uint64_t v)--{-- uint32_t hi = (uint32_t)(v >> 32);-- uint32_t lo = (uint32_t)v;-- if (lo) return openvibe_qjs_ctz32(lo);-- return 32 + openvibe_qjs_ctz32(hi);--}----#ifndef __builtin_clz--#define __builtin_clz(x) openvibe_qjs_clz32((unsigned int)(x))--#endif--#ifndef __builtin_ctz--#define __builtin_ctz(x) openvibe_qjs_ctz32((unsigned int)(x))--#endif--#ifndef __builtin_clzll--#define __builtin_clzll(x) openvibe_qjs_clz64((uint64_t)(x))--#endif--#ifndef __builtin_ctzll--#define __builtin_ctzll(x) openvibe_qjs_ctz64((uint64_t)(x))--#endif----#ifndef alloca--#define alloca _alloca--#endif--#ifndef strdup--#define strdup _strdup--#endif----#endif /* _MSC_VER */--#endif /* OPENVIBE_QJS_MSVC_COMPAT_H */--'@ | Set-Content -Encoding ascii $MsvcCompat----# Upstream QuickJS includes <sys/time.h>. MSVC does not provide it.--$TimeHeader = Join-Path $CompatSys 'time.h'--@'--#ifndef OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H--#define OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H----#if defined(_WIN32)--#ifndef WIN32_LEAN_AND_MEAN--#define WIN32_LEAN_AND_MEAN--#endif--#ifndef NOMINMAX--#define NOMINMAX--#endif--#include <windows.h>----#ifdef __cplusplus--extern "C" {--#endif----#ifndef _TIMEVAL_DEFINED--#define _TIMEVAL_DEFINED--typedef long suseconds_t;--struct timeval {-- long tv_sec;-- long tv_usec;--};--#endif----static __inline int gettimeofday(struct timeval *tv, void *tz)--{-- FILETIME ft;-- ULARGE_INTEGER uli;-- unsigned long long usec;-- const unsigned long long unix_epoch = 116444736000000000ULL;-- (void)tz;-- if (!tv) return -1;-- GetSystemTimeAsFileTime(&ft);-- uli.LowPart = ft.dwLowDateTime;-- uli.HighPart = ft.dwHighDateTime;-- usec = (uli.QuadPart - unix_epoch) / 10ULL;-- tv->tv_sec = (long)(usec / 1000000ULL);-- tv->tv_usec = (long)(usec % 1000000ULL);-- return 0;--}----#ifdef __cplusplus--}--#endif--#else--#include_next <sys/time.h>--#endif----#endif /* OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H */--'@ | Set-Content -Encoding ascii $TimeHeader----# Stub enough pthread API for accidental includes. CONFIG_ATOMICS is disabled below,--# so this should not be exercised, but the header keeps MSVC moving if upstream includes it.--$PThreadHeader = Join-Path $CompatInclude 'pthread.h'--@'--#ifndef OPENVIBE_QUICKJS_COMPAT_PTHREAD_H--#define OPENVIBE_QUICKJS_COMPAT_PTHREAD_H--#if defined(_WIN32)--typedef int pthread_mutex_t;--typedef int pthread_t;--typedef int pthread_attr_t;--#define PTHREAD_MUTEX_INITIALIZER 0--static __inline int pthread_mutex_init(pthread_mutex_t *m, const void *a) { (void)a; if (m) *m = 0; return 0; }--static __inline int pthread_mutex_destroy(pthread_mutex_t *m) { (void)m; return 0; }--static __inline int pthread_mutex_lock(pthread_mutex_t *m) { (void)m; return 0; }--static __inline int pthread_mutex_unlock(pthread_mutex_t *m) { (void)m; return 0; }--static __inline int pthread_create(pthread_t *t, const pthread_attr_t *a, void *(*fn)(void *), void *arg) { (void)t; (void)a; (void)fn; (void)arg; return -1; }--static __inline int pthread_join(pthread_t t, void **ret) { (void)t; if (ret) *ret = 0; return 0; }--#else--#include_next <pthread.h>--#endif--#endif /* OPENVIBE_QUICKJS_COMPAT_PTHREAD_H */--'@ | Set-Content -Encoding ascii $PThreadHeader----# Disable QuickJS atomics/workers for this embedded game runtime on MSVC.--# That avoids needing a real pthread implementation and is fine for our scripts.--$QuickJsC = Join-Path $SdkQjs 'quickjs.c'--if (Test-Path $QuickJsC) {-- $q = Get-Content -Raw $QuickJsC-- $q2 = [regex]::Replace($q, '(?m)^\s*#\s*define\s+CONFIG_ATOMICS\s*$', '/* OpenVibe MSVC build: CONFIG_ATOMICS disabled */')-- if ($q2 -ne $q) {-- Set-Content -Encoding ascii $QuickJsC $q2-- Say 'disabled CONFIG_ATOMICS in SDK quickjs.c copy'-- } else {-- Say 'CONFIG_ATOMICS define not found in quickjs.c copy; pthread shim remains available'-- }--}----Say "compat include=$CompatInclude"--Say "compat forced include=$MsvcCompat"--Say "compat sys/time.h=$TimeHeader"--Say "compat pthread.h=$PThreadHeader"----$sources = @('quickjs.c','libregexp.c','libunicode.c','cutils.c')--if (Test-Path (Join-Path $SdkQjs 'dtoa.c')) { $sources += 'dtoa.c' }--if (Test-Path (Join-Path $SdkQjs 'libbf.c')) { $sources += 'libbf.c' }----Remove-Item (Join-Path $Out '*.obj') -Force -ErrorAction SilentlyContinue--Remove-Item (Join-Path $Out 'libquickjs_openvibe.lib') -Force -ErrorAction SilentlyContinue----$common = @(-- '/nologo',-- '/TC',-- '/O2',-- '/MT',-- '/W3',-- '/D_CRT_SECURE_NO_WARNINGS',-- '/D_CRT_NONSTDC_NO_WARNINGS',-- '/DWIN32',-- '/D_WINDOWS',-- '/DNOMINMAX',-- '/DWIN32_LEAN_AND_MEAN',-- "/DCONFIG_VERSION=`\`"openvibe`\`"",-- "/FI$MsvcCompat",-- "/I$CompatInclude",-- "/I$SdkQjs"--)----$objects = @()--foreach ($src in $sources) {-- $srcPath = Join-Path $SdkQjs $src-- $obj = Join-Path $Out ([IO.Path]::GetFileNameWithoutExtension($src) + '.obj')-- Say "cl $src -> $obj"-- & cl.exe @common /c "$srcPath" "/Fo$obj"-- if ($LASTEXITCODE -ne 0) { throw "cl failed for $src" }-- if (!(Test-Path $obj)) { throw "cl reported success but did not produce $obj" }-- $objects += $obj--}----$outLib = Join-Path $Out 'libquickjs_openvibe.lib'--Say "lib -> $outLib"--& lib.exe /nologo "/OUT:$outLib" $objects--if ($LASTEXITCODE -ne 0) { throw "lib.exe failed" }--if (!(Test-Path $outLib)) { throw "lib.exe did not produce $outLib" }----Say "built $outLib"--PS1----cat > docs/WINDOWS_QUICKJS_MSVC.md <<'MD'--# Windows QuickJS MSVC Build Note----The vendored QuickJS source is GCC/clang oriented. On Windows/MSVC it needs compatibility for:----- POSIX `sys/time.h`--- accidental `pthread.h` includes--- GNU `__attribute__((...))`--- GNU `__builtin_clz`, `__builtin_ctz`, and `__builtin_expect`----`tools/build-quickjs-lib-windows.ps1` now generates build-local compatibility headers under:----```text--engine/source-sdk-2013/src/game/shared/openvibe/third_party/quickjs/build/compat/include--```----It also force-includes `openvibe_qjs_msvc_compat.h` for every QuickJS source file and disables `CONFIG_ATOMICS` in the copied SDK-side `quickjs.c` before compiling. Linux builds are unchanged.--MD----# Keep generated diagnostics/build deps out of git.--if [[ -f .gitignore ]]; then-- grep -qxF 'artifacts/' .gitignore || echo 'artifacts/' >> .gitignore-- grep -qxF '_deps/' .gitignore || echo '_deps/' >> .gitignore-- grep -qxF 'engine/source-sdk-2013/' .gitignore || echo 'engine/source-sdk-2013/' >> .gitignore--else-- cat > .gitignore <<'GI'--artifacts/--_deps/--engine/source-sdk-2013/--GI--fi----git rm -r --cached artifacts >/dev/null 2>&1 || true----git add .gitignore docs/WINDOWS_QUICKJS_MSVC.md tools/build-quickjs-lib-windows.ps1 "$0" 2>/dev/null || \-- git add .gitignore docs/WINDOWS_QUICKJS_MSVC.md tools/build-quickjs-lib-windows.ps1----say "git diff summary"--git diff --cached --stat || true----if ! git diff --cached --quiet; then-- git commit -m "Fix QuickJS MSVC GNU compatibility"-- git push--else-- say "no changes to commit"--fi----if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then-- say "triggering clean Windows DLL build"-- tools/trigger-windows-dll-build-clean.sh--else-- REPO="$(git remote get-url origin | sed -E 's#^[email protected]:##; s#^https://github.com/##; s#\.git$##')"-- say "triggering workflow repo=$REPO"-- gh workflow run windows-source-sdk-dlls.yml --repo "$REPO" --ref "$BRANCH"-- sleep 5-- gh run list --repo "$REPO" --workflow windows-source-sdk-dlls.yml --limit 3--fi----say "done"ddeleted file mode 100755@@@ -1,199 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--BRANCH="$(git -C "$ROOT" branch --show-current 2>/dev/null || true)"--cd "$ROOT"----echo "[openvibe] fix QuickJS MSVC sys/time.h shim + rerun Windows DLL workflow"--echo "[openvibe] root=$ROOT"--echo "[openvibe] branch=${BRANCH:-unknown}"----mkdir -p tools docs----cat > tools/build-quickjs-lib-windows.ps1 <<'PS1'--$ErrorActionPreference = 'Stop'----$Root = if ($env:OPENVIBE_ROOT) { (Resolve-Path $env:OPENVIBE_ROOT).Path } else { (Resolve-Path (Join-Path $PSScriptRoot '..')).Path }--$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root 'engine/source-sdk-2013' }----$SrcQjs = Join-Path $Root 'sdk/openvibe/third_party/quickjs'--$SdkQjs = Join-Path $Sdk 'src/game/shared/openvibe/third_party/quickjs'--$Out = Join-Path $SdkQjs 'build'--$LogDir = Join-Path $Root 'artifacts/windows-build-debug'----function Say($m) { Write-Host "[openvibe-qjs-win] $m" }----if (!(Test-Path (Join-Path $SrcQjs 'quickjs.c'))) {-- throw "Missing $SrcQjs/quickjs.c. Run tools/vendor-quickjs.sh first."--}--if (!(Get-Command cl.exe -ErrorAction SilentlyContinue)) {-- throw "cl.exe not found. Run this from a Visual Studio Developer PowerShell or Developer Command Prompt."--}--if (!(Get-Command lib.exe -ErrorAction SilentlyContinue)) {-- throw "lib.exe not found. Run this from a Visual Studio Developer PowerShell or Developer Command Prompt."--}----New-Item -ItemType Directory -Force -Path $SdkQjs, $Out, $LogDir | Out-Null--Copy-Item -Path (Join-Path $SrcQjs '*') -Destination $SdkQjs -Recurse -Force----# Upstream QuickJS includes <sys/time.h>. MSVC does not ship that POSIX header.--# Provide a tiny compatibility header that exposes timeval + gettimeofday for quickjs.c.--$CompatInclude = Join-Path $Out 'compat/include'--$CompatSys = Join-Path $CompatInclude 'sys'--New-Item -ItemType Directory -Force -Path $CompatSys | Out-Null--$TimeHeader = Join-Path $CompatSys 'time.h'--@'--#ifndef OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H--#define OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H----#if defined(_WIN32)--#ifndef WIN32_LEAN_AND_MEAN--#define WIN32_LEAN_AND_MEAN--#endif--#ifndef NOMINMAX--#define NOMINMAX--#endif--#include <windows.h>----#ifdef __cplusplus--extern "C" {--#endif----#ifndef _TIMEVAL_DEFINED--#define _TIMEVAL_DEFINED--typedef long suseconds_t;--struct timeval {-- long tv_sec;-- long tv_usec;--};--#endif----static __inline int gettimeofday(struct timeval *tv, void *tz)--{-- FILETIME ft;-- ULARGE_INTEGER uli;-- unsigned long long usec;-- const unsigned long long unix_epoch = 116444736000000000ULL;-- (void)tz;-- if (!tv) return -1;-- GetSystemTimeAsFileTime(&ft);-- uli.LowPart = ft.dwLowDateTime;-- uli.HighPart = ft.dwHighDateTime;-- usec = (uli.QuadPart - unix_epoch) / 10ULL;-- tv->tv_sec = (long)(usec / 1000000ULL);-- tv->tv_usec = (long)(usec % 1000000ULL);-- return 0;--}----#ifdef __cplusplus--}--#endif--#else--#include_next <sys/time.h>--#endif----#endif /* OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H */--'@ | Set-Content -Encoding ascii $TimeHeader----Say "compat include=$CompatInclude"--Say "compat header=$TimeHeader"----$sources = @('quickjs.c','libregexp.c','libunicode.c','cutils.c')--if (Test-Path (Join-Path $SdkQjs 'dtoa.c')) { $sources += 'dtoa.c' }--if (Test-Path (Join-Path $SdkQjs 'libbf.c')) { $sources += 'libbf.c' }----Remove-Item (Join-Path $Out '*.obj') -Force -ErrorAction SilentlyContinue--Remove-Item (Join-Path $Out 'libquickjs_openvibe.lib') -Force -ErrorAction SilentlyContinue----$common = @(-- '/nologo',-- '/TC',-- '/O2',-- '/MT',-- '/W3',-- '/D_CRT_SECURE_NO_WARNINGS',-- '/DWIN32',-- '/D_WINDOWS',-- '/DNOMINMAX',-- '/DWIN32_LEAN_AND_MEAN',-- "/DCONFIG_VERSION=`\`"openvibe`\`"",-- "/I$CompatInclude",-- "/I$SdkQjs"--)----$objects = @()--foreach ($src in $sources) {-- $srcPath = Join-Path $SdkQjs $src-- $obj = Join-Path $Out ([IO.Path]::GetFileNameWithoutExtension($src) + '.obj')-- Say "cl $src -> $obj"-- & cl.exe @common /c "$srcPath" "/Fo$obj"-- if ($LASTEXITCODE -ne 0) { throw "cl failed for $src" }-- if (!(Test-Path $obj)) { throw "cl reported success but did not produce $obj" }-- $objects += $obj--}----$outLib = Join-Path $Out 'libquickjs_openvibe.lib'--Say "lib -> $outLib"--& lib.exe /nologo "/OUT:$outLib" $objects--if ($LASTEXITCODE -ne 0) { throw "lib.exe failed" }--if (!(Test-Path $outLib)) { throw "lib.exe did not produce $outLib" }----Say "built $outLib"--PS1----cat > docs/WINDOWS_QUICKJS_MSVC.md <<'MD'--# Windows QuickJS MSVC Build Note----The vendored QuickJS C source includes POSIX `<sys/time.h>`, which MSVC does not provide. The Windows QuickJS build helper now generates a tiny compatibility include directory at build time:----```text--engine/source-sdk-2013/src/game/shared/openvibe/third_party/quickjs/build/compat/include/sys/time.h--```----That shim defines `struct timeval` and `gettimeofday()` using `GetSystemTimeAsFileTime()`, then passes the compatibility include directory to `cl.exe` before compiling QuickJS.----This keeps Linux builds unchanged while allowing the Windows GitHub Actions runner to produce `libquickjs_openvibe.lib` for `client.dll` / `server.dll` builds.--MD----# Keep local generated diagnostics out of future commits.--if [[ -f .gitignore ]]; then-- grep -qxF 'artifacts/' .gitignore || echo 'artifacts/' >> .gitignore-- grep -qxF '_deps/' .gitignore || echo '_deps/' >> .gitignore-- grep -qxF 'engine/source-sdk-2013/' .gitignore || echo 'engine/source-sdk-2013/' >> .gitignore--else-- cat > .gitignore <<'GI'--artifacts/--_deps/--engine/source-sdk-2013/--GI--fi----# Avoid staging downloaded workflow diagnostics again.--git rm -r --cached artifacts >/dev/null 2>&1 || true----git add .gitignore docs/WINDOWS_QUICKJS_MSVC.md tools/build-quickjs-lib-windows.ps1 "$0" 2>/dev/null || \-- git add .gitignore docs/WINDOWS_QUICKJS_MSVC.md tools/build-quickjs-lib-windows.ps1----echo "[openvibe] git diff summary"--git diff --cached --stat || true----if ! git diff --cached --quiet; then-- git commit -m "Fix QuickJS MSVC compatibility build"-- git push--else-- echo "[openvibe] no changes to commit"--fi----if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then-- echo "[openvibe] triggering clean Windows DLL build"-- tools/trigger-windows-dll-build-clean.sh--else-- REPO="$(git remote get-url origin | sed -E 's#^[email protected]:##; s#^https://github.com/##; s#\.git$##')"-- echo "[openvibe] triggering workflow repo=$REPO"-- gh workflow run windows-source-sdk-dlls.yml --repo "$REPO" --ref "${BRANCH:-codex/openvibe-next-steps}"-- sleep 5-- gh run list --repo "$REPO" --workflow windows-source-sdk-dlls.yml --limit 3--fi----echo "[openvibe] done"ddeleted file mode 100755@@@ -1,342 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"--cd "$ROOT"--BRANCH="$(git branch --show-current)"----say() { echo "[openvibe] $*"; }----say "fix QuickJS Windows build: use clang-cl for GNU/C99 QuickJS objects"--say "root=$ROOT"--say "branch=$BRANCH"----mkdir -p tools docs----cat > tools/build-quickjs-lib-windows.ps1 <<'PS1'--$ErrorActionPreference = 'Stop'----$Root = if ($env:OPENVIBE_ROOT) { (Resolve-Path $env:OPENVIBE_ROOT).Path } else { (Resolve-Path (Join-Path $PSScriptRoot '..')).Path }--$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root 'engine/source-sdk-2013' }----$SrcQjs = Join-Path $Root 'sdk/openvibe/third_party/quickjs'--$SdkQjs = Join-Path $Sdk 'src/game/shared/openvibe/third_party/quickjs'--$Out = Join-Path $SdkQjs 'build'--$LogDir = Join-Path $Root 'artifacts/windows-build-debug'----function Say($m) { Write-Host "[openvibe-qjs-win] $m" }--function FirstExistingCommand([string[]]$names) {-- foreach ($name in $names) {-- $cmd = Get-Command $name -ErrorAction SilentlyContinue-- if ($cmd) { return $cmd.Source }-- }-- return $null--}--function FirstExistingPath([string[]]$paths) {-- foreach ($p in $paths) {-- if ($p -and (Test-Path $p)) { return (Resolve-Path $p).Path }-- }-- return $null--}----if (!(Test-Path (Join-Path $SrcQjs 'quickjs.c'))) {-- throw "Missing $SrcQjs/quickjs.c. Run tools/vendor-quickjs.sh first."--}--if (!(Get-Command lib.exe -ErrorAction SilentlyContinue)) {-- throw "lib.exe not found. Run this from a Visual Studio Developer PowerShell or Developer Command Prompt."--}----New-Item -ItemType Directory -Force -Path $SdkQjs, $Out, $LogDir | Out-Null--Copy-Item -Path (Join-Path $SrcQjs '*') -Destination $SdkQjs -Recurse -Force----$CompatInclude = Join-Path $Out 'compat/include'--$CompatSys = Join-Path $CompatInclude 'sys'--New-Item -ItemType Directory -Force -Path $CompatInclude, $CompatSys | Out-Null----# QuickJS is written for GCC/clang C, not plain MSVC C. Prefer clang-cl because it--# emits MSVC-compatible COFF .obj files while accepting GNU/C99 constructs that--# QuickJS uses heavily. Keep cl.exe only as a last-resort fallback.--$clangCandidates = @(-- (FirstExistingCommand @('clang-cl.exe','clang-cl')),-- (Join-Path ${env:ProgramFiles} 'LLVM/bin/clang-cl.exe'),-- (Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/2022/Enterprise/VC/Tools/Llvm/x86/bin/clang-cl.exe'),-- (Join-Path ${env:ProgramFiles} 'Microsoft Visual Studio/2022/Enterprise/VC/Tools/Llvm/x86/bin/clang-cl.exe'),-- (Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/2022/BuildTools/VC/Tools/Llvm/x86/bin/clang-cl.exe'),-- (Join-Path ${env:ProgramFiles} 'Microsoft Visual Studio/2022/BuildTools/VC/Tools/Llvm/x86/bin/clang-cl.exe')--)--$ClangCl = FirstExistingPath $clangCandidates--$ClExe = FirstExistingCommand @('cl.exe')--if ($ClangCl) { Say "using clang-cl=$ClangCl" }--elseif ($ClExe) { Say "clang-cl not found; falling back to cl.exe=$ClExe" }--else { throw "Neither clang-cl.exe nor cl.exe was found." }----# Windows sys/time.h shim for upstream QuickJS.--$TimeHeader = Join-Path $CompatSys 'time.h'--@'--#ifndef OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H--#define OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H--#if defined(_WIN32)--#ifndef WIN32_LEAN_AND_MEAN--#define WIN32_LEAN_AND_MEAN--#endif--#ifndef NOMINMAX--#define NOMINMAX--#endif--#include <windows.h>--#ifdef __cplusplus--extern "C" {--#endif--#ifndef _TIMEVAL_DEFINED--#define _TIMEVAL_DEFINED--typedef long suseconds_t;--struct timeval {-- long tv_sec;-- long tv_usec;--};--#endif--static __inline int gettimeofday(struct timeval *tv, void *tz)--{-- FILETIME ft;-- ULARGE_INTEGER uli;-- unsigned long long usec;-- const unsigned long long unix_epoch = 116444736000000000ULL;-- (void)tz;-- if (!tv) return -1;-- GetSystemTimeAsFileTime(&ft);-- uli.LowPart = ft.dwLowDateTime;-- uli.HighPart = ft.dwHighDateTime;-- usec = (uli.QuadPart - unix_epoch) / 10ULL;-- tv->tv_sec = (long)(usec / 1000000ULL);-- tv->tv_usec = (long)(usec % 1000000ULL);-- return 0;--}--#ifdef __cplusplus--}--#endif--#else--#include_next <sys/time.h>--#endif--#endif--'@ | Set-Content -Encoding ascii $TimeHeader----# pthread.h shim. We also disable CONFIG_ATOMICS below, so this is only to satisfy--# accidental includes from vendored QuickJS variants.--$PThreadHeader = Join-Path $CompatInclude 'pthread.h'--@'--#ifndef OPENVIBE_QUICKJS_COMPAT_PTHREAD_H--#define OPENVIBE_QUICKJS_COMPAT_PTHREAD_H--#if defined(_WIN32)--typedef int pthread_mutex_t;--typedef int pthread_t;--typedef int pthread_attr_t;--#define PTHREAD_MUTEX_INITIALIZER 0--static __inline int pthread_mutex_init(pthread_mutex_t *m, const void *a) { (void)a; if (m) *m = 0; return 0; }--static __inline int pthread_mutex_destroy(pthread_mutex_t *m) { (void)m; return 0; }--static __inline int pthread_mutex_lock(pthread_mutex_t *m) { (void)m; return 0; }--static __inline int pthread_mutex_unlock(pthread_mutex_t *m) { (void)m; return 0; }--static __inline int pthread_create(pthread_t *t, const pthread_attr_t *a, void *(*fn)(void *), void *arg) { (void)t; (void)a; (void)fn; (void)arg; return -1; }--static __inline int pthread_join(pthread_t t, void **ret) { (void)t; if (ret) *ret = 0; return 0; }--#else--#include_next <pthread.h>--#endif--#endif--'@ | Set-Content -Encoding ascii $PThreadHeader----# Forced include for clang-cl. Do NOT redefine __attribute__ or __builtin_* here;--# clang handles those and QuickJS needs them.--$ClangCompat = Join-Path $CompatInclude 'openvibe_qjs_clangcl_compat.h'--@'--#ifndef OPENVIBE_QJS_CLANGCL_COMPAT_H--#define OPENVIBE_QJS_CLANGCL_COMPAT_H--#if defined(_WIN32)--#ifndef WIN32_LEAN_AND_MEAN--#define WIN32_LEAN_AND_MEAN--#endif--#ifndef NOMINMAX--#define NOMINMAX--#endif--#include <stdint.h>--#include <stdlib.h>--#include <malloc.h>--#ifndef alloca--#define alloca _alloca--#endif--#ifndef strdup--#define strdup _strdup--#endif--#endif--#endif--'@ | Set-Content -Encoding ascii $ClangCompat----# Forced include for emergency cl.exe fallback only.--$MsvcCompat = Join-Path $CompatInclude 'openvibe_qjs_msvc_compat.h'--@'--#ifndef OPENVIBE_QJS_MSVC_COMPAT_H--#define OPENVIBE_QJS_MSVC_COMPAT_H--#if defined(_MSC_VER) && !defined(__clang__)--#ifndef WIN32_LEAN_AND_MEAN--#define WIN32_LEAN_AND_MEAN--#endif--#ifndef NOMINMAX--#define NOMINMAX--#endif--#include <stdint.h>--#include <stdlib.h>--#include <malloc.h>--#include <intrin.h>--#ifndef __attribute__--#define __attribute__(x)--#endif--#ifndef __builtin_expect--#define __builtin_expect(x, y) (x)--#endif--#ifndef __builtin_frame_address--#define __builtin_frame_address(x) ((void*)0)--#endif--static __inline int openvibe_qjs_clz32(unsigned int v) { unsigned long idx; if (_BitScanReverse(&idx, v)) return 31 - (int)idx; return 32; }--static __inline int openvibe_qjs_ctz32(unsigned int v) { unsigned long idx; if (_BitScanForward(&idx, v)) return (int)idx; return 32; }--static __inline int openvibe_qjs_clz64(uint64_t v) { uint32_t hi = (uint32_t)(v >> 32); uint32_t lo = (uint32_t)v; if (hi) return openvibe_qjs_clz32(hi); return 32 + openvibe_qjs_clz32(lo); }--static __inline int openvibe_qjs_ctz64(uint64_t v) { uint32_t hi = (uint32_t)(v >> 32); uint32_t lo = (uint32_t)v; if (lo) return openvibe_qjs_ctz32(lo); return 32 + openvibe_qjs_ctz32(hi); }--#ifndef __builtin_clz--#define __builtin_clz(x) openvibe_qjs_clz32((unsigned int)(x))--#endif--#ifndef __builtin_ctz--#define __builtin_ctz(x) openvibe_qjs_ctz32((unsigned int)(x))--#endif--#ifndef __builtin_clzll--#define __builtin_clzll(x) openvibe_qjs_clz64((uint64_t)(x))--#endif--#ifndef __builtin_ctzll--#define __builtin_ctzll(x) openvibe_qjs_ctz64((uint64_t)(x))--#endif--#ifndef alloca--#define alloca _alloca--#endif--#ifndef strdup--#define strdup _strdup--#endif--#endif--#endif--'@ | Set-Content -Encoding ascii $MsvcCompat----# Disable QuickJS atomics/workers for the embedded game runtime.--$QuickJsC = Join-Path $SdkQjs 'quickjs.c'--if (Test-Path $QuickJsC) {-- $q = Get-Content -Raw $QuickJsC-- $q2 = [regex]::Replace($q, '(?m)^\s*#\s*define\s+CONFIG_ATOMICS\s*$', '/* OpenVibe Windows embedded build: CONFIG_ATOMICS disabled */')-- if ($q2 -ne $q) {-- Set-Content -Encoding ascii $QuickJsC $q2-- Say 'disabled CONFIG_ATOMICS in SDK quickjs.c copy'-- } else {-- Say 'CONFIG_ATOMICS define not found in quickjs.c copy; pthread shim remains available'-- }--}----Say "compat include=$CompatInclude"--Say "compat sys/time.h=$TimeHeader"--Say "compat pthread.h=$PThreadHeader"--Say "clang forced include=$ClangCompat"--Say "msvc forced include=$MsvcCompat"----$sources = @('quickjs.c','libregexp.c','libunicode.c','cutils.c')--if (Test-Path (Join-Path $SdkQjs 'dtoa.c')) { $sources += 'dtoa.c' }--if (Test-Path (Join-Path $SdkQjs 'libbf.c')) { $sources += 'libbf.c' }----Remove-Item (Join-Path $Out '*.obj') -Force -ErrorAction SilentlyContinue--Remove-Item (Join-Path $Out 'libquickjs_openvibe.lib') -Force -ErrorAction SilentlyContinue----if ($ClangCl) {-- $Compiler = $ClangCl-- $Common = @(-- '/nologo',-- '/TC',-- '/O2',-- '/MT',-- '/W3',-- '/D_CRT_SECURE_NO_WARNINGS',-- '/D_CRT_NONSTDC_NO_WARNINGS',-- '/DWIN32',-- '/D_WINDOWS',-- '/DNOMINMAX',-- '/DWIN32_LEAN_AND_MEAN',-- "/DCONFIG_VERSION=`\`"openvibe`\`"",-- "/FI$ClangCompat",-- "/I$CompatInclude",-- "/I$SdkQjs",-- '/clang:-std=gnu11',-- '/clang:-Wno-everything'-- )--} else {-- $Compiler = $ClExe-- $Common = @(-- '/nologo',-- '/TC',-- '/O2',-- '/MT',-- '/W3',-- '/D_CRT_SECURE_NO_WARNINGS',-- '/D_CRT_NONSTDC_NO_WARNINGS',-- '/DWIN32',-- '/D_WINDOWS',-- '/DNOMINMAX',-- '/DWIN32_LEAN_AND_MEAN',-- "/DCONFIG_VERSION=`\`"openvibe`\`"",-- "/FI$MsvcCompat",-- "/I$CompatInclude",-- "/I$SdkQjs"-- )--}----$objects = @()--foreach ($src in $sources) {-- $srcPath = Join-Path $SdkQjs $src-- $obj = Join-Path $Out ([IO.Path]::GetFileNameWithoutExtension($src) + '.obj')-- Say "$([IO.Path]::GetFileName($Compiler)) $src -> $obj"-- & $Compiler @Common /c "$srcPath" "/Fo$obj"-- if ($LASTEXITCODE -ne 0) { throw "$Compiler failed for $src" }-- if (!(Test-Path $obj)) { throw "$Compiler reported success but did not produce $obj" }-- $objects += $obj--}----$outLib = Join-Path $Out 'libquickjs_openvibe.lib'--Say "lib -> $outLib"--& lib.exe /nologo "/OUT:$outLib" $objects--if ($LASTEXITCODE -ne 0) { throw "lib.exe failed" }--if (!(Test-Path $outLib)) { throw "lib.exe did not produce $outLib" }--Say "built $outLib"--PS1----cat > docs/WINDOWS_QUICKJS_CLANGCL.md <<'MD'--# Windows QuickJS build uses clang-cl----QuickJS uses GNU/C99 compiler features that plain `cl.exe` does not parse cleanly, including GNU attributes, GCC/Clang builtins, and C initializer patterns. The Windows workflow now prefers `clang-cl.exe` for QuickJS only. `clang-cl` emits MSVC-compatible COFF `.obj` files, and `lib.exe` packs them into `libquickjs_openvibe.lib` for the regular Source SDK MSVC link.----The rest of the Source SDK build still uses the Visual Studio/MSBuild toolchain.--MD----chmod +x tools/trigger-windows-dll-build-clean.sh 2>/dev/null || true----git add tools/build-quickjs-lib-windows.ps1 docs/WINDOWS_QUICKJS_CLANGCL.md "$0" 2>/dev/null || git add tools/build-quickjs-lib-windows.ps1 docs/WINDOWS_QUICKJS_CLANGCL.md----say "git diff summary"--git diff --cached --stat || true----if git diff --cached --quiet; then-- say "no changes to commit"--else-- git commit -m "Build QuickJS with clang-cl on Windows"-- git push--fi----say "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 windows-source-sdk-dlls.yml --ref "$BRANCH"-- sleep 10-- RUN_ID="$(gh run list --workflow windows-source-sdk-dlls.yml --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"-- gh run watch "$RUN_ID" --exit-status || true-- tools/windows-workflow-debug-and-install.sh "$RUN_ID" || true--fi----say "done"ddeleted file mode 100755@@@ -1,104 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--QJS="$ROOT/sdk/openvibe/third_party/quickjs"----cd "$ROOT"----echo "[openvibe] forcing clean QuickJS re-vendor"--rm -rf "$QJS"--tools/vendor-quickjs.sh----echo "[openvibe] replacing smoke-quickjs.sh with robust version"----cat > tools/smoke-quickjs.sh <<'SMOKE'--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--QJS="$ROOT/sdk/openvibe/third_party/quickjs"--BUILD="$ROOT/.tmp/quickjs-smoke"----rm -rf "$BUILD"--mkdir -p "$BUILD"----cat > "$BUILD/smoke.c" <<'C'--#include <stdio.h>--#include "quickjs.h"----int main(void) {-- JSRuntime *rt = JS_NewRuntime();-- JSContext *ctx = JS_NewContext(rt);---- JSValue value = JS_Eval(ctx, "1 + 2 + 3", 9, "<smoke>", JS_EVAL_TYPE_GLOBAL);-- int32_t out = 0;-- JS_ToInt32(ctx, &out, value);---- JS_FreeValue(ctx, value);-- JS_FreeContext(ctx);-- JS_FreeRuntime(rt);---- printf("quickjs result=%d\n", out);-- return out == 6 ? 0 : 1;--}--C----sources=(-- "$QJS/quickjs.c"-- "$QJS/libregexp.c"-- "$QJS/libunicode.c"-- "$QJS/cutils.c"--)----[[ -f "$QJS/dtoa.c" ]] && sources+=("$QJS/dtoa.c")--[[ -f "$QJS/libbf.c" ]] && sources+=("$QJS/libbf.c")----cc \-- -std=gnu11 \-- -D_GNU_SOURCE \-- -DCONFIG_VERSION=\"openvibe\" \-- -I"$QJS" \-- "$BUILD/smoke.c" \-- "${sources[@]}" \-- -lm \-- -ldl \-- -lpthread \-- -o "$BUILD/smoke-quickjs"----"$BUILD/smoke-quickjs"--SMOKE----chmod +x tools/smoke-quickjs.sh----echo "[openvibe] patching apply-openvibe-sdk.sh to not require libbf.c blindly"----python3 <<'PY'--from pathlib import Path----p = Path("tools/apply-openvibe-sdk.sh")--s = p.read_text()----# Remove hardcoded libbf VPC line insertion if present.--s = s.replace(-- '\\n\\t\\t\\t\\$File\\t"..\\\\shared\\\\openvibe\\\\third_party\\\\quickjs\\\\libbf.c"',-- ''--)----# Also remove the cleanup regex for libbf so repeated runs don't matter.--s = s.replace(-- ' s/^.*openvibe\\\\\\\\third_party\\\\\\\\quickjs\\\\\\\\libbf\\\\.c.*\\n//mg;\\n',-- ''--)----p.write_text(s)--PY----echo "[openvibe] quickjs folder now contains:"--find "$QJS" -maxdepth 1 -type f -printf " - %f\n" | sort----echo "[openvibe] running smoke test"--tools/smoke-quickjs.sh----echo--echo "[openvibe] QuickJS vendor/smoke fixed."ddeleted file mode 100755@@@ -1,215 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"--RUN_BUILD="${RUN_BUILD:-1}"--STAMP="$(date +%Y%m%d-%H%M%S)"----cd "$ROOT"----backup_file() {-- local file="$1"-- [[ -f "$file" ]] && cp "$file" "$file.bak.$STAMP"--}----say() {-- echo "[openvibe] $*"--}----say "fixing QuickJS static-library VPC link issue"----mkdir -p tools----# Clean accidentally tracked local/generated junk if it exists.--say "cleaning accidental tracked junk if present"--git rm -f .tmp/quickjs-smoke/smoke-quickjs 2>/dev/null || true--git rm -f tools/apply-openvibe-sdk.sh.bak.* 2>/dev/null || true----touch .gitignore--if ! grep -qxF ".tmp/" .gitignore; then-- cat >> .gitignore <<'GITIGNORE_ADD'----# OpenVibe local/generated build junk--.tmp/--*.bak.*--tools/*.bak.*--engine/source-sdk-2013/src/game/shared/openvibe/third_party/quickjs/build/--GITIGNORE_ADD--fi----say "writing tools/build-quickjs-lib.sh"----cat > tools/build-quickjs-lib.sh <<'BUILDQJS'--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"----SRC_QJS="$ROOT/sdk/openvibe/third_party/quickjs"--SDK_QJS="$SDK/src/game/shared/openvibe/third_party/quickjs"--OUT="$SDK_QJS/build"----if [[ ! -f "$SRC_QJS/quickjs.c" ]]; then-- echo "Missing $SRC_QJS/quickjs.c" >&2-- echo "Run: tools/vendor-quickjs.sh" >&2-- exit 1--fi----mkdir -p "$SDK_QJS"----# Exclude build/ so rsync does not delete our .o/.a output dir.--rsync -a --delete --exclude='build/' "$SRC_QJS/" "$SDK_QJS/"----mkdir -p "$OUT"----sources=(-- quickjs.c-- libregexp.c-- libunicode.c-- cutils.c--)----[[ -f "$SDK_QJS/dtoa.c" ]] && sources+=(dtoa.c)--[[ -f "$SDK_QJS/libbf.c" ]] && sources+=(libbf.c)----rm -f "$OUT"/*.o "$OUT"/libquickjs_openvibe.a----for src in "${sources[@]}"; do-- obj="$OUT/${src%.c}.o"-- echo "[openvibe-qjs] cc $src -> $obj"---- cc \-- -std=gnu11 \-- -O2 \-- -fPIC \-- -D_GNU_SOURCE \-- -DCONFIG_VERSION=\"openvibe\" \-- -I"$SDK_QJS" \-- -c "$SDK_QJS/$src" \-- -o "$obj"--done----ar rcs "$OUT/libquickjs_openvibe.a" "$OUT"/*.o----echo "[openvibe-qjs] built $OUT/libquickjs_openvibe.a"--ls -lh "$OUT/libquickjs_openvibe.a"--BUILDQJS----chmod +x tools/build-quickjs-lib.sh----say "patching tools/apply-openvibe-sdk.sh"----if [[ ! -f tools/apply-openvibe-sdk.sh ]]; then-- echo "Missing tools/apply-openvibe-sdk.sh" >&2-- exit 1--fi----backup_file tools/apply-openvibe-sdk.sh----python3 <<'PY'--from pathlib import Path--import re----p = Path("tools/apply-openvibe-sdk.sh")--s = p.read_text()----# Remove older injected QuickJS final blocks so this script stays idempotent.--patterns = [-- r'\n# OPENVIBE_REMOVE_DIRECT_QUICKJS_C\n.*?(?=\n# OPENVIBE|\necho "\[openvibe-sdk\] Source SDK OpenVibe patch applied"|\Z)',-- r'\n# OPENVIBE_LINK_QUICKJS_STATIC_LIB\n.*?(?=\n# OPENVIBE|\necho "\[openvibe-sdk\] Source SDK OpenVibe patch applied"|\Z)',-- r'\n# OPENVIBE_FIX_QUICKJS_LINK_FINAL_V2\n.*?(?=\n# OPENVIBE|\necho "\[openvibe-sdk\] Source SDK OpenVibe patch applied"|\Z)',--]--for pat in patterns:-- s = re.sub(pat, "\n", s, flags=re.S)----# Do not allow a literal libquickjs_openvibe.a in any VPC $Lib line.--# VPC appends .a itself, so ".a" here becomes ".a.a".--s = s.replace("libquickjs_openvibe.a", "libquickjs_openvibe")----# Ensure apply script calls the QuickJS static library builder.--if '"$ROOT/tools/build-quickjs-lib.sh"' not in s:-- needle = 'copy_tree "$ROOT/sdk/openvibe/third_party/quickjs" \\\n "$SDK/src/game/shared/openvibe/third_party/quickjs"'-- if needle in s:-- s = s.replace(needle, needle + '\n\n"$ROOT/tools/build-quickjs-lib.sh"')-- else:-- s += '\n\n"$ROOT/tools/build-quickjs-lib.sh"\n'----final_block = r'''--# OPENVIBE_FIX_QUICKJS_LINK_FINAL_V2--# QuickJS is C, so do not compile its .c files through Source SDK/VPC C++.--# We build libquickjs_openvibe.a with cc and link it as a library.--if [[ -f "$SERVER_VPC" ]]; then-- perl -0pi -e '-- s/^.*quickjs\\quickjs\.c.*\n//mg;-- s/^.*quickjs\\libregexp\.c.*\n//mg;-- s/^.*quickjs\\libunicode\.c.*\n//mg;-- s/^.*quickjs\\cutils\.c.*\n//mg;-- s/^.*quickjs\\dtoa\.c.*\n//mg;-- s/^.*quickjs\\libbf\.c.*\n//mg;-- ' "$SERVER_VPC"---- perl -0pi -e 's/^.*libquickjs_openvibe(?:\.a)?".*\n//mg' "$SERVER_VPC"---- perl -0pi -e 's/(\$File\s+"hl2mp\\openvibe_js_server\.cpp"\n)/$1\t\t\t\$Lib\t"..\\shared\\openvibe\\third_party\\quickjs\\build\\libquickjs_openvibe"\n/s' "$SERVER_VPC"---- echo "[openvibe-sdk] linked QuickJS static library"--fi--'''----final_echo = 'echo "[openvibe-sdk] Source SDK OpenVibe patch applied"'--if final_echo in s:-- s = s.replace(final_echo, final_block + "\n" + final_echo)--else:-- s += "\n" + final_block + "\n"----p.write_text(s)--PY----chmod +x tools/apply-openvibe-sdk.sh----say "applying SDK patch"--tools/apply-openvibe-sdk.sh----SERVER_VPC="$SDK/src/game/server/server_hl2mp.vpc"----say "force-fixing already-applied SDK server_hl2mp.vpc"----if [[ -f "$SERVER_VPC" ]]; then-- backup_file "$SERVER_VPC"---- perl -0pi -e '-- s/^.*quickjs\\quickjs\.c.*\n//mg;-- s/^.*quickjs\\libregexp\.c.*\n//mg;-- s/^.*quickjs\\libunicode\.c.*\n//mg;-- s/^.*quickjs\\cutils\.c.*\n//mg;-- s/^.*quickjs\\dtoa\.c.*\n//mg;-- s/^.*quickjs\\libbf\.c.*\n//mg;-- ' "$SERVER_VPC"---- perl -0pi -e 's/^.*libquickjs_openvibe(?:\.a)?".*\n//mg' "$SERVER_VPC"-- perl -0pi -e 's/(\$File\s+"hl2mp\\openvibe_js_server\.cpp"\n)/$1\t\t\t\$Lib\t"..\\shared\\openvibe\\third_party\\quickjs\\build\\libquickjs_openvibe"\n/s' "$SERVER_VPC"--fi----say "building QuickJS static lib"--tools/build-quickjs-lib.sh----say "VPC quickjs check"--if [[ -f "$SERVER_VPC" ]]; then-- grep -nE 'quickjs\\(quickjs|libregexp|libunicode|cutils|dtoa|libbf)\.c|libquickjs_openvibe' "$SERVER_VPC" || true--else-- echo "Missing $SERVER_VPC" >&2--fi----if [[ "$RUN_BUILD" = "1" ]]; then-- say "running SDK build"-- tools/build-sdk-linux.sh 2>&1 | tee "$HOME/ov-build.log"---- say "build finished. Last lines:"-- tail -80 "$HOME/ov-build.log"--else-- say "skipping build because RUN_BUILD=0"-- echo "Run manually:"-- echo " tools/build-sdk-linux.sh 2>&1 | tee ~/ov-build.log"--fiddeleted file mode 100755@@@ -1,127 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----say() { echo "[openvibe] $*"; }--warn() { echo "[openvibe warn] $*" >&2; }--fail() { echo "[openvibe error] $*" >&2; exit 1; }----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--cd "$ROOT"--BRANCH="${OPENVIBE_BRANCH:-$(git rev-parse --abbrev-ref HEAD)}"--REPO="${OPENVIBE_REPO:-$(tools/openvibe-gh-repo.sh)}"--WORKFLOW="${OPENVIBE_WORKFLOW:-windows-source-sdk-dlls.yml}"--BUILD_PS1="tools/build-sdk-windows.ps1"----say "fix Win32 build: build/copy bitmap.lib before client link"--say "root=$ROOT"--say "branch=$BRANCH"--say "repo=$REPO"----command -v python3 >/dev/null 2>&1 || fail "python3 is required"--command -v gh >/dev/null 2>&1 || fail "GitHub CLI gh is required"--[[ -f "$BUILD_PS1" ]] || fail "missing $BUILD_PS1"----python3 - <<'PY'--from pathlib import Path--p = Path('tools/build-sdk-windows.ps1')--s = p.read_text()--orig = s----# Add bitmap project to the dependency build list. Client links ..\..\lib\public\bitmap.lib.--if '"bitmap*.vcxproj"' not in s:-- anchor = ' "mathlib*.vcxproj",\n'-- if anchor not in s:-- raise SystemExit('could not find dependency patterns anchor near mathlib*.vcxproj')-- s = s.replace(anchor, anchor + ' "bitmap*.vcxproj",\n', 1)----# Ensure bitmap.lib is copied into the public lib dir after dependency builds.--if 'Copy-LibIfNeeded "bitmap.lib"' not in s:-- anchor = ' Copy-LibIfNeeded "mathlib.lib" $publicLibDir\n'-- if anchor not in s:-- raise SystemExit('could not find Copy-LibIfNeeded anchor near mathlib.lib')-- s = s.replace(anchor, anchor + ' Copy-LibIfNeeded "bitmap.lib" $publicLibDir\n', 1)----# Improve fallback search ordering for libs: prefer target arch output dirs over stale x64.--old = ''' $found = Get-ChildItem -Path $Sdk -Recurse -File -Filter $libName -ErrorAction SilentlyContinue |-- Where-Object { $_.FullName -notmatch "artifacts|windows-build-debug|_deps" } |-- Sort-Object LastWriteTime -Descending |-- Select-Object -First 1--'''--new = ''' $found = Get-ChildItem -Path $Sdk -Recurse -File -Filter $libName -ErrorAction SilentlyContinue |-- Where-Object { $_.FullName -notmatch "artifacts|windows-build-debug|_deps" } |-- Sort-Object @{ Expression = {-- if ($script:OpenVibeTargetArch -eq "x86") {-- if ($_.FullName -match "\\x64\\|/x64/|win64|x64") { 1 } else { 0 }-- } else {-- if ($_.FullName -match "\\x64\\|/x64/|win64|x64") { 0 } else { 1 }-- }-- } }, LastWriteTime -Descending |-- Select-Object -First 1--'''--if old in s and new not in s:-- s = s.replace(old, new, 1)----# Add an explicit diagnostic line so we can tell this patch ran.--marker = '# OPENVIBE_WIN32_BITMAP_LIB_DEP_PATCH'--if marker not in s:-- s = s.replace('function Build-SourceSdkDependencyProjects {\n', f'{marker}\nfunction Build-SourceSdkDependencyProjects {{\n', 1)----if s != orig:-- p.write_text(s)-- print('[patched] tools/build-sdk-windows.ps1')--else:-- print('[openvibe] tools/build-sdk-windows.ps1 already patched')--PY----mkdir -p docs--cat > docs/WINDOWS_WIN32_BITMAP_LIB_FIX.md <<'DOC'--# Windows Win32 bitmap.lib fix----The Win32/Proton DLL build converts VPC's generated win64 projects to Win32 before MSBuild.--After conversion, the HL2MP client links against `..\..\lib\public\bitmap.lib`.----This patch adds the generated bitmap project to the dependency build list and copies--`bitmap.lib` into the target public library directory before building `client.dll`.--DOC----say "git diff summary"--git diff --stat----git add tools/build-sdk-windows.ps1 docs/WINDOWS_WIN32_BITMAP_LIB_FIX.md tools/fix-win32-build-missing-bitmap-lib-and-install.sh 2>/dev/null || true--if ! git diff --cached --quiet; then-- git commit -m "Build bitmap lib for Win32 Proton DLLs"-- git push origin "$BRANCH"--else-- say "no commit needed"--fi----HEAD="$(git rev-parse HEAD)"--say "head=$HEAD"--say "triggering Win32 workflow"--gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$BRANCH" -f diagnostics=1 || true----RUN_ID=""--for _ in $(seq 1 60); do-- RUN_ID="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --commit "$HEAD" --limit 10 --json databaseId,createdAt,status --jq 'sort_by(.createdAt) | last | .databaseId' 2>/dev/null || true)"-- if [[ -n "$RUN_ID" && "$RUN_ID" != "null" ]]; then-- break-- fi-- sleep 5--done--[[ -n "$RUN_ID" && "$RUN_ID" != "null" ]] || fail "could not find workflow run for commit $HEAD"----say "watching run $RUN_ID"--if ! gh run watch "$RUN_ID" --repo "$REPO" --exit-status; then-- warn "workflow failed; downloading diagnostics"-- tools/windows-workflow-debug-and-install.sh "$RUN_ID" || true-- fail "Win32 DLL workflow failed"--fi----say "workflow passed; installing PE32 OpenVibe DLLs"--OPENVIBE_EXPECT_DLL_ARCH=x86 tools/install-latest-openvibe-windows-dlls.sh "$RUN_ID"----say "done. Now fully close hl2.exe/Proton, relaunch, and test:"--echo 'OPENVIBE_PROTON_LOG=1 OPENVIBE_CLIENT_MODE=proton tools/run-client-auto.sh 127.0.0.1 27015'--echo 'ov_help'--echo 'ov_join hub'--echo 'ov_menu'ddeleted file mode 100755@@@ -1,195 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--cd "$ROOT"----say() { printf '[openvibe] %s\n' "$*"; }----BRANCH="$(git rev-parse --abbrev-ref HEAD)"--REPO=""--if [[ -x tools/openvibe-gh-repo.sh ]]; then-- REPO="$(tools/openvibe-gh-repo.sh || true)"--fi--if [[ -z "$REPO" ]]; then-- REPO="$(git remote get-url origin | sed -E 's#^[email protected]:##; s#^https://github.com/##; s#\.git$##')"--else-- REPO="$(printf '%s' "$REPO" | sed -E 's#^[email protected]:##; s#^https://github.com/##; s#\.git$##')"--fi--WORKFLOW="windows-source-sdk-dlls.yml"--WORKFLOW_PATH=".github/workflows/$WORKFLOW"--BOOT="tools/bootstrap-source-sdk-2013-windows.ps1"----say "fix Windows bootstrap by using actions/checkout for Valve SDK"--say "root=$ROOT"--say "branch=$BRANCH"--say "repo=$REPO"----mkdir -p .github/workflows tools docs----# Keep downloaded diagnostics and bootstrapped SDK out of git from now on.--touch .gitignore--for line in 'artifacts/' '_deps/' 'engine/source-sdk-2013/'; do-- grep -qxF "$line" .gitignore || echo "$line" >> .gitignore--done----cat > "$BOOT" <<'PS1'--$ErrorActionPreference = 'Stop'----$Root = if ($env:OPENVIBE_ROOT) { (Resolve-Path $env:OPENVIBE_ROOT).Path } else { (Resolve-Path (Join-Path $PSScriptRoot '..')).Path }--$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root 'engine/source-sdk-2013' }--$Src = Join-Path $Sdk 'src'--$Deps = Join-Path $Root '_deps'--$Checkout = Join-Path $Deps 'source-sdk-2013-upstream'--$LogDir = Join-Path $Root 'artifacts/windows-build-debug'--New-Item -ItemType Directory -Force -Path $LogDir | Out-Null----function Say($m) { Write-Host "[openvibe-sdk-bootstrap] $m" }--function WriteTree($path, $name) {-- try {-- if (Test-Path $path) {-- "=== $path ===" | Out-File (Join-Path $LogDir $name)-- Get-ChildItem -Force $path | Select-Object Mode,Length,Name,FullName | Format-Table -AutoSize | Out-File (Join-Path $LogDir $name) -Append-- } else {-- "[missing] $path" | Out-File (Join-Path $LogDir $name)-- }-- } catch {-- "[tree failed] $path $($_.Exception.Message)" | Out-File (Join-Path $LogDir $name)-- }--}--function HasHl2mp($root) {-- $src = Join-Path $root 'src'-- return ((Test-Path (Join-Path $src 'game/client/hl2mp')) -and (Test-Path (Join-Path $src 'game/server/hl2mp')))--}--function CopyLayout($fromRoot, $label) {-- Say "using Valve SDK layout: $label -> $Sdk"-- if (Test-Path $Sdk) { Remove-Item -Recurse -Force $Sdk }-- New-Item -ItemType Directory -Force -Path $Sdk | Out-Null-- Get-ChildItem -Force $fromRoot | Where-Object { $_.Name -ne '.git' } | ForEach-Object {-- Copy-Item $_.FullName -Destination $Sdk -Recurse -Force-- }-- if (-not (HasHl2mp $Sdk)) {-- WriteTree $Sdk 'sdk-root-after-copy.txt'-- throw "Copied SDK layout is missing expected src/game/client/hl2mp and src/game/server/hl2mp folders"-- }-- Say "SDK bootstrapped successfully"--}----Say "root=$Root"--Say "sdk=$Sdk"--Say "deps=$Deps"--Say "checkout=$Checkout"--WriteTree $Root 'repo-root-before-bootstrap.txt'--WriteTree $Deps 'deps-before-bootstrap.txt'----if (HasHl2mp $Sdk) {-- Say "existing SDK tree looks usable"-- exit 0--}----# Preferred path: GitHub Actions checks out ValveSoftware/source-sdk-2013 into _deps/source-sdk-2013-upstream.--# This avoids flaky codeload/curl behavior and avoids git credential leakage from the OpenVibe checkout.--$candidates = @(-- @{ Root = $Checkout; Label = 'upstream root/src' },-- @{ Root = (Join-Path $Checkout 'mp'); Label = 'upstream mp/src' },-- @{ Root = (Join-Path $Checkout 'sp'); Label = 'upstream sp/src' }--)--foreach ($c in $candidates) {-- if ((Test-Path $c.Root) -and (HasHl2mp $c.Root)) {-- CopyLayout $c.Root $c.Label-- exit 0-- }--}----# Emergency fallback only: try a clean public clone with credentials explicitly disabled.--# The workflow should normally never reach this if the actions/checkout step worked.--Say "actions checkout layout was not found; trying emergency public git clone fallback"--$Fallback = Join-Path $Deps 'source-sdk-2013-git-fallback'--if (Test-Path $Fallback) { Remove-Item -Recurse -Force $Fallback }--New-Item -ItemType Directory -Force -Path $Deps | Out-Null--$cloneLog = Join-Path $LogDir 'bootstrap-git-clone-fallback.log'--& git -c http.https://github.com/.extraheader= clone --depth 1 https://github.com/ValveSoftware/source-sdk-2013.git $Fallback 2>&1 | Tee-Object -FilePath $cloneLog--if ($LASTEXITCODE -ne 0) {-- WriteTree $Deps 'deps-after-bootstrap-failure.txt'-- throw "Could not clone ValveSoftware/source-sdk-2013. Check $cloneLog"--}----foreach ($root in @($Fallback, (Join-Path $Fallback 'mp'), (Join-Path $Fallback 'sp'))) {-- if ((Test-Path $root) -and (HasHl2mp $root)) {-- CopyLayout $root "git fallback $root"-- exit 0-- }--}----WriteTree $Fallback 'fallback-root-after-clone.txt'--throw "ValveSoftware/source-sdk-2013 was fetched, but no usable HL2MP src layout was found."--PS1----python3 - <<'PY'--from pathlib import Path--p = Path('.github/workflows/windows-source-sdk-dlls.yml')--s = p.read_text()--insert = '''\n - name: Checkout Valve Source SDK 2013\n uses: actions/checkout@v4\n with:\n repository: ValveSoftware/source-sdk-2013\n path: _deps/source-sdk-2013-upstream\n fetch-depth: 1\n persist-credentials: false\n\n'''--if 'Checkout Valve Source SDK 2013' not in s:-- marker = ' - name: Bootstrap Source SDK 2013\n'-- if marker not in s:-- raise SystemExit('Could not find Bootstrap Source SDK 2013 step in workflow')-- s = s.replace(marker, insert + marker, 1)--# Make the tree/debug step aware of the second checkout path if it exists later in the job.--old = "if (Test-Path engine/source-sdk-2013/src) { Get-ChildItem -Force engine/source-sdk-2013/src | Select-Object Mode,Length,Name } else { Write-Host \"[miss] engine/source-sdk-2013/src\" }"--new = old + "\n if (Test-Path _deps/source-sdk-2013-upstream) { Get-ChildItem -Force _deps/source-sdk-2013-upstream | Select-Object Mode,Length,Name } else { Write-Host \"[miss] _deps/source-sdk-2013-upstream before checkout step\" }"--if old in s and '_deps/source-sdk-2013-upstream before checkout step' not in s:-- s = s.replace(old, new, 1)--p.write_text(s)--PY----cat > docs/WINDOWS_WORKFLOW_SDK_BOOTSTRAP.md <<'MD'--# Windows Source SDK bootstrap----The Windows DLL workflow uses a two-checkout model:----1. Checkout this OpenVibe repository.--2. Checkout `ValveSoftware/source-sdk-2013` into `_deps/source-sdk-2013-upstream`.--3. Normalize the Valve SDK layout into `engine/source-sdk-2013`.--4. Apply OpenVibe SDK patches.--5. Build `client.dll` and `server.dll` with MSBuild.----The bootstrap script accepts these upstream layouts:----- `_deps/source-sdk-2013-upstream/src/...`--- `_deps/source-sdk-2013-upstream/mp/src/...`--- `_deps/source-sdk-2013-upstream/sp/src/...`----Diagnostics are uploaded under the `openvibe-windows-build-debug` artifact.--MD----say "git diff summary"--git diff --stat -- .gitignore "$WORKFLOW_PATH" "$BOOT" docs/WINDOWS_WORKFLOW_SDK_BOOTSTRAP.md || true----git add .gitignore "$WORKFLOW_PATH" "$BOOT" docs/WINDOWS_WORKFLOW_SDK_BOOTSTRAP.md "$0" 2>/dev/null || \-- git add .gitignore "$WORKFLOW_PATH" "$BOOT" docs/WINDOWS_WORKFLOW_SDK_BOOTSTRAP.md tools/fix-windows-bootstrap-actions-checkout-and-rerun.sh----git commit -m "Use actions checkout for Windows Source SDK bootstrap" || say "nothing to commit"--git push----say "triggering workflow"--gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$BRANCH"--sleep 8--RUN_ID="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"--say "watching run $RUN_ID"--if gh run watch "$RUN_ID" --repo "$REPO" --exit-status; then-- say "workflow passed"--else-- say "workflow failed; downloading diagnostics anyway"--fi----if [[ -x tools/windows-workflow-debug-and-install.sh ]]; then-- tools/windows-workflow-debug-and-install.sh || true--fi----say "verify DLL content"--if [[ -x tools/verify-openvibe-dll-content.sh ]]; then-- tools/verify-openvibe-dll-content.sh || true--fi----say "done"ddeleted file mode 100755@@@ -1,248 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--cd "$ROOT"----BRANCH="$(git rev-parse --abbrev-ref HEAD)"--REPO="${OPENVIBE_REPO:-$(git remote get-url origin | sed -E 's#.*github.com[:/]([^/]+/[^/.]+)(\.git)?#\1#')}"--REPO="${REPO%.git}"--WORKFLOW="windows-source-sdk-dlls.yml"----echo "[openvibe] fix Windows bootstrap: use Valve master zip/download fallback"--echo "[openvibe] root=$ROOT"--echo "[openvibe] branch=$BRANCH"--echo "[openvibe] repo=$REPO"----mkdir -p tools docs .github/workflows----cat > tools/bootstrap-source-sdk-2013-windows.ps1 <<'PS'--$ErrorActionPreference = 'Stop'----$Root = if ($env:OPENVIBE_ROOT) { (Resolve-Path $env:OPENVIBE_ROOT).Path } else { (Resolve-Path (Join-Path $PSScriptRoot '..')).Path }--$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root 'engine/source-sdk-2013' }--$Src = Join-Path $Sdk 'src'--$Deps = Join-Path $Root '_deps'--$LogDir = Join-Path $Root 'artifacts/windows-build-debug'--New-Item -ItemType Directory -Force -Path $Deps, $LogDir | Out-Null----function Say($m) { Write-Host "[openvibe-sdk-bootstrap] $m" }--function Dump-Dir($path, $outName) {-- try {-- if (Test-Path $path) {-- Get-ChildItem -Force $path | Select-Object Mode,Length,Name | Format-Table -AutoSize | Out-File (Join-Path $LogDir $outName)-- } else {-- "[missing] $path" | Out-File (Join-Path $LogDir $outName)-- }-- } catch {-- "dump failed: $($_.Exception.Message)" | Out-File (Join-Path $LogDir $outName)-- }--}--function Assert-UsableSdk($prefix) {-- if ((Test-Path (Join-Path $Src 'game/client/hl2mp')) -and (Test-Path (Join-Path $Src 'game/server/hl2mp'))) {-- Say "$prefix SDK tree looks usable"-- return $true-- }-- return $false--}--function Copy-SdkRoot($sourceRoot) {-- Say "copying SDK root $sourceRoot -> $Sdk"-- if (Test-Path $Sdk) { Remove-Item -Recurse -Force $Sdk }-- New-Item -ItemType Directory -Force -Path $Sdk | Out-Null-- Copy-Item -Path (Join-Path $sourceRoot '*') -Destination $Sdk -Recurse -Force-- Dump-Dir $Sdk 'sdk-root-after-copy.txt'-- if (!(Assert-UsableSdk 'copied')) {-- throw "Copied SDK is missing expected HL2MP folders under $Src"-- }--}----Say "root=$Root"--Say "sdk=$Sdk"--Say "deps=$Deps"----if (Assert-UsableSdk 'existing') { exit 0 }----# ValveSoftware/source-sdk-2013 currently uses the default/master layout with src/ at the repository root.--# Do not assume an mp branch or mp/src folder. Downloading the zip avoids git/extraheader/auth weirdness on Actions runners.--$zip = Join-Path $Deps 'source-sdk-2013-master.zip'--$extractParent = Join-Path $Deps 'source-sdk-2013-zip'--$zipRoot = Join-Path $extractParent 'source-sdk-2013-master'--$zipUrl = 'https://codeload.github.com/ValveSoftware/source-sdk-2013/zip/refs/heads/master'----$zipOk = $false--try {-- Say "downloading Valve SDK master zip from $zipUrl"-- if (Test-Path $zip) { Remove-Item -Force $zip }-- if (Test-Path $extractParent) { Remove-Item -Recurse -Force $extractParent }-- New-Item -ItemType Directory -Force -Path $extractParent | Out-Null---- if (Get-Command curl.exe -ErrorAction SilentlyContinue) {-- & curl.exe -L --retry 4 --retry-delay 3 --connect-timeout 30 -o $zip $zipUrl 2>&1 | Tee-Object -FilePath (Join-Path $LogDir 'bootstrap-curl-master-zip.log')-- if ($LASTEXITCODE -ne 0) { throw "curl.exe failed with exit code $LASTEXITCODE" }-- } else {-- Invoke-WebRequest -Uri $zipUrl -OutFile $zip -UseBasicParsing 2>&1 | Tee-Object -FilePath (Join-Path $LogDir 'bootstrap-iwr-master-zip.log')-- }---- $zipInfo = Get-Item $zip-- Say "zip bytes=$($zipInfo.Length)"-- if ($zipInfo.Length -lt 1000000) { throw "downloaded zip is suspiciously small" }---- Expand-Archive -Path $zip -DestinationPath $extractParent -Force-- Dump-Dir $extractParent 'zip-extract-parent.txt'-- Dump-Dir $zipRoot 'zip-root.txt'---- if (Test-Path (Join-Path $zipRoot 'src/game/client/hl2mp')) {-- Copy-SdkRoot $zipRoot-- $zipOk = $true-- } else {-- throw "zip did not contain expected src/game/client/hl2mp at $zipRoot"-- }--} catch {-- Say "zip bootstrap failed: $($_.Exception.Message)"-- $_ | Out-String | Out-File (Join-Path $LogDir 'bootstrap-zip-exception.txt')--}----if ($zipOk) { Say 'SDK bootstrapped successfully from zip'; exit 0 }----# Last-resort git fallback. Clear possible GitHub Actions extraheaders and force public HTTPS.--$gitRepo = Join-Path $Deps 'source-sdk-2013-git'--try {-- Say "trying git clone fallback"-- git config --global --unset-all http.https://github.com/.extraheader 2>$null-- git config --global --unset-all http.https://github.com/ValveSoftware/source-sdk-2013.extraheader 2>$null-- if (Test-Path $gitRepo) { Remove-Item -Recurse -Force $gitRepo }-- & git -c http.https://github.com/.extraheader= clone --depth 1 --single-branch https://github.com/ValveSoftware/source-sdk-2013.git $gitRepo 2>&1 | Tee-Object -FilePath (Join-Path $LogDir 'bootstrap-git-clone-master.log')-- if ($LASTEXITCODE -ne 0) { throw "git clone failed with exit code $LASTEXITCODE" }-- Dump-Dir $gitRepo 'git-root.txt'-- if (Test-Path (Join-Path $gitRepo 'src/game/client/hl2mp')) {-- Copy-SdkRoot $gitRepo-- Say 'SDK bootstrapped successfully from git'-- exit 0-- }-- throw "git clone did not contain src/game/client/hl2mp"--} catch {-- Say "git bootstrap failed: $($_.Exception.Message)"-- $_ | Out-String | Out-File (Join-Path $LogDir 'bootstrap-git-exception.txt')--}----Dump-Dir $Deps 'deps-after-bootstrap-failure.txt'--throw "Could not bootstrap ValveSoftware/source-sdk-2013. Check openvibe-windows-build-debug artifact, especially bootstrap-curl-master-zip.log, bootstrap-zip-exception.txt, and bootstrap-git-clone-master.log."--PS----cat > docs/WINDOWS_WORKFLOW_SDK_BOOTSTRAP.md <<'MD'--# Windows Source SDK bootstrap----The Windows GitHub Actions runner is fresh and does not have Alex's local `engine/source-sdk-2013` checkout.----The workflow bootstraps Valve's public Source SDK 2013 repository into:----```text--engine/source-sdk-2013--```----The expected SDK layout is:----```text--engine/source-sdk-2013/src/game/client/hl2mp--engine/source-sdk-2013/src/game/server/hl2mp--```----The bootstrap intentionally downloads the default/master zip first:----```text--https://codeload.github.com/ValveSoftware/source-sdk-2013/zip/refs/heads/master--```----This avoids brittle branch assumptions and avoids GitHub Actions `git` auth/header issues. A git clone fallback is still present for debugging.--MD----# Make sure the workflow has the bootstrap step before build, without rewriting the whole file if it already does.--python3 - <<'PY'--from pathlib import Path--p = Path('.github/workflows/windows-source-sdk-dlls.yml')--s = p.read_text()--if 'Bootstrap Source SDK 2013 MP' not in s and 'Bootstrap Source SDK 2013' not in s:-- marker = ' - name: Build Windows Source SDK DLLs\n'-- insert = ''' - name: Bootstrap Source SDK 2013\n run: |\n $ErrorActionPreference = 'Stop'\n powershell -NoProfile -ExecutionPolicy Bypass -File tools/bootstrap-source-sdk-2013-windows.ps1 *>&1 | Tee-Object -FilePath artifacts/windows-build-debug/bootstrap-source-sdk-2013.log\n\n'''-- if marker not in s:-- raise SystemExit('could not find Build Windows Source SDK DLLs step marker')-- s = s.replace(marker, insert + marker, 1)--# Normalize the old step label if present.--s = s.replace('Bootstrap Source SDK 2013 MP', 'Bootstrap Source SDK 2013')--p.write_text(s)--PY----# Improve debug helper to print the new bootstrap files if the run fails.--python3 - <<'PY'--from pathlib import Path--p = Path('tools/windows-workflow-debug-and-install.sh')--if not p.exists():-- raise SystemExit(0)--s = p.read_text()--needle = 'grep -RInE "(error|failed|fatal|C[0-9]{4}|LNK[0-9]{4}|MSB[0-9]{4}|No patched OpenVibe DLLs)" "$OUT" 2>/dev/null | head -n 80 || true'--replacement = '''grep -RInE "(error|failed|fatal|C[0-9]{4}|LNK[0-9]{4}|MSB[0-9]{4}|No patched OpenVibe DLLs|Could not bootstrap|suspiciously small)" "$OUT" 2>/dev/null | head -n 120 || true----for f in \-- "$OUT"/openvibe-windows-build-debug/bootstrap-source-sdk-2013.log \-- "$OUT"/openvibe-windows-build-debug/bootstrap-curl-master-zip.log \-- "$OUT"/openvibe-windows-build-debug/bootstrap-zip-exception.txt \-- "$OUT"/openvibe-windows-build-debug/bootstrap-git-clone-master.log \-- "$OUT"/openvibe-windows-build-debug/bootstrap-git-exception.txt \-- "$OUT"/openvibe-windows-build-debug/build-sdk-windows.log; do-- if [[ -f "$f" ]]; then-- echo-- echo "[openvibe] tail: ${f#$ROOT/}"-- tail -n 80 "$f" || true-- fi--done'''--if needle in s:-- s = s.replace(needle, replacement)--elif 'bootstrap-curl-master-zip.log' not in s:-- s += '\n\n# Extra bootstrap tails for current Windows workflow debugging.\n'--p.write_text(s)--PY----echo "[openvibe] git diff summary"--git diff --stat----if ! git diff --quiet; then-- git add tools/bootstrap-source-sdk-2013-windows.ps1 docs/WINDOWS_WORKFLOW_SDK_BOOTSTRAP.md .github/workflows/windows-source-sdk-dlls.yml tools/windows-workflow-debug-and-install.sh-- git commit -m "Fix Windows SDK bootstrap download path"-- git push origin "$BRANCH"--else-- echo "[openvibe] no changes to commit"--fi----if ! command -v gh >/dev/null 2>&1; then-- echo "[openvibe] gh missing; install/authenticate GitHub CLI, then run tools/windows-workflow-debug-and-install.sh after triggering workflow."-- exit 0--fi----echo "[openvibe] triggering workflow"--gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$BRANCH" || true--sleep 8--RUN_ID="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"--echo "[openvibe] watching run $RUN_ID"--if gh run watch "$RUN_ID" --repo "$REPO" --exit-status; then-- echo "[openvibe] workflow passed"--else-- echo "[openvibe warn] workflow failed; downloading diagnostics"--fi----tools/windows-workflow-debug-and-install.sh || true----echo--if [[ -x tools/verify-openvibe-dll-content.sh ]]; then-- tools/verify-openvibe-dll-content.sh || true--fi----echo--cat <<'EOF'--[openvibe] next if patched DLLs are installed:-- OPENVIBE_PROTON_LOG=1 OPENVIBE_CLIENT_MODE=proton tools/run-client-auto.sh 127.0.0.1 27015----In game console:-- ov_join hub-- ov_menu-- ov_menu_servers--EOFddeleted file mode 100755@@@ -1,206 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--cd "$ROOT"----BRANCH="$(git rev-parse --abbrev-ref HEAD)"--REPO="${OPENVIBE_GITHUB_REPO:-$(gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null || true)}"--REPO="${REPO:-OpenVibers/OpenVibe.Games}"--REPO="${REPO%.git}"--WORKFLOW="windows-source-sdk-dlls.yml"----say(){ printf '[openvibe] %s\n' "$*"; }----say "fix Windows bootstrap to clone the real Source SDK 2013 MP branch"--say "root=$ROOT"--say "branch=$BRANCH"--say "repo=$REPO"----if ! command -v gh >/dev/null 2>&1; then-- echo "Missing gh. Run: sudo apt install gh && gh auth login" >&2-- exit 1--fi----if ! gh auth status >/dev/null 2>&1; then-- echo "gh is not authenticated. Run: gh auth login" >&2-- exit 1--fi----mkdir -p tools docs .github/workflows----cat > tools/bootstrap-source-sdk-2013-windows.ps1 <<'PS1'--$ErrorActionPreference = 'Stop'----$Root = if ($env:OPENVIBE_ROOT) { (Resolve-Path $env:OPENVIBE_ROOT).Path } else { (Resolve-Path (Join-Path $PSScriptRoot '..')).Path }--$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root 'engine/source-sdk-2013' }--$Src = Join-Path $Sdk 'src'--$Deps = Join-Path $Root '_deps'--$LogDir = Join-Path $Root 'artifacts/windows-build-debug'--New-Item -ItemType Directory -Force -Path $LogDir | Out-Null----function Say($m) { Write-Host "[openvibe-sdk-bootstrap] $m" }--function Has-HL2MP($base) {-- return ((Test-Path (Join-Path $base 'src/game/client/hl2mp')) -and (Test-Path (Join-Path $base 'src/game/server/hl2mp')))--}--function Run-Git($args, $logName) {-- $log = Join-Path $LogDir $logName-- Say "git $args"-- & git @args 2>&1 | Tee-Object -FilePath $log-- if ($LASTEXITCODE -ne 0) { throw "git $args failed with exit code $LASTEXITCODE. See $log" }--}----Say "root=$Root"--Say "sdk=$Sdk"--Say "logdir=$LogDir"----if (Has-HL2MP $Sdk) {-- Say "existing SDK tree already contains HL2MP client/server"-- exit 0--}----New-Item -ItemType Directory -Force -Path $Deps | Out-Null----# Valve's source-sdk-2013 repository is normally organized by branches: mp and sp.--# Some forks/mirrors use root folders like mp/src. Support both layouts.--$RepoUrl = if ($env:OPENVIBE_SOURCE_SDK_REPO) { $env:OPENVIBE_SOURCE_SDK_REPO } else { 'https://github.com/ValveSoftware/source-sdk-2013.git' }--$ValveRepo = Join-Path $Deps 'source-sdk-2013-mp'----if (Test-Path $ValveRepo) { Remove-Item -Recurse -Force $ValveRepo }----$cloned = $false--try {-- Say "cloning Valve Source SDK 2013 branch mp"-- Run-Git @('clone','--depth','1','--branch','mp',$RepoUrl,$ValveRepo) 'bootstrap-git-clone-mp.log'-- $cloned = $true--} catch {-- Say "mp branch clone failed: $($_.Exception.Message)"-- if (Test-Path $ValveRepo) { Remove-Item -Recurse -Force $ValveRepo }-- Say "falling back to default branch clone"-- Run-Git @('clone','--depth','1',$RepoUrl,$ValveRepo) 'bootstrap-git-clone-default.log'-- $cloned = $true--}----if (-not $cloned) { throw "could not clone Source SDK repo" }----"=== Valve repo root ===" | Out-File (Join-Path $LogDir 'valve-repo-root.txt')--Get-ChildItem -Force $ValveRepo | Select-Object Mode,Length,Name | Format-Table -AutoSize | Out-File (Join-Path $LogDir 'valve-repo-root.txt') -Append--"=== candidate src folders ===" | Out-File (Join-Path $LogDir 'valve-candidate-src.txt')--Get-ChildItem -Path $ValveRepo -Recurse -Directory -Filter src -ErrorAction SilentlyContinue |-- Select-Object FullName | Format-List | Out-File (Join-Path $LogDir 'valve-candidate-src.txt') -Append----$candidates = @(-- $ValveRepo,-- (Join-Path $ValveRepo 'mp'),-- (Join-Path $ValveRepo 'srcsdk/mp'),-- (Join-Path $ValveRepo 'source-sdk-2013/mp')--)----$SourceRoot = $null--foreach ($candidate in $candidates) {-- if (Has-HL2MP $candidate) {-- $SourceRoot = $candidate-- break-- }--}----if (-not $SourceRoot) {-- $matches = Get-ChildItem -Path $ValveRepo -Recurse -Directory -Filter hl2mp -ErrorAction SilentlyContinue |-- Where-Object { $_.FullName -match '[\\/]src[\\/]game[\\/](client|server)[\\/]hl2mp$' }-- $matches | Select-Object FullName | Format-List | Out-File (Join-Path $LogDir 'hl2mp-folder-search.txt')-- throw "Could not find Source SDK 2013 MP HL2MP layout after clone. Check valve-repo-root.txt, valve-candidate-src.txt, and hl2mp-folder-search.txt."--}----Say "source root=$SourceRoot"--Say "copying Source SDK MP layout into $Sdk"--if (Test-Path $Sdk) { Remove-Item -Recurse -Force $Sdk }--New-Item -ItemType Directory -Force -Path $Sdk | Out-Null--Copy-Item -Path (Join-Path $SourceRoot '*') -Destination $Sdk -Recurse -Force----if (!(Has-HL2MP $Sdk)) {-- Get-ChildItem -Force $Sdk | Select-Object Mode,Length,Name | Format-Table -AutoSize | Out-File (Join-Path $LogDir 'sdk-root-after-copy.txt')-- throw "Bootstrapped SDK is missing expected HL2MP folders after copy"--}----"=== SDK root after copy ===" | Out-File (Join-Path $LogDir 'sdk-root-after-copy.txt')--Get-ChildItem -Force $Sdk | Select-Object Mode,Length,Name | Format-Table -AutoSize | Out-File (Join-Path $LogDir 'sdk-root-after-copy.txt') -Append--"=== SDK src root after copy ===" | Out-File (Join-Path $LogDir 'sdk-src-root-after-copy.txt')--Get-ChildItem -Force (Join-Path $Sdk 'src') | Select-Object Mode,Length,Name | Format-Table -AutoSize | Out-File (Join-Path $LogDir 'sdk-src-root-after-copy.txt') -Append----Say "SDK bootstrapped successfully"--PS1----cat > docs/WINDOWS_WORKFLOW_SDK_BOOTSTRAP.md <<'MD'--# Windows Source SDK bootstrap----The GitHub Actions Windows runner does not have Alex's local `engine/source-sdk-2013` checkout. The workflow bootstraps it by cloning ValveSoftware/source-sdk-2013.----Important detail: the Source SDK 2013 multiplayer code is normally on the `mp` branch, not necessarily in a `mp/` directory on the default branch. The bootstrap script now supports both layouts:----- `source-sdk-2013` checked out directly on the `mp` branch, with `src/...` at repo root--- older/mirrored layout with `mp/src/...`----The workflow should not upload or install stock/stale DLLs. A DLL artifact is considered useful only when `client.dll` contains OpenVibe command strings such as `ov_join`, `ov_menu`, or `OpenVibe`.--MD----# Make workflow rerun on bootstrap changes and make bootstrap log more obvious.--python3 - <<'PY'--from pathlib import Path--p = Path('.github/workflows/windows-source-sdk-dlls.yml')--s = p.read_text()--if 'tools/bootstrap-source-sdk-2013-windows.ps1' not in s:-- s = s.replace(' - "tools/build-sdk-windows.ps1"\n', ' - "tools/build-sdk-windows.ps1"\n - "tools/bootstrap-source-sdk-2013-windows.ps1"\n')--# Ensure bootstrap step captures a specific log and prints it on failure before exiting.--old = ''' - name: Bootstrap Source SDK 2013 MP\n run: |\n powershell -NoProfile -ExecutionPolicy Bypass -File tools/bootstrap-source-sdk-2013-windows.ps1 *>&1 | Tee-Object -FilePath artifacts/windows-build-debug/bootstrap-source-sdk-2013.log\n'''--new = ''' - name: Bootstrap Source SDK 2013 MP\n run: |\n $ErrorActionPreference = 'Continue'\n New-Item -ItemType Directory -Force -Path artifacts/windows-build-debug | Out-Null\n powershell -NoProfile -ExecutionPolicy Bypass -File tools/bootstrap-source-sdk-2013-windows.ps1 *>&1 | Tee-Object -FilePath artifacts/windows-build-debug/bootstrap-source-sdk-2013.log\n $code = $LASTEXITCODE\n if ($code -ne 0) {\n Write-Host \"[openvibe] bootstrap failed; tail follows\"\n Get-Content artifacts/windows-build-debug/bootstrap-source-sdk-2013.log -Tail 120\n exit $code\n }\n'''--if old in s:-- s = s.replace(old, new)--p.write_text(s)--PY----# Improve debug downloader so it actually prints the useful logs next time.--if [[ -f tools/windows-workflow-debug-and-install.sh ]]; then--python3 - <<'PY'--from pathlib import Path--p = Path('tools/windows-workflow-debug-and-install.sh')--s = p.read_text()--needle = 'say "artifact files:"\nfind "$OUT" -type f | sort || true\n'--insert = '''say "artifact files:"\nfind "$OUT" -type f | sort || true\n\nfor f in \\\n "$OUT"/openvibe-windows-build-debug/bootstrap-source-sdk-2013.log \\\n "$OUT"/openvibe-windows-build-debug/build-sdk-windows.log \\\n "$OUT"/openvibe-windows-build-debug/bootstrap-git-clone-mp.log \\\n "$OUT"/openvibe-windows-build-debug/valve-repo-root.txt \\\n "$OUT"/openvibe-windows-build-debug/valve-candidate-src.txt \\\n "$OUT"/openvibe-windows-build-debug/hl2mp-folder-search.txt; do\n if [[ -f "$f" ]]; then\n echo\n say "tail: ${f#$OUT/}"\n tail -n 120 "$f" || true\n fi\ndone\n'''--if needle in s and 'bootstrap-git-clone-mp.log' not in s:-- s = s.replace(needle, insert)--p.write_text(s)--PY--fi----say "git diff summary"--git diff --stat----git add .github/workflows/windows-source-sdk-dlls.yml tools/bootstrap-source-sdk-2013-windows.ps1 docs/WINDOWS_WORKFLOW_SDK_BOOTSTRAP.md tools/windows-workflow-debug-and-install.sh----if git diff --cached --quiet; then-- say "nothing to commit"--else-- git commit -m "Fix Windows Source SDK MP branch bootstrap"-- git push origin "$BRANCH"--fi----say "triggering workflow"--gh workflow run "$WORKFLOW" --ref "$BRANCH"--sleep 6--RUN_ID="$(gh run list --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId -q '.[0].databaseId')"--say "watching run $RUN_ID"--set +e--gh run watch "$RUN_ID" --exit-status--STATUS=$?--set -e----say "downloading diagnostics/artifacts"--tools/windows-workflow-debug-and-install.sh || true----if [[ $STATUS -eq 0 ]]; then-- say "workflow succeeded"--else-- say "workflow failed again; paste the tail printed above if it does not clearly show the next compile error"--fi----exit $STATUSddeleted file mode 100755@@@ -1,183 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$(pwd)}"--if [[ ! -d "$ROOT/.git" ]]; then-- ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--fi--cd "$ROOT"----say(){ printf '[openvibe] %s\n' "$*"; }----say "fix Windows build: build Source SDK deps + make python available"--say "root=$ROOT"--say "branch=$(git branch --show-current)"----python3 - <<'PY'--from pathlib import Path--import re----root = Path.cwd()--workflow = root / '.github/workflows/windows-source-sdk-dlls.yml'--build = root / 'tools/build-sdk-windows.ps1'----# -----------------------------------------------------------------------------# 1) Workflow: GitHub Windows runner has Python installed in some contexts, but--# Source SDK custom build steps call literal "python" from inside vcvars/msbuild.--# setup-python makes that deterministic.--# -----------------------------------------------------------------------------y = workflow.read_text()--if 'actions/setup-python@v5' not in y:-- needle = ''' - name: Setup MSBuild\n uses: microsoft/setup-msbuild@v2\n'''-- repl = needle + '''\n - name: Setup Python\n uses: actions/setup-python@v5\n with:\n python-version: "3.x"\n'''-- if needle not in y:-- raise SystemExit('Could not find Setup MSBuild block in workflow')-- y = y.replace(needle, repl)--workflow.write_text(y)----# -----------------------------------------------------------------------------# 2) Build script: install a python command/shim onto PATH after vcvars64.--# -----------------------------------------------------------------------------s = build.read_text()--helper = r'''--function Ensure-PythonOnPath {-- $pyCmd = Get-Command python.exe -ErrorAction SilentlyContinue-- if (-not $pyCmd) { $pyCmd = Get-Command python -ErrorAction SilentlyContinue }-- if ($pyCmd) {-- Say "python=$($pyCmd.Source)"-- try { & $pyCmd.Source --version 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "python-version.txt") | Out-Null } catch {}-- return-- }---- $pyLauncher = Get-Command py.exe -ErrorAction SilentlyContinue-- if ($pyLauncher) {-- $shimDir = Join-Path $Root "_tools/python-shim"-- New-Item -ItemType Directory -Force -Path $shimDir | Out-Null-- $shim = Join-Path $shimDir "python.bat"-- "@echo off`r`npy -3 %*`r`n" | Set-Content -Encoding ascii $shim-- $env:PATH = "$shimDir;$env:PATH"-- Say "python shim=$shim"-- & python --version 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "python-version.txt") | Out-Null-- return-- }---- throw "python was not found on PATH and py.exe was not found. Add actions/setup-python before the build step."--}--'''--if 'function Ensure-PythonOnPath' not in s:-- marker = 'function Require($p, $msg) {\n if (!(Test-Path $p)) { throw "$msg`nMissing: $p" }\n}\n'-- if marker not in s:-- raise SystemExit('Could not find Require function marker')-- s = s.replace(marker, marker + helper + '\n')----call_marker = 'where.exe lib | Out-File (Join-Path $LogDir "where-lib.txt")\n'--if 'Ensure-PythonOnPath' in s and 'Ensure-PythonOnPath\n\n# Apply OpenVibe' not in s:-- if call_marker not in s:-- raise SystemExit('Could not find where-lib marker')-- s = s.replace(call_marker, call_marker + 'Ensure-PythonOnPath\n\n')----# -----------------------------------------------------------------------------# 3) Build dependency libs before client/server projects.--# Direct .vcxproj builds do not necessarily build VPC's implicit lib deps.--# Current failure was missing src/lib/public/x64/mathlib.lib.--# -----------------------------------------------------------------------------deps_block = r'''--function Find-ProjectByPattern([string]$pattern) {-- return @(Get-ChildItem -Path $Src -Recurse -File -Filter $pattern -ErrorAction SilentlyContinue |-- Sort-Object @{ Expression = { if ($_.FullName -match "\\(mathlib|tier1|raytrace|vgui2|fgdlib)\\") { 0 } else { 1 } } }, FullName)--}----function Copy-LibIfNeeded([string]$libName, [string]$destDir) {-- New-Item -ItemType Directory -Force -Path $destDir | Out-Null-- $dest = Join-Path $destDir $libName-- if (Test-Path $dest) { return }-- $found = Get-ChildItem -Path $Sdk -Recurse -File -Filter $libName -ErrorAction SilentlyContinue |-- Where-Object { $_.FullName -notmatch "artifacts|windows-build-debug|_deps" } |-- Sort-Object LastWriteTime -Descending |-- Select-Object -First 1-- if ($found) {-- Say "copying fallback lib $($found.FullName) -> $dest"-- Copy-Item $found.FullName $dest -Force-- }--}----function Build-SourceSdkDependencyProjects {-- Say "building Source SDK dependency libraries before HL2MP client/server"---- $patterns = @(-- "tier1*_win64.vcxproj",-- "mathlib*_win64.vcxproj",-- "raytrace*_win64.vcxproj",-- "vgui_controls*_win64.vcxproj",-- "matsys_controls*_win64.vcxproj",-- "fgdlib*_win64.vcxproj"-- )---- $projects = @()-- foreach ($pat in $patterns) {-- $projects += Find-ProjectByPattern $pat-- }-- $projects = @($projects | Sort-Object FullName -Unique)---- "=== dependency projects ===" | Out-File (Join-Path $LogDir "dependency-projects.txt")-- $projects | Select-Object FullName,Length,LastWriteTime | Format-List | Out-File (Join-Path $LogDir "dependency-projects.txt") -Append---- foreach ($dep in $projects) {-- $label = "dep-$($dep.BaseName -replace '[^A-Za-z0-9]+','_')"-- [void](Invoke-MSBuildProject $dep $label)-- }---- $publicX64 = Join-Path $Src "lib/public/x64"-- Copy-LibIfNeeded "mathlib.lib" $publicX64-- Copy-LibIfNeeded "tier1.lib" $publicX64-- Copy-LibIfNeeded "raytrace.lib" $publicX64-- Copy-LibIfNeeded "vgui_controls.lib" $publicX64-- Copy-LibIfNeeded "matsys_controls.lib" $publicX64---- "=== public x64 libs after deps ===" | Out-File (Join-Path $LogDir "public-x64-libs-after-deps.txt")-- if (Test-Path $publicX64) {-- Get-ChildItem -Path $publicX64 -File -ErrorAction SilentlyContinue |-- Select-Object FullName,Length,LastWriteTime |-- Format-List | Out-File (Join-Path $LogDir "public-x64-libs-after-deps.txt") -Append-- }--}--'''--if 'function Build-SourceSdkDependencyProjects' not in s:-- marker = 'function Invoke-MSBuildProject([System.IO.FileInfo]$proj, [string]$label) {'-- idx = s.find(marker)-- if idx == -1:-- raise SystemExit('Could not find Invoke-MSBuildProject function marker')-- # place deps block after Invoke-MSBuildProject function by locating the next Generate projects comment-- gen_marker = '# Generate projects if no relevant vcxproj files exist yet.'-- gen_idx = s.find(gen_marker)-- if gen_idx == -1:-- raise SystemExit('Could not find Generate projects marker')-- s = s[:gen_idx] + deps_block + '\n' + s[gen_idx:]----build_call_marker = '$beforeBuild = Get-Date\n\nif (-not (Invoke-MSBuildProject $clientProject "client")) {'--if 'Build-SourceSdkDependencyProjects\n\n$beforeBuild = Get-Date' not in s:-- if build_call_marker not in s:-- raise SystemExit('Could not find beforeBuild/client build marker')-- s = s.replace(build_call_marker, 'Build-SourceSdkDependencyProjects\n\n$beforeBuild = Get-Date\n\nif (-not (Invoke-MSBuildProject $clientProject "client")) {')----build.write_text(s)--PY----say "git diff summary"--git diff --stat----git add .github/workflows/windows-source-sdk-dlls.yml tools/build-sdk-windows.ps1--if git diff --cached --quiet; then-- say "nothing to commit"--else-- git commit -m "Build Windows Source SDK dependencies before HL2MP DLLs"--fi----git push----say "triggering clean Windows DLL build"--if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then-- tools/trigger-windows-dll-build-clean.sh--else-- tools/gh-windows-build-and-install.sh--fiddeleted file mode 100755@@@ -1,168 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"--cd "$ROOT"--BRANCH="$(git rev-parse --abbrev-ref HEAD)"--WORKFLOW="windows-source-sdk-dlls.yml"----if [[ -x tools/openvibe-gh-repo.sh ]]; then-- # shellcheck disable=SC1091-- REPO="$(tools/openvibe-gh-repo.sh)"--else-- REPO="$(git remote get-url origin | sed -E 's#^[email protected]:##; s#^https://github.com/##; s#\.git$##')"--fi--REPO="${REPO%.git}"----echo "[openvibe] fix Windows MSBuild Python shim + rerun"--echo "[openvibe] root=$ROOT"--echo "[openvibe] branch=$BRANCH"--echo "[openvibe] repo=$REPO"----# Keep setup-python in the workflow. This is still useful, but vcvars/msbuild can lose PATH,--# so the PowerShell build script will also create its own python.bat shim.--python3 - <<'PY'--from pathlib import Path--p = Path('.github/workflows/windows-source-sdk-dlls.yml')--if p.exists():-- s = p.read_text()-- if 'actions/setup-python@v5' not in s:-- marker = ' - name: Setup MSBuild\n'-- block = """ - name: Setup Python\n uses: actions/setup-python@v5\n with:\n python-version: '3.x'\n\n"""-- if marker in s:-- s = s.replace(marker, block + marker, 1)-- else:-- s += "\n" + block-- p.write_text(s)--PY----python3 - <<'PY'--from pathlib import Path--p = Path('tools/build-sdk-windows.ps1')--s = p.read_text()--marker = 'OPENVIBE_PYTHON_SHIM_FOR_MSBUILD'--func = r'''--# OPENVIBE_PYTHON_SHIM_FOR_MSBUILD--function Ensure-PythonCommandForMSBuild {-- $shimDir = Join-Path $Root "artifacts/python-shim"-- New-Item -ItemType Directory -Force -Path $shimDir | Out-Null-- $log = Join-Path $LogDir "python-version.txt"-- "=== Ensure-PythonCommandForMSBuild ===" | Out-File $log-- "initial PATH=$env:PATH" | Out-File $log -Append-- "pythonLocation=$env:pythonLocation" | Out-File $log -Append-- "Python_ROOT_DIR=$env:Python_ROOT_DIR" | Out-File $log -Append---- $candidates = New-Object System.Collections.Generic.List[string]-- foreach ($base in @($env:pythonLocation, $env:Python_ROOT_DIR, $env:Python3_ROOT_DIR)) {-- if ($base) {-- [void]$candidates.Add((Join-Path $base "python.exe"))-- [void]$candidates.Add((Join-Path $base "python3.exe"))-- }-- }---- foreach ($name in @("python.exe", "python3.exe", "py.exe")) {-- $cmd = Get-Command $name -ErrorAction SilentlyContinue-- if ($cmd -and $cmd.Source) { [void]$candidates.Add($cmd.Source) }-- }---- foreach ($rootCandidate in @("C:\hostedtoolcache\windows\Python", "C:\hostedtoolcache\windows\PyPy")) {-- if (Test-Path $rootCandidate) {-- Get-ChildItem -Path $rootCandidate -Recurse -File -Filter "python.exe" -ErrorAction SilentlyContinue |-- Sort-Object LastWriteTime -Descending |-- ForEach-Object { [void]$candidates.Add($_.FullName) }-- }-- }---- $py = $null-- foreach ($cand in ($candidates | Select-Object -Unique)) {-- if ($cand -and (Test-Path $cand)) {-- $py = (Resolve-Path $cand).Path-- break-- }-- }---- if (-not $py) {-- "candidate list:" | Out-File $log -Append-- $candidates | Out-File $log -Append-- throw "Python executable not found after setup-python/vcvars. Check python-version.txt in the debug artifact."-- }---- $pyDir = Split-Path -Parent $py-- $bat = Join-Path $shimDir "python.bat"-- $cmdFile = Join-Path $shimDir "python.cmd"-- $batContent = @"--@echo off--"$py" %*--exit /b %ERRORLEVEL%--"@-- Set-Content -Encoding ascii -Path $bat -Value $batContent-- Set-Content -Encoding ascii -Path $cmdFile -Value $batContent---- # Put the shim first so custom build steps that literally run `python` find it.-- $env:PATH = "$shimDir;$pyDir;$env:PATH"-- "selected python=$py" | Out-File $log -Append-- "shimDir=$shimDir" | Out-File $log -Append-- "updated PATH=$env:PATH" | Out-File $log -Append-- & $py --version 2>&1 | Tee-Object -FilePath $log -Append-- & cmd.exe /d /s /c "where python" 2>&1 | Tee-Object -FilePath $log -Append--}--'''----if marker not in s:-- # Insert after Require function, before Find-VcVars64.-- needle = 'function Find-VcVars64 {'-- if needle not in s:-- raise SystemExit('Could not find Find-VcVars64 insertion point in tools/build-sdk-windows.ps1')-- s = s.replace(needle, func + '\n' + needle, 1)----# Ensure it is called after tool where dumps in the x64 dev shell, before apply-openvibe-sdk/build.--call = 'Ensure-PythonCommandForMSBuild'--if call not in s.split('# Apply OpenVibe source files', 1)[0]:-- needle = 'where.exe lib | Out-File (Join-Path $LogDir "where-lib.txt")\n'-- if needle in s:-- s = s.replace(needle, needle + call + '\n', 1)-- else:-- needle2 = 'Say "cl.exe already available"\n'-- if needle2 not in s:-- raise SystemExit('Could not find tool setup insertion point in tools/build-sdk-windows.ps1')-- s = s.replace(needle2, needle2 + call + '\n', 1)----p.write_text(s)--PY----# Commit patch.--echo "[openvibe] git diff summary"--git diff --stat .github/workflows/windows-source-sdk-dlls.yml tools/build-sdk-windows.ps1 || true----git add .github/workflows/windows-source-sdk-dlls.yml tools/build-sdk-windows.ps1--if git diff --cached --quiet; then-- echo "[openvibe] no code changes to commit"--else-- git commit -m "Make python available to Windows MSBuild custom steps"--fi----echo "[openvibe] pushing $BRANCH"--git push origin "$BRANCH"----# Trigger and watch/download using the repo helper if available.--if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then-- echo "[openvibe] triggering clean Windows DLL build"-- tools/trigger-windows-dll-build-clean.sh--else-- echo "[openvibe] triggering workflow"-- gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$BRANCH"-- sleep 8-- 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 --repo "$REPO" "$RUN_ID" --exit-status || true-- if [[ -x tools/windows-workflow-debug-and-install.sh ]]; then-- tools/windows-workflow-debug-and-install.sh || true-- fi--fi----if [[ -x tools/verify-openvibe-dll-content.sh ]]; then-- echo "[openvibe] verify DLL content"-- tools/verify-openvibe-dll-content.sh || true--fi----echo "[openvibe] done"ddeleted file mode 100755@@@ -1,521 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--cd "$ROOT"--BRANCH="$(git rev-parse --abbrev-ref HEAD)"--REPO="${OPENVIBE_GITHUB_REPO:-OpenVibers/OpenVibe.Games}"--REPO="${REPO%.git}"--WORKFLOW="windows-source-sdk-dlls.yml"----echo "[openvibe] fix Windows SDK bootstrap + real DLL artifact validation"--echo "[openvibe] root=$ROOT"--echo "[openvibe] branch=$BRANCH"--echo "[openvibe] repo=$REPO"----mkdir -p tools .github/workflows docs----cat > tools/bootstrap-source-sdk-2013-windows.ps1 <<'PS1'--$ErrorActionPreference = 'Stop'----$Root = if ($env:OPENVIBE_ROOT) { (Resolve-Path $env:OPENVIBE_ROOT).Path } else { (Resolve-Path (Join-Path $PSScriptRoot '..')).Path }--$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root 'engine/source-sdk-2013' }--$Src = Join-Path $Sdk 'src'--$Deps = Join-Path $Root '_deps'--$ValveRepo = Join-Path $Deps 'source-sdk-2013'--$LogDir = Join-Path $Root 'artifacts/windows-build-debug'--New-Item -ItemType Directory -Force -Path $LogDir | Out-Null----function Say($m) { Write-Host "[openvibe-sdk-bootstrap] $m" }----Say "root=$Root"--Say "sdk=$Sdk"----if ((Test-Path (Join-Path $Src 'game/client/hl2mp')) -and (Test-Path (Join-Path $Src 'game/server/hl2mp'))) {-- Say "existing SDK tree looks usable"-- exit 0--}----New-Item -ItemType Directory -Force -Path $Deps | Out-Null----if (!(Test-Path (Join-Path $ValveRepo '.git'))) {-- Say "cloning ValveSoftware/source-sdk-2013"-- if (Test-Path $ValveRepo) { Remove-Item -Recurse -Force $ValveRepo }-- git clone --depth 1 https://github.com/ValveSoftware/source-sdk-2013.git $ValveRepo 2>&1 | Tee-Object -FilePath (Join-Path $LogDir 'bootstrap-git-clone.log')--} else {-- Say "updating cached Valve source-sdk-2013"-- Push-Location $ValveRepo-- git fetch --depth 1 origin 2>&1 | Tee-Object -FilePath (Join-Path $LogDir 'bootstrap-git-fetch.log')-- git reset --hard origin/master 2>&1 | Tee-Object -FilePath (Join-Path $LogDir 'bootstrap-git-reset.log')-- Pop-Location--}----$Mp = Join-Path $ValveRepo 'mp'--if (!(Test-Path (Join-Path $Mp 'src'))) {-- Get-ChildItem -Force $ValveRepo | Select-Object Mode,Length,Name | Format-Table -AutoSize | Out-File (Join-Path $LogDir 'valve-repo-root.txt')-- throw "Valve source-sdk-2013 clone does not contain mp/src at $Mp/src"--}----Say "copying mp branch layout into $Sdk"--if (Test-Path $Sdk) { Remove-Item -Recurse -Force $Sdk }--New-Item -ItemType Directory -Force -Path $Sdk | Out-Null--Copy-Item -Path (Join-Path $Mp '*') -Destination $Sdk -Recurse -Force----if (!(Test-Path (Join-Path $Src 'game/client/hl2mp')) -or !(Test-Path (Join-Path $Src 'game/server/hl2mp'))) {-- Get-ChildItem -Force $Sdk | Select-Object Mode,Length,Name | Format-Table -AutoSize | Out-File (Join-Path $LogDir 'sdk-root-after-copy.txt')-- throw "Bootstrapped SDK is missing expected HL2MP folders"--}----Say "SDK bootstrapped successfully"--PS1----cat > tools/build-sdk-windows.ps1 <<'PS1'--param(-- [switch]$InDevShell--)----$ErrorActionPreference = "Stop"----$Root = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path--$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root "engine/source-sdk-2013" }--$Src = Join-Path $Sdk "src"--$Mod = Join-Path $Root "game/openvibe.games"--$OutBin = Join-Path $Mod "bin"--$LogDir = Join-Path $Root "artifacts/windows-build-debug"--New-Item -ItemType Directory -Force -Path $LogDir | Out-Null----function Say($m) { Write-Host "[openvibe-win] $m" }--function Require($p, $msg) { if (!(Test-Path $p)) { throw "$msg`nMissing: $p" } }----function Enter-MsvcDevShellIfNeeded {-- if (Get-Command cl.exe -ErrorAction SilentlyContinue) {-- Say "cl.exe already available"-- return-- }-- if ($InDevShell) { throw "cl.exe still not found after entering MSVC dev shell" }---- $vswhere = Join-Path ${env:ProgramFiles(x86)} "Microsoft Visual Studio/Installer/vswhere.exe"-- if (!(Test-Path $vswhere)) { throw "vswhere.exe not found; Visual Studio Build Tools are not installed" }---- $install = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath-- if (!$install) { throw "Could not find Visual Studio installation with VC x86/x64 tools" }---- $vcvars = Join-Path $install "VC/Auxiliary/Build/vcvars32.bat"-- if (!(Test-Path $vcvars)) { throw "vcvars32.bat not found at $vcvars" }---- Say "relaunching through MSVC x86 dev shell: $vcvars"-- $self = $PSCommandPath-- $cmd = "call `"$vcvars`" && powershell -NoProfile -ExecutionPolicy Bypass -File `"$self`" -InDevShell"-- & cmd.exe /d /s /c $cmd-- exit $LASTEXITCODE--}----Say "root=$Root"--Say "sdk=$Sdk"--Say "src=$Src"----$env:OPENVIBE_ROOT = $Root--$env:OPENVIBE_SDK = $Sdk----if (!(Test-Path $Src)) {-- Say "SDK src missing; bootstrapping Source SDK 2013 MP"-- & powershell -NoProfile -ExecutionPolicy Bypass -File (Join-Path $Root "tools/bootstrap-source-sdk-2013-windows.ps1")-- if ($LASTEXITCODE -ne 0) { throw "bootstrap-source-sdk-2013-windows.ps1 failed with exit code $LASTEXITCODE" }--}----Require $Src "Source SDK checkout is missing from this runner."--Require (Join-Path $Src "game/client/hl2mp") "Source SDK HL2MP client tree missing."--Require (Join-Path $Src "game/server/hl2mp") "Source SDK HL2MP server tree missing."----Enter-MsvcDevShellIfNeeded----# Do not let old/stock repo DLLs get mistaken for a successful new build.--New-Item -ItemType Directory -Force -Path $OutBin | Out-Null--Remove-Item (Join-Path $OutBin "client.dll") -Force -ErrorAction SilentlyContinue--Remove-Item (Join-Path $OutBin "server.dll") -Force -ErrorAction SilentlyContinue--Remove-Item -Recurse -Force (Join-Path $Sdk "game/mod_hl2mp/bin") -ErrorAction SilentlyContinue----# Apply OpenVibe source files using Git Bash. Skip the Linux QuickJS build inside apply-openvibe-sdk.sh.--$bash = (Get-Command bash -ErrorAction SilentlyContinue)--if ($bash) {-- Say "applying OpenVibe SDK patch through bash"-- $env:OPENVIBE_SKIP_QJS_BUILD = "1"-- & bash "$Root/tools/apply-openvibe-sdk.sh" 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "apply-openvibe-sdk.log")-- if ($LASTEXITCODE -ne 0) { throw "apply-openvibe-sdk.sh failed with exit code $LASTEXITCODE" }--} else {-- throw "Git Bash is required on the Windows runner to apply the OpenVibe SDK patch."--}----Say "building QuickJS Windows static library"--& powershell -NoProfile -ExecutionPolicy Bypass -File (Join-Path $Root "tools/build-quickjs-lib-windows.ps1") 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "quickjs-windows.log")--if ($LASTEXITCODE -ne 0) { throw "build-quickjs-lib-windows.ps1 failed with exit code $LASTEXITCODE" }----Set-Location $Src----# Generate Visual Studio project files if needed.--$solutions = @(Get-ChildItem -Path $Src -Recurse -Filter "*.sln" -ErrorAction SilentlyContinue)--if ($solutions.Count -eq 0) {-- Say "no .sln files found; trying Source SDK project generators"-- $generators = @(-- (Join-Path $Src "creategameprojects.bat"),-- (Join-Path $Src "createallprojects.bat"),-- (Join-Path $Src "createprojects.bat")-- )-- $ran = $false-- foreach ($gen in $generators) {-- if (Test-Path $gen) {-- Say "running $gen"-- & cmd /d /s /c "`"$gen`"" 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "project-generation.log")-- $ran = $true-- break-- }-- }---- if (-not $ran) {-- $vpc = Join-Path $Src "devtools/bin/vpc.exe"-- if (Test-Path $vpc) {-- Say "running vpc.exe fallback"-- & $vpc /hl2mp +game /mksln OpenVibe_HL2MP.sln 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "vpc.log")-- $ran = $true-- }-- }---- $solutions = @(Get-ChildItem -Path $Src -Recurse -Filter "*.sln" -ErrorAction SilentlyContinue)-- if ($solutions.Count -eq 0) {-- Get-ChildItem -Path $Src -Force | Select-Object Mode,Length,Name | Format-Table -AutoSize | Out-File (Join-Path $LogDir "src-root-after-generator.txt")-- throw "No Visual Studio solution was generated. Check openvibe-windows-build-debug artifact."-- }--}----Say "solutions found:"--$solutions | ForEach-Object { Say " $($_.FullName)" }----$solution = $solutions |-- Where-Object { $_.Name -match "hl2mp|game|sdk|everything|OpenVibe" } |-- Select-Object -First 1--if (-not $solution) { $solution = $solutions | Select-Object -First 1 }--Say "selected solution=$($solution.FullName)"----$configs = @("Release", "Release_HL2MP", "Release HL2MP")--$platforms = @("Win32", "x86")--$targets = @("client_hl2mp", "server_hl2mp", "client", "server", "Build")----$builtAny = $false--foreach ($cfg in $configs) {-- foreach ($plat in $platforms) {-- foreach ($target in $targets) {-- Say "msbuild cfg=$cfg platform=$plat target=$target"-- $log = Join-Path $LogDir "msbuild-$($cfg -replace '[^A-Za-z0-9]','_')-$plat-$target.log"-- & msbuild $solution.FullName /m /p:Configuration=$cfg /p:Platform=$plat /p:PlatformToolset=v143 /t:$target /v:minimal /nologo 2>&1 | Tee-Object -FilePath $log-- if ($LASTEXITCODE -eq 0) { $builtAny = $true; break }-- }-- if ($builtAny) { break }-- }-- if ($builtAny) { break }--}----if (-not $builtAny) { throw "MSBuild could not build any known Source SDK HL2MP target. Check uploaded msbuild logs." }----New-Item -ItemType Directory -Force -Path $OutBin | Out-Null----$client = Get-ChildItem -Path $Sdk -Recurse -Filter client.dll -ErrorAction SilentlyContinue |-- Where-Object { $_.FullName -match "hl2mp|mod_hl2mp|client" } |-- Sort-Object LastWriteTime -Descending |-- Select-Object -First 1--$server = Get-ChildItem -Path $Sdk -Recurse -Filter server.dll -ErrorAction SilentlyContinue |-- Where-Object { $_.FullName -match "hl2mp|mod_hl2mp|server" } |-- Sort-Object LastWriteTime -Descending |-- Select-Object -First 1----if (-not $client) { throw "client.dll was not produced" }--if (-not $server) { throw "server.dll was not produced" }----Say "copy client=$($client.FullName)"--Say "copy server=$($server.FullName)"--Copy-Item $client.FullName (Join-Path $OutBin "client.dll") -Force--Copy-Item $server.FullName (Join-Path $OutBin "server.dll") -Force----$clientText = & strings.exe (Join-Path $OutBin "client.dll") 2>$null | Select-String -Pattern "ov_join|ov_menu|OpenVibe" -Quiet--if (-not $clientText) { throw "Built client.dll does not contain OpenVibe strings; refusing to publish stale/stock DLL" }----Say "done"--PS1----python3 - <<'PY'--from pathlib import Path--p = Path('tools/apply-openvibe-sdk.sh')--s = p.read_text()--old = '''copy_tree() {\n local src="$1"\n local dst="$2"\n mkdir -p "$dst"\n rsync -a --delete "$src/" "$dst/"\n echo "[openvibe-sdk] copied tree ${dst#$SDK/}"\n}\n'''--new = '''copy_tree() {\n local src="$1"\n local dst="$2"\n mkdir -p "$dst"\n if command -v rsync >/dev/null 2>&1; then\n rsync -a --delete "$src/" "$dst/"\n else\n rm -rf "$dst"\n mkdir -p "$dst"\n cp -R "$src"/. "$dst"/\n fi\n echo "[openvibe-sdk] copied tree ${dst#$SDK/}"\n}\n'''--if old not in s:-- raise SystemExit('Could not patch copy_tree() in tools/apply-openvibe-sdk.sh')--p.write_text(s.replace(old, new))--PY----cat > .github/workflows/windows-source-sdk-dlls.yml <<'YAML'--name: Build Windows Source DLLs----on:-- workflow_dispatch:-- inputs:-- diagnostics:-- description: "Upload verbose build diagnostics"-- required: false-- default: "1"-- push:-- branches:-- - codex/openvibe-next-steps-- paths:-- - ".github/workflows/windows-source-sdk-dlls.yml"-- - "tools/bootstrap-source-sdk-2013-windows.ps1"-- - "tools/build-sdk-windows.ps1"-- - "tools/build-quickjs-lib-windows.ps1"-- - "tools/apply-openvibe-sdk.sh"-- - "sdk/openvibe/**"-- - "game/openvibe.games/js/**"----permissions:-- contents: read-- actions: read----jobs:-- windows-dlls:-- name: Windows client.dll/server.dll-- runs-on: windows-2022-- defaults:-- run:-- shell: pwsh---- steps:-- - name: Checkout OpenVibe-- uses: actions/checkout@v4-- with:-- fetch-depth: 1---- - name: Configure git line endings-- run: |-- git config --global core.autocrlf false-- git config --global core.eol lf-- git status --short---- - name: Setup MSBuild-- uses: microsoft/setup-msbuild@v2---- - name: Show toolchain and tree-- run: |-- $ErrorActionPreference = 'Continue'-- New-Item -ItemType Directory -Force -Path artifacts/windows-build-debug | Out-Null-- Write-Host "pwd=$(Get-Location)"-- git rev-parse --short HEAD-- where.exe msbuild-- where.exe cl-- where.exe link-- where.exe bash-- where.exe git-- Get-ChildItem -Force | Select-Object Mode,Length,Name | Tee-Object -FilePath artifacts/windows-build-debug/repo-root-before-build.txt-- if (Test-Path engine) { Get-ChildItem -Force engine | Select-Object Mode,Length,Name | Tee-Object -FilePath artifacts/windows-build-debug/engine-before-build.txt } else { Write-Host "[miss] engine directory" }---- - name: Bootstrap Source SDK 2013 MP-- run: |-- $ErrorActionPreference = 'Stop'-- powershell -NoProfile -ExecutionPolicy Bypass -File tools/bootstrap-source-sdk-2013-windows.ps1 *>&1 | Tee-Object -FilePath artifacts/windows-build-debug/bootstrap-source-sdk-2013.log---- - name: Build Windows Source SDK DLLs-- id: build-- run: |-- $ErrorActionPreference = 'Stop'-- New-Item -ItemType Directory -Force -Path artifacts/windows-build-debug | Out-Null-- powershell -NoProfile -ExecutionPolicy Bypass -File tools/build-sdk-windows.ps1 *>&1 | Tee-Object -FilePath artifacts/windows-build-debug/build-sdk-windows.log---- - name: Stage build outputs and diagnostics-- if: always()-- run: |-- $ErrorActionPreference = 'Continue'-- New-Item -ItemType Directory -Force -Path artifacts/windows-dlls | Out-Null-- New-Item -ItemType Directory -Force -Path artifacts/windows-build-debug | Out-Null---- "=== repo ===" | Out-File artifacts/windows-build-debug/tree.txt-- Get-ChildItem -Force | Format-Table -AutoSize | Out-File artifacts/windows-build-debug/tree.txt -Append-- "=== dll search ===" | Out-File artifacts/windows-build-debug/dll-search.txt-- Get-ChildItem -Recurse -Force -Include client.dll,server.dll -ErrorAction SilentlyContinue |-- Select-Object FullName,Length,LastWriteTime |-- Format-List | Out-File artifacts/windows-build-debug/dll-search.txt -Append-- "=== project/log search ===" | Out-File artifacts/windows-build-debug/project-search.txt-- Get-ChildItem -Recurse -Force -Include *.sln,*.vcxproj,*.vcproj,*.log -ErrorAction SilentlyContinue |-- Select-Object FullName,Length,LastWriteTime |-- Format-List | Out-File artifacts/windows-build-debug/project-search.txt -Append---- $client = "game/openvibe.games/bin/client.dll"-- $server = "game/openvibe.games/bin/server.dll"-- $clientOk = $false-- if (Test-Path $client) {-- $text = strings.exe $client 2>$null | Select-String -Pattern "ov_join|ov_menu|OpenVibe" -Quiet-- if ($text) { $clientOk = $true }-- }-- if ($clientOk -and (Test-Path $server)) {-- Copy-Item $client artifacts/windows-dlls/client.dll -Force-- Copy-Item $server artifacts/windows-dlls/server.dll -Force-- Write-Host "[ok] staged patched OpenVibe Windows DLLs"-- } else {-- Write-Host "[miss] not staging DLLs because patched OpenVibe client.dll was not produced"-- "No patched OpenVibe DLLs staged. Check build logs." | Out-File artifacts/windows-build-debug/no-patched-dlls.txt-- }---- - name: Upload Windows DLL artifact-- if: always()-- uses: actions/upload-artifact@v4-- with:-- name: openvibe-windows-dlls-- path: artifacts/windows-dlls/**-- if-no-files-found: warn---- - name: Upload Windows build diagnostics-- if: always()-- uses: actions/upload-artifact@v4-- with:-- name: openvibe-windows-build-debug-- path: artifacts/windows-build-debug/**-- if-no-files-found: warn--YAML----cat > tools/windows-workflow-debug-and-install.sh <<'BASH2'--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--cd "$ROOT"--REPO="${OPENVIBE_GITHUB_REPO:-OpenVibers/OpenVibe.Games}"--WORKFLOW="${OPENVIBE_WINDOWS_WORKFLOW:-windows-source-sdk-dlls.yml}"--RUN_ID="${1:-}"----if ! command -v gh >/dev/null 2>&1; then-- echo "[openvibe] missing gh. Install with: sudo apt install gh" >&2-- exit 1--fi----if [[ -z "$RUN_ID" ]]; then-- RUN_ID="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$(git rev-parse --abbrev-ref HEAD)" --limit 1 --json databaseId --jq '.[0].databaseId')"--fi----OUT="$ROOT/artifacts/windows-workflow-debug/run-$RUN_ID-artifacts"--rm -rf "$OUT"--mkdir -p "$OUT"----echo "[openvibe] run=$RUN_ID"--echo "[openvibe] downloading logs/artifacts into $OUT"----gh run view "$RUN_ID" --repo "$REPO" --log > "$OUT/full-run.log" || true--gh run download "$RUN_ID" --repo "$REPO" --dir "$OUT" || true----find "$OUT" -maxdepth 3 -type f | sort > "$OUT/file-list.txt"----echo--echo "[openvibe] likely errors:"--grep -RniE "error MSB|fatal error|undefined reference|unresolved external|LNK[0-9]+|C[0-9]{4}:|throw |Missing:|not found|could not|failed|No Visual Studio solution|No patched" "$OUT" | tail -n 120 || true----echo--echo "[openvibe] artifact files:"--sed -n '1,160p' "$OUT/file-list.txt"----DLLDIR="$OUT/openvibe-windows-dlls"--if [[ -f "$DLLDIR/client.dll" && -f "$DLLDIR/server.dll" ]]; then-- if strings "$DLLDIR/client.dll" | grep -Eq 'ov_join|ov_menu|OpenVibe'; then-- mkdir -p game/openvibe.games/bin-- cp -f "$DLLDIR/client.dll" game/openvibe.games/bin/client.dll-- cp -f "$DLLDIR/server.dll" game/openvibe.games/bin/server.dll-- echo "[openvibe] installed patched Windows DLLs"-- tools/verify-openvibe-dll-content.sh || true-- else-- echo "[openvibe] refused to install DLL artifact because client.dll lacks OpenVibe strings" >&2-- exit 2-- fi--else-- echo "[openvibe] no DLL artifact available yet" >&2-- exit 3--fi--BASH2--chmod +x tools/windows-workflow-debug-and-install.sh----cat > docs/WINDOWS_WORKFLOW_SDK_BOOTSTRAP.md <<'MD'--# Windows DLL workflow bootstrap----The Windows GitHub Actions runner does not have the local `engine/source-sdk-2013` checkout that exists on the Linux workstation.----The workflow now bootstraps Valve's Source SDK 2013 multiplayer tree into:----```text--engine/source-sdk-2013--```----Then it applies the OpenVibe SDK patch, skips the Linux QuickJS static-library build, builds QuickJS with MSVC, generates Visual Studio projects, builds `client.dll/server.dll`, and only uploads DLLs when `client.dll` contains OpenVibe command strings such as `ov_join`, `ov_menu`, or `OpenVibe`.----Use:----```bash--tools/gh-windows-build-and-install.sh--```----or debug the latest run with:----```bash--tools/windows-workflow-debug-and-install.sh--```--MD----# Keep gh helper stricter if present: do not install stale DLLs.--if [[ -f tools/gh-windows-build-and-install.sh ]]; then--python3 - <<'PY'--from pathlib import Path--p = Path('tools/gh-windows-build-and-install.sh')--s = p.read_text()--if 'strings "$ARTDIR/openvibe-windows-dlls/client.dll"' not in s and 'refused to install DLL artifact' not in s:-- marker = 'if [[ -f "$ARTDIR/openvibe-windows-dlls/client.dll" && -f "$ARTDIR/openvibe-windows-dlls/server.dll" ]]; then'-- if marker in s:-- repl = marker + '''-- if ! strings "$ARTDIR/openvibe-windows-dlls/client.dll" | grep -Eq 'ov_join|ov_menu|OpenVibe'; then-- echo "[openvibe] refused to install DLL artifact because client.dll lacks OpenVibe strings" >&2-- tools/windows-workflow-debug-and-install.sh "$RUN_ID" || true-- exit 2-- fi'''-- s = s.replace(marker, repl)-- p.write_text(s)--PY--fi----# Commit/push and run workflow if gh is available.--echo--echo "[openvibe] git diff summary"--git diff --stat----git add \-- .github/workflows/windows-source-sdk-dlls.yml \-- tools/bootstrap-source-sdk-2013-windows.ps1 \-- tools/build-sdk-windows.ps1 \-- tools/apply-openvibe-sdk.sh \-- tools/windows-workflow-debug-and-install.sh \-- tools/gh-windows-build-and-install.sh \-- docs/WINDOWS_WORKFLOW_SDK_BOOTSTRAP.md 2>/dev/null || true----if ! git diff --cached --quiet; then-- git commit -m "Bootstrap Source SDK for Windows DLL workflow"-- git push origin "$BRANCH"--else-- echo "[openvibe] no changes to commit"--fi----if command -v gh >/dev/null 2>&1; then-- echo "[openvibe] triggering workflow"-- gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$BRANCH" -f diagnostics=1-- sleep 6-- 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" || true-- tools/windows-workflow-debug-and-install.sh "$RUN_ID" || true--else-- echo "[openvibe] gh missing; run tools/gh-windows-build-and-install.sh after installing/authenticating gh"--fi----echo--echo "[openvibe] next if DLLs installed:"--echo " OPENVIBE_PROTON_LOG=1 OPENVIBE_CLIENT_MODE=proton tools/run-client-auto.sh 127.0.0.1 27015"--echo " # in game console: ov_join hub ; ov_menu ; ov_menu_servers"ddeleted file mode 100755@@@ -1,146 +0,0 @@--#!/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="$(git remote get-url origin | sed -E 's#^[email protected]:##; s#^https://github.com/##; s#\.git$##')"----echo "[openvibe] fix Windows server vcxproj python custom build commands + rerun"--echo "[openvibe] root=$ROOT"--echo "[openvibe] branch=$BRANCH"--echo "[openvibe] repo=$REPO"----python3 - <<'PY'--from pathlib import Path--p = Path('tools/build-sdk-windows.ps1')--s = p.read_text()--orig = s----# 1) Ensure the robust MSBuild python shim is actually called. The previous patch--# defined Ensure-PythonCommandForMSBuild but only called Ensure-PythonOnPath.--needle = 'Ensure-PythonOnPath\n'--if needle in s and 'Ensure-PythonCommandForMSBuild\n' not in s.split(needle,1)[1][:120]:-- s = s.replace(needle, 'Ensure-PythonOnPath\nEnsure-PythonCommandForMSBuild\n', 1)----# 2) Make Ensure-PythonCommandForMSBuild publish the selected shim path for later project-file patching.--needle2 = '$env:PATH = "$shimDir;$pyDir;$env:PATH"\n'--if needle2 in s and '$script:OpenVibePythonCommand' not in s[s.find(needle2):s.find(needle2)+260]:-- s = s.replace(needle2, needle2 + ' $script:OpenVibePythonCommand = $bat\n', 1)----# 3) Add a generated vcxproj patcher. The Source SDK VPC emits custom build commands--# that literally start with `python ...`. On hosted Windows, MSBuild sometimes does--# not resolve batch shims through PATH for these custom steps. Rewriting generated--# project files to call the absolute shim is deterministic.--marker = 'function Patch-GeneratedPythonCustomBuildCommands'--func = r'''--function Patch-GeneratedPythonCustomBuildCommands {-- if (-not $script:OpenVibePythonCommand -or !(Test-Path $script:OpenVibePythonCommand)) {-- Ensure-PythonCommandForMSBuild-- }---- $py = $script:OpenVibePythonCommand-- Say "patching generated vcxproj python custom build commands to $py"-- $patchLog = Join-Path $LogDir "python-vcxproj-patches.txt"-- "python command=$py" | Out-File $patchLog---- $projects = @(Get-ChildItem -Path $Src -Recurse -File -Filter "*.vcxproj" -ErrorAction SilentlyContinue)-- foreach ($proj in $projects) {-- $text = Get-Content -Raw $proj.FullName-- $before = $text---- # XML command bodies can contain python at line start, after XML tags, or after &&.-- # Replace only executable tokens, preserving surrounding whitespace/tags.-- $text = [regex]::Replace($text, '(?im)(^|[>`r`n])([ `t]*)python(\.exe)?([ `t]+)', {-- param($m)-- return $m.Groups[1].Value + $m.Groups[2].Value + '"' + $py + '"' + $m.Groups[4].Value-- })-- $text = [regex]::Replace($text, '(?i)(&&[ `t]*)python(\.exe)?([ `t]+)', {-- param($m)-- return $m.Groups[1].Value + '"' + $py + '"' + $m.Groups[3].Value-- })-- $text = [regex]::Replace($text, '(?i)(&&[ `t]*)python(\.exe)?([ `t]+)', {-- param($m)-- return $m.Groups[1].Value + '"' + $py + '"' + $m.Groups[3].Value-- })---- if ($text -ne $before) {-- Set-Content -Encoding utf8 -Path $proj.FullName -Value $text-- "patched $($proj.FullName)" | Out-File $patchLog -Append-- }-- }--}--'''--if marker not in s:-- insert_after = 'function Invoke-MSBuildProject([System.IO.FileInfo]$proj, [string]$label) {'-- idx = s.find(insert_after)-- if idx == -1:-- raise SystemExit('Could not find Invoke-MSBuildProject insertion point')-- s = s[:idx] + func + '\n\n' + s[idx:]----# 4) Call patcher after project generation/project discovery but before building dependencies/client/server.--call = 'Patch-GeneratedPythonCustomBuildCommands\n\nBuild-SourceSdkDependencyProjects'--if call not in s:-- s = s.replace('Build-SourceSdkDependencyProjects\n\n$beforeBuild = Get-Date', call + '\n\n$beforeBuild = Get-Date', 1)----if s == orig:-- print('[openvibe] build-sdk-windows.ps1 already had python project patch')--else:-- p.write_text(s)-- print('[openvibe] patched tools/build-sdk-windows.ps1')--PY----cat > docs/WINDOWS_SERVER_PYTHON_CUSTOM_BUILD.md <<'EOF_DOC'--# Windows server.vcxproj Python custom build fix----Valve Source SDK's generated server project uses custom build commands that run--`python` while converting `.nut` script resources into generated headers. On--GitHub-hosted Windows runners, `actions/setup-python` may succeed while the VPC--emitted MSBuild custom step still cannot resolve the literal `python` command.----The Windows build script now:----1. creates a deterministic `python.bat`/`python.cmd` shim;--2. prepends it to PATH;--3. rewrites generated `.vcxproj` custom build command bodies so `python ...`-- uses the absolute shim path.----This keeps the generated Source SDK tree disposable while making CI builds--reliable.--EOF_DOC----echo "[openvibe] git diff summary"--git diff --stat----git add tools/build-sdk-windows.ps1 docs/WINDOWS_SERVER_PYTHON_CUSTOM_BUILD.md--if git diff --cached --quiet; then-- echo "[openvibe] no changes to commit"--else-- git commit -m "Patch Windows server Python custom build commands"--fi----echo "[openvibe] pushing $BRANCH"--git push origin "$BRANCH"----if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then-- echo "[openvibe] triggering clean Windows DLL build"-- tools/trigger-windows-dll-build-clean.sh || true--else-- echo "[openvibe] triggering workflow via gh"-- gh workflow run windows-source-sdk-dlls.yml --repo "$REPO" --ref "$BRANCH"-- sleep 5-- RUN_ID="$(gh run list --repo "$REPO" --workflow windows-source-sdk-dlls.yml --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"-- echo "[openvibe] watching run $RUN_ID"-- gh run watch "$RUN_ID" --repo "$REPO" --exit-status || true--fi----if [[ -x tools/windows-workflow-debug-and-install.sh ]]; then-- echo "[openvibe] downloading diagnostics/artifacts"-- tools/windows-workflow-debug-and-install.sh || true--fi----if [[ -x tools/verify-openvibe-dll-content.sh ]]; then-- tools/verify-openvibe-dll-content.sh || true--fi----echo "[openvibe] done"ddeleted file mode 100755@@@ -1,188 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----log(){ printf '[openvibe] %s\n' "$*"; }--warn(){ printf '[openvibe warn] %s\n' "$*" >&2; }----ROOT="${OPENVIBE_ROOT:-$(pwd)}"--cd "$ROOT"--BRANCH="$(git branch --show-current)"--REPO="$(git config --get remote.origin.url | sed -E 's#^[email protected]:##; s#^https://github.com/##; s#\.git$##')"--: "${REPO:=OpenVibers/OpenVibe.Games}"----log "fix Windows server build: QuickJS C++ header + vscript nut headers + real msbuild failures"--log "root=$ROOT"--log "branch=$BRANCH"--log "repo=$REPO"----python3 - <<'PY'--from pathlib import Path--p = Path('tools/build-sdk-windows.ps1')--text = p.read_text()----# 1) Make Invoke-MSBuildProject stop leaking Tee-Object output into the function return value.--old = '& msbuild $proj.FullName /m /p:Configuration="$($p.Configuration)" /p:Platform="$($p.Platform)" /t:Build /v:minimal /nologo 2>&1 | Tee-Object -FilePath $log'--new = '& msbuild $proj.FullName /m /p:Configuration="$($p.Configuration)" /p:Platform="$($p.Platform)" /t:Build /v:minimal /nologo 2>&1 | Tee-Object -FilePath $log | Out-Host'--if old in text:-- text = text.replace(old, new)----# 2) Ensure the stronger python shim is actually called and exposes paths for other helpers.--if 'Ensure-PythonOnPath\nEnsure-PythonCommandForMSBuild' not in text and 'Ensure-PythonOnPath\r\nEnsure-PythonCommandForMSBuild' not in text:-- text = text.replace('Ensure-PythonOnPath\n', 'Ensure-PythonOnPath\nEnsure-PythonCommandForMSBuild\n')----# Add script-scoped python vars inside Ensure-PythonCommandForMSBuild if not present.--if '$script:OpenVibePythonExe = $py' not in text:-- text = text.replace(' $pyDir = Split-Path -Parent $py\n', ' $pyDir = Split-Path -Parent $py\n $script:OpenVibePythonExe = $py\n')--if '$script:OpenVibePythonShim = $bat' not in text:-- text = text.replace(' Set-Content -Encoding ascii -Path $cmdFile -Value $batContent\n', ' Set-Content -Encoding ascii -Path $cmdFile -Value $batContent\n $script:OpenVibePythonShim = $bat\n')----helper = r'''----# OPENVIBE_FIX_QJS_CPP_AND_NUT_HEADERS--function Patch-QuickJsHeaderForMsvcCpp {-- $header = Join-Path $Sdk "src/game/shared/openvibe/third_party/quickjs/quickjs.h"-- if (!(Test-Path $header)) {-- Say "QuickJS header not present yet, skipping C++ compatibility patch: $header"-- return-- }---- $log = Join-Path $LogDir "quickjs-cpp-header-patch.txt"-- "header=$header" | Out-File $log-- $q = Get-Content -Raw $header-- $orig = $q---- $mkvalOld = '#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .uint64 = (uint32_t)(val) }, tag }'-- $mkvalNew = @'--#if defined(__cplusplus)--static inline JSValue JS_MKVAL_CPP(int tag, uint32_t val) { JSValue v; v.u.uint64 = (uint32_t)val; v.tag = tag; return v; }--#define JS_MKVAL(tag, val) JS_MKVAL_CPP((tag), (uint32_t)(val))--#else--#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .uint64 = (uint32_t)(val) }, tag }--#endif--'@-- $q = $q.Replace($mkvalOld, $mkvalNew)---- $mkptrOld = '#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag }'-- $mkptrNew = @'--#if defined(__cplusplus)--static inline JSValue JS_MKPTR_CPP(int tag, void *p) { JSValue v; v.u.ptr = p; v.tag = tag; return v; }--#define JS_MKPTR(tag, p) JS_MKPTR_CPP((tag), (void *)(p))--#else--#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag }--#endif--'@-- $q = $q.Replace($mkptrOld, $mkptrNew)---- $nanOld = '#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 }'-- $nanNew = @'--#if defined(__cplusplus)--static inline JSValue JS_NAN_CPP(void) { JSValue v; v.u.float64 = JS_FLOAT64_NAN; v.tag = JS_TAG_FLOAT64; return v; }--#define JS_NAN JS_NAN_CPP()--#else--#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 }--#endif--'@-- $q = $q.Replace($nanOld, $nanNew)---- $q = $q.Replace(' JSCFunctionType ft = { .generic_magic = func };', ' JSCFunctionType ft; memset(&ft, 0, sizeof(ft)); ft.generic_magic = func;')---- if ($q -ne $orig) {-- Set-Content -Encoding ascii -Path $header -Value $q-- "patched=1" | Out-File $log -Append-- Say "patched QuickJS header for MSVC C++ compound literal/designated initializer compatibility"-- } else {-- "patched=0" | Out-File $log -Append-- Say "QuickJS header C++ patch made no changes"-- }--}----function Ensure-ServerNutHeaders {-- $serverDir = Join-Path $Src "game/server"-- $textToArray = Join-Path $Src "devtools/bin/texttoarray.py"-- $log = Join-Path $LogDir "server-nut-headers.txt"-- "serverDir=$serverDir" | Out-File $log-- "textToArray=$textToArray" | Out-File $log -Append-- "pythonExe=$script:OpenVibePythonExe" | Out-File $log -Append-- "pythonShim=$script:OpenVibePythonShim" | Out-File $log -Append---- if (!(Test-Path $textToArray)) {-- throw "Missing Source SDK texttoarray.py at $textToArray"-- }-- if (-not $script:OpenVibePythonExe -or !(Test-Path $script:OpenVibePythonExe)) {-- throw "Python exe was not captured for nut header generation. Check python-version.txt."-- }---- foreach ($name in @("spawn_helper", "vscript_server")) {-- $input = Join-Path $serverDir "$name.nut"-- $out = Join-Path $serverDir "${name}_nut.h"-- if (!(Test-Path $input)) {-- "missing input $input" | Out-File $log -Append-- continue-- }-- Say "generating $out from $input"-- & $script:OpenVibePythonExe $textToArray $input "g_Script_$name" | Set-Content -Encoding ascii -Path $out-- if ($LASTEXITCODE -ne 0) { throw "texttoarray.py failed for $input" }-- if (!(Test-Path $out)) { throw "Expected generated nut header was not created: $out" }-- "generated $out" | Out-File $log -Append-- }--}--'''----if 'function Patch-QuickJsHeaderForMsvcCpp' not in text:-- marker = 'function Find-VcVars64 {'-- text = text.replace(marker, helper + '\n' + marker)----# Call QuickJS C++ header patch right after QuickJS lib build and before Set-Location.--if 'Patch-QuickJsHeaderForMsvcCpp\n\nSet-Location $Src' not in text:-- text = text.replace('\nSet-Location $Src\n', '\nPatch-QuickJsHeaderForMsvcCpp\n\nSet-Location $Src\n')----# Generate vscript headers after project generation/patches and before dependency/client/server builds.--if 'Ensure-ServerNutHeaders\nBuild-SourceSdkDependencyProjects' not in text:-- text = text.replace('Build-SourceSdkDependencyProjects\n', 'Ensure-ServerNutHeaders\nBuild-SourceSdkDependencyProjects\n')----p.write_text(text)--PY----cat > docs/WINDOWS_SERVER_QJS_CPP_NUT_HEADERS.md <<'EOF'--# Windows server build: QuickJS C++ and vscript nut headers----The Windows HL2MP server build needs two extra fixes on hosted Actions runners:----- The Source SDK custom build step for `vscript_server.nut` can report success without producing `vscript_server_nut.h`, so the build script pre-generates `spawn_helper_nut.h` and `vscript_server_nut.h` with `devtools/bin/texttoarray.py`.--- QuickJS is compiled as C with `clang-cl`, but its header is also included from C++ server files. The script patches the SDK copy of `quickjs.h` so MSVC C++ can compile QuickJS value macros without C compound literals/designated initializers.----The patch is applied only to the generated SDK copy under `engine/source-sdk-2013`, not to the vendored source files.--EOF----log "git diff summary"--git diff --stat----git add tools/build-sdk-windows.ps1 docs/WINDOWS_SERVER_QJS_CPP_NUT_HEADERS.md--if git diff --cached --quiet; then-- log "nothing new to commit"--else-- git commit -m "Fix Windows server QuickJS C++ and nut headers"--fi----log "pushing $BRANCH"--git push origin "$BRANCH"----if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then-- log "triggering clean Windows DLL build"-- tools/trigger-windows-dll-build-clean.sh || true--elif [[ -x tools/gh-windows-build-and-install.sh ]]; then-- log "triggering Windows DLL build/install helper"-- tools/gh-windows-build-and-install.sh || true--else-- log "triggering workflow directly"-- gh workflow run windows-source-sdk-dlls.yml --repo "$REPO" --ref "$BRANCH"-- sleep 5-- run_id="$(gh run list --repo "$REPO" --workflow windows-source-sdk-dlls.yml --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"-- log "watching run $run_id"-- gh run watch "$run_id" --repo "$REPO" --exit-status || true-- if [[ -x tools/windows-workflow-debug-and-install.sh ]]; then-- tools/windows-workflow-debug-and-install.sh "$run_id" || true-- fi--fi----log "done"ddeleted file mode 100755@@@ -1,160 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----log(){ printf '[openvibe] %s\n' "$*"; }--warn(){ printf '[openvibe warn] %s\n' "$*" >&2; }----ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"--cd "$ROOT"--BRANCH="$(git rev-parse --abbrev-ref HEAD)"--if [[ -x tools/openvibe-gh-repo.sh ]]; then-- REPO="$(tools/openvibe-gh-repo.sh)"--else-- remote="$(git config --get remote.origin.url || true)"-- REPO="${remote#[email protected]:}"; REPO="${REPO#ssh://[email protected]/}"; REPO="${REPO#https://github.com/}"; REPO="${REPO%.git}"; REPO="${REPO%/}"--fi--WORKFLOW="windows-source-sdk-dlls.yml"----log "fix Windows server stub PCH order + rerun"--log "root=$ROOT"--log "branch=$BRANCH"--log "repo=$REPO"----PY="$(command -v python3 || command -v python || true)"--if [[ -z "$PY" ]]; then-- echo "python3/python required for this patch script" >&2-- exit 1--fi----"$PY" - <<'PY'--from pathlib import Path--import re----root = Path.cwd()--files = [-- Path("sdk/openvibe/server/hl2mp/openvibe_js_server.cpp"),-- Path("sdk/openvibe/shared/ov_js_runtime.cpp"),-- Path("sdk/openvibe/shared/ov_js_bindings.cpp"),-- Path("sdk/openvibe/shared/ov_js_player.cpp"),--]--marker = "// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB"--cond = "#if defined(_WIN32) && defined(GAME_DLL) && !defined(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS)"----for rel in files:-- path = root / rel-- if not path.exists():-- raise SystemExit(f"missing expected file: {rel}")-- text = path.read_text(encoding="utf-8")-- original = text-- if marker not in text:-- print(f"[skip] no stub marker: {rel}")-- continue---- # MSVC /Yu cbase.h skips preprocessor/code before the PCH include. The earlier-- # stub patch put #if before #include "cbase.h", so MSVC skipped the #if and then-- # saw an unexpected #else. Make cbase.h the first real line.-- bom = "\ufeff" if text.startswith("\ufeff") else ""-- if bom:-- text = text[1:]---- start_pat = re.compile(-- r"\A\s*// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB\s*\r?\n"-- r"\s*#if defined\(_WIN32\) && defined\(GAME_DLL\) && !defined\(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS\)\s*\r?\n"-- r"\s*(?:#include \"cbase\.h\"\s*\r?\n)?",-- re.MULTILINE,-- )-- text2, n = start_pat.subn(f'#include "cbase.h"\n{marker}\n{cond}\n', text, count=1)-- if n == 0:-- # Idempotent path: already starts with cbase.h, but still normalize the next lines.-- text2 = text-- text2 = re.sub(-- r"\A\s*#include \"cbase\.h\"\s*\r?\n\s*// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB\s*\r?\n\s*#if defined\(_WIN32\) && defined\(GAME_DLL\) && !defined\(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS\)\s*\r?\n",-- f'#include "cbase.h"\n{marker}\n{cond}\n',-- text2,-- count=1,-- )---- # Remove the duplicate cbase.h include from the real/non-stub side. It is already-- # first in the translation unit and must not be repeated immediately after #else.-- text2 = re.sub(r"(#else\s*\r?\n)\s*#include \"cbase\.h\"\s*\r?\n", r"\1", text2, count=1)---- # Ensure there is a newline before #endif at EOF.-- text2 = text2.rstrip() + "\n"---- # Cheap sanity checks for the stub wrapper.-- if not text2.startswith('#include "cbase.h"\n'):-- raise SystemExit(f"{rel}: cbase.h is not first after patch")-- if text2.count(cond) != 1:-- raise SystemExit(f"{rel}: expected exactly one stub #if, found {text2.count(cond)}")-- if text2.count("#else") < 1:-- raise SystemExit(f"{rel}: expected at least one #else")-- if text2.count("#endif") < 1:-- raise SystemExit(f"{rel}: expected at least one #endif")---- if text2 != original:-- path.write_text(bom + text2, encoding="utf-8", newline="\n")-- print(f"[patched] {rel}")-- else:-- print(f"[ok] already normalized: {rel}")--PY----cat > docs/WINDOWS_SERVER_STUB_PCH_ORDER.md <<'EOF_DOC'--# Windows server stub PCH order----Source SDK server projects compile with MSVC precompiled headers using `/Yu cbase.h`.--MSVC ignores/skips text before the configured PCH include, so the Windows server--QuickJS stub wrapper cannot put `#if ...` before `#include "cbase.h"`.----The stubbed OpenVibe server `.cpp` files must start with:----```cpp--#include "cbase.h"--// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB--#if defined(_WIN32) && defined(GAME_DLL) && !defined(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS)--```----The non-stub branch must not re-include `cbase.h` immediately after `#else`.--EOF_DOC----log "git diff summary"--git diff --stat----if [[ -n "$(git status --porcelain)" ]]; then-- git add \-- sdk/openvibe/server/hl2mp/openvibe_js_server.cpp \-- sdk/openvibe/shared/ov_js_runtime.cpp \-- sdk/openvibe/shared/ov_js_bindings.cpp \-- sdk/openvibe/shared/ov_js_player.cpp \-- docs/WINDOWS_SERVER_STUB_PCH_ORDER.md-- git commit -m "Fix Windows server stub PCH include order"--else-- log "no source changes to commit"--fi----log "pushing $BRANCH"--git push origin "$BRANCH"----if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then-- log "triggering clean Windows DLL build"-- tools/trigger-windows-dll-build-clean.sh || true--elif command -v gh >/dev/null 2>&1; then-- log "triggering workflow"-- gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$BRANCH" || true-- sleep 8-- RUN_ID="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId' || true)"-- if [[ -n "${RUN_ID:-}" && "$RUN_ID" != "null" ]]; then-- log "watching run $RUN_ID"-- gh run watch "$RUN_ID" --repo "$REPO" --exit-status || true-- if [[ -x tools/windows-workflow-debug-and-install.sh ]]; then-- tools/windows-workflow-debug-and-install.sh "$RUN_ID" || true-- fi-- fi--else-- warn "gh not found; pushed patch but did not trigger workflow"--fi----if [[ -x tools/verify-openvibe-dll-content.sh ]]; then-- tools/verify-openvibe-dll-content.sh || true--fi----log "done"ddeleted file mode 100755@@@ -1,330 +0,0 @@--#!/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"ddeleted file mode 100755@@@ -1,335 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--cd "$ROOT"----BRANCH="$(git branch --show-current)"--echo "[openvibe] fix Windows build: VPC generated extensionless solution / direct vcxproj build"--echo "[openvibe] root=$ROOT"--echo "[openvibe] branch=$BRANCH"----mkdir -p tools docs----cat > tools/build-sdk-windows.ps1 <<'PS1'--param(-- [switch]$InDevShell--)----$ErrorActionPreference = "Stop"----$Root = if ($env:OPENVIBE_ROOT) { (Resolve-Path $env:OPENVIBE_ROOT).Path } else { (Resolve-Path (Join-Path $PSScriptRoot "..")).Path }--$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root "engine/source-sdk-2013" }--$Src = Join-Path $Sdk "src"--$Mod = Join-Path $Root "game/openvibe.games"--$OutBin = Join-Path $Mod "bin"--$LogDir = Join-Path $Root "artifacts/windows-build-debug"--New-Item -ItemType Directory -Force -Path $LogDir | Out-Null----function Say($m) { Write-Host "[openvibe-win] $m" }--function Require($p, $msg) {-- if (!(Test-Path $p)) { throw "$msg`nMissing: $p" }--}----function Find-VcVars64 {-- $candidates = @(-- "${env:ProgramFiles}\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat",-- "${env:ProgramFiles}\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvars64.bat",-- "${env:ProgramFiles}\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat",-- "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat",-- "${env:ProgramFiles}\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat"-- )-- foreach ($c in $candidates) {-- if ($c -and (Test-Path $c)) { return $c }-- }-- return $null--}----Say "root=$Root"--Say "sdk=$Sdk"--Say "src=$Src"----Require $Src "Source SDK checkout is missing from this runner. Bootstrap must create engine/source-sdk-2013 first."----# VPC is generating *_win64_*.vcxproj on the hosted runner, so use an x64 VS shell.--# clang-cl/lib/msbuild all remain MSVC ABI-compatible.--if (-not $InDevShell -or !(Get-Command cl.exe -ErrorAction SilentlyContinue)) {-- $vcvars = Find-VcVars64-- if (-not $vcvars) { throw "Could not find Visual Studio vcvars64.bat" }-- Say "relaunching through MSVC x64 dev shell: $vcvars"-- $self = $PSCommandPath-- $cmd = "`"$vcvars`" && powershell -NoProfile -ExecutionPolicy Bypass -File `"$self`" -InDevShell"-- & cmd.exe /d /s /c $cmd-- exit $LASTEXITCODE--}----Say "cl.exe already available"--where.exe cl | Out-File (Join-Path $LogDir "where-cl.txt")--where.exe msbuild | Out-File (Join-Path $LogDir "where-msbuild.txt")--where.exe lib | Out-File (Join-Path $LogDir "where-lib.txt")----# Apply OpenVibe source files using Git Bash when available. Skip Linux QuickJS build on Windows.--$bash = (Get-Command bash -ErrorAction SilentlyContinue)--if ($bash) {-- Say "applying OpenVibe SDK patch through bash"-- $env:OPENVIBE_ROOT = $Root-- $env:OPENVIBE_SDK = $Sdk-- $env:OPENVIBE_SKIP_QJS_BUILD = "1"-- & bash "$Root/tools/apply-openvibe-sdk.sh" 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "apply-openvibe-sdk.log")-- if ($LASTEXITCODE -ne 0) { throw "apply-openvibe-sdk.sh failed with exit code $LASTEXITCODE" }--} else {-- throw "Git Bash is required on the Windows runner to apply the OpenVibe SDK patch."--}----# Build QuickJS as MSVC ABI objects/lib. quickjs itself uses clang-cl because the upstream C is GNU/C99-ish.--if (Test-Path (Join-Path $Root "tools/build-quickjs-lib-windows.ps1")) {-- Say "building QuickJS Windows static library"-- & powershell -NoProfile -ExecutionPolicy Bypass -File (Join-Path $Root "tools/build-quickjs-lib-windows.ps1") 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "quickjs-windows.log")-- if ($LASTEXITCODE -ne 0) { throw "build-quickjs-lib-windows.ps1 failed with exit code $LASTEXITCODE" }--} else {-- Say "no build-quickjs-lib-windows.ps1 found; continuing"--}----Set-Location $Src----function Test-SlnContent([string]$path) {-- if (!(Test-Path $path)) { return $false }-- try {-- $first = Get-Content -Path $path -TotalCount 3 -ErrorAction Stop-- return (($first -join "`n") -match "Microsoft Visual Studio Solution File")-- } catch {-- return $false-- }--}----function Normalize-Solutions {-- $out = New-Object System.Collections.Generic.List[System.IO.FileInfo]---- # Normal .sln files.-- Get-ChildItem -Path $Src -Recurse -File -Filter "*.sln" -ErrorAction SilentlyContinue | ForEach-Object {-- if (Test-SlnContent $_.FullName) { [void]$out.Add($_) }-- }---- # VPC sometimes logs "Writing solution file ...\everything." and produces an extensionless-ish file.-- Get-ChildItem -Path $Src -Recurse -File -ErrorAction SilentlyContinue |-- Where-Object { $_.Name -match '^(everything|games|OpenVibe_HL2MP)(\.?)$' } |-- ForEach-Object {-- if (Test-SlnContent $_.FullName) {-- $fixed = Join-Path $_.DirectoryName ($_.BaseName.TrimEnd('.') + ".sln")-- if ($fixed -ne $_.FullName) {-- Copy-Item $_.FullName $fixed -Force-- Say "normalized extensionless VPC solution $($_.FullName) -> $fixed"-- [void]$out.Add((Get-Item $fixed))-- } else {-- [void]$out.Add($_)-- }-- }-- }---- return @($out | Sort-Object FullName -Unique)--}----function Run-ProjectGenerator {-- Say "generating Source SDK Visual Studio projects"-- $generators = @(-- (Join-Path $Src "createallprojects.bat"),-- (Join-Path $Src "creategameprojects.bat"),-- (Join-Path $Src "createprojects.bat")-- )---- $ran = $false-- foreach ($gen in $generators) {-- if (Test-Path $gen) {-- Say "running $gen"-- & cmd /c "`"$gen`"" 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "project-generation.log")-- $ran = $true-- break-- }-- }---- if (-not $ran) {-- $vpc = Join-Path $Src "devtools/bin/vpc.exe"-- if (Test-Path $vpc) {-- Say "running vpc.exe fallback"-- & $vpc /hl2mp /define:SOURCESDK +game /mksln OpenVibe_HL2MP.sln 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "vpc.log")-- $ran = $true-- }-- }---- if (-not $ran) {-- throw "No Source SDK project generator found."-- }--}----function Get-ProjectConfigs([string]$projPath) {-- $text = Get-Content -Raw $projPath-- $matches = [regex]::Matches($text, '<ProjectConfiguration\s+Include="([^|"]+)\|([^"]+)"')-- $pairs = @()-- foreach ($m in $matches) {-- $pairs += [pscustomobject]@{ Configuration = $m.Groups[1].Value; Platform = $m.Groups[2].Value }-- }-- return @($pairs | Sort-Object Configuration,Platform -Unique)--}----function Sort-PreferredConfigs($pairs, [string]$projName) {-- $preferred = @()-- foreach ($cfg in @("Release", "Release_HL2MP", "Release HL2MP", "Debug")) {-- foreach ($plat in @("x64", "Win64", "Win32", "x86")) {-- $hit = $pairs | Where-Object { $_.Configuration -eq $cfg -and $_.Platform -eq $plat }-- if ($hit) { $preferred += $hit }-- }-- }-- # Add anything else not covered.-- foreach ($p in $pairs) {-- if (-not ($preferred | Where-Object { $_.Configuration -eq $p.Configuration -and $_.Platform -eq $p.Platform })) {-- $preferred += $p-- }-- }-- return @($preferred)--}----function Invoke-MSBuildProject([System.IO.FileInfo]$proj, [string]$label) {-- Say "building $label project=$($proj.FullName)"-- $pairs = Get-ProjectConfigs $proj.FullName-- if ($pairs.Count -eq 0) {-- $pairs = @([pscustomobject]@{ Configuration = "Release"; Platform = "x64" })-- }-- $pairs = Sort-PreferredConfigs $pairs $proj.Name---- foreach ($p in $pairs) {-- $cfgSafe = ($p.Configuration -replace '[^A-Za-z0-9]+','_')-- $platSafe = ($p.Platform -replace '[^A-Za-z0-9]+','_')-- $log = Join-Path $LogDir "msbuild-$label-$cfgSafe-$platSafe.log"-- Say "msbuild $label cfg=$($p.Configuration) platform=$($p.Platform)"-- & msbuild $proj.FullName /m /p:Configuration="$($p.Configuration)" /p:Platform="$($p.Platform)" /t:Build /v:minimal /nologo 2>&1 | Tee-Object -FilePath $log-- if ($LASTEXITCODE -eq 0) {-- Say "built $label with cfg=$($p.Configuration) platform=$($p.Platform)"-- return $true-- }-- }---- return $false--}----# Generate projects if no relevant vcxproj files exist yet.--$clientProjects = @(Get-ChildItem -Path $Src -Recurse -File -Filter "*client*hl2mp*.vcxproj" -ErrorAction SilentlyContinue)--$serverProjects = @(Get-ChildItem -Path $Src -Recurse -File -Filter "*server*hl2mp*.vcxproj" -ErrorAction SilentlyContinue)--$solutions = Normalize-Solutions----if ($clientProjects.Count -eq 0 -or $serverProjects.Count -eq 0) {-- Run-ProjectGenerator-- $solutions = Normalize-Solutions-- $clientProjects = @(Get-ChildItem -Path $Src -Recurse -File -Filter "*client*hl2mp*.vcxproj" -ErrorAction SilentlyContinue)-- $serverProjects = @(Get-ChildItem -Path $Src -Recurse -File -Filter "*server*hl2mp*.vcxproj" -ErrorAction SilentlyContinue)--}----"=== normalized solutions ===" | Out-File (Join-Path $LogDir "normalized-solutions.txt")--$solutions | Select-Object FullName,Length,LastWriteTime | Format-List | Out-File (Join-Path $LogDir "normalized-solutions.txt") -Append--"=== hl2mp projects ===" | Out-File (Join-Path $LogDir "hl2mp-projects.txt")--@($clientProjects + $serverProjects) | Select-Object FullName,Length,LastWriteTime | Format-List | Out-File (Join-Path $LogDir "hl2mp-projects.txt") -Append----if ($clientProjects.Count -eq 0) {-- Get-ChildItem -Path $Src -Recurse -File -Include "*.vcxproj","*.sln","everything*" -ErrorAction SilentlyContinue |-- Select-Object FullName,Length,LastWriteTime |-- Format-List | Out-File (Join-Path $LogDir "project-search-after-generator.txt")-- throw "No HL2MP client vcxproj was generated. Check openvibe-windows-build-debug artifact."--}--if ($serverProjects.Count -eq 0) {-- Get-ChildItem -Path $Src -Recurse -File -Include "*.vcxproj","*.sln","everything*" -ErrorAction SilentlyContinue |-- Select-Object FullName,Length,LastWriteTime |-- Format-List | Out-File (Join-Path $LogDir "project-search-after-generator.txt")-- throw "No HL2MP server vcxproj was generated. Check openvibe-windows-build-debug artifact."--}----# Prefer the actual HL2MP game projects and prefer win64 because current Valve VPC emitted *_win64_hl2mp.vcxproj on the runner.--$clientProject = $clientProjects |-- Sort-Object @{ Expression = { if ($_.Name -match 'win64') { 0 } else { 1 } } },-- @{ Expression = { if ($_.Name -match '^client') { 0 } else { 1 } } },-- FullName |-- Select-Object -First 1--$serverProject = $serverProjects |-- Sort-Object @{ Expression = { if ($_.Name -match 'win64') { 0 } else { 1 } } },-- @{ Expression = { if ($_.Name -match '^server') { 0 } else { 1 } } },-- FullName |-- Select-Object -First 1----$beforeBuild = Get-Date----if (-not (Invoke-MSBuildProject $clientProject "client")) {-- throw "MSBuild failed all configurations for $($clientProject.FullName). Check msbuild-client-*.log."--}--if (-not (Invoke-MSBuildProject $serverProject "server")) {-- throw "MSBuild failed all configurations for $($serverProject.FullName). Check msbuild-server-*.log."--}----New-Item -ItemType Directory -Force -Path $OutBin | Out-Null----# Prefer recently built DLLs. Then prefer HL2MP paths.--$allDlls = @(Get-ChildItem -Path $Sdk -Recurse -Filter "*.dll" -ErrorAction SilentlyContinue |-- Where-Object { $_.LastWriteTime -ge $beforeBuild.AddMinutes(-2) } |-- Sort-Object LastWriteTime -Descending)----"=== dlls after build ===" | Out-File (Join-Path $LogDir "dlls-after-build.txt")--$allDlls | Select-Object FullName,Length,LastWriteTime | Format-List | Out-File (Join-Path $LogDir "dlls-after-build.txt") -Append----$client = $allDlls |-- Where-Object { $_.Name -ieq "client.dll" -and $_.FullName -match "hl2mp|client" } |-- Select-Object -First 1--$server = $allDlls |-- Where-Object { $_.Name -ieq "server.dll" -and $_.FullName -match "hl2mp|server" } |-- Select-Object -First 1----# Fallback: search all SDK DLLs if timestamps were weird.--if (-not $client) {-- $client = Get-ChildItem -Path $Sdk -Recurse -Filter client.dll -ErrorAction SilentlyContinue |-- Where-Object { $_.FullName -match "hl2mp|client" } |-- Sort-Object LastWriteTime -Descending |-- Select-Object -First 1--}--if (-not $server) {-- $server = Get-ChildItem -Path $Sdk -Recurse -Filter server.dll -ErrorAction SilentlyContinue |-- Where-Object { $_.FullName -match "hl2mp|server" } |-- Sort-Object LastWriteTime -Descending |-- Select-Object -First 1--}----if (-not $client) { throw "client.dll was not produced" }--if (-not $server) { throw "server.dll was not produced" }----Say "copy client=$($client.FullName)"--Say "copy server=$($server.FullName)"--Copy-Item $client.FullName (Join-Path $OutBin "client.dll") -Force--Copy-Item $server.FullName (Join-Path $OutBin "server.dll") -Force----Say "done"--PS1----cat > docs/WINDOWS_VPC_DIRECT_PROJECT_BUILD.md <<'EOF'--# Windows VPC direct project build----Valve's `createallprojects.bat` runs VPC and is expected to emit `everything.sln`, but on the hosted runner VPC logged an extensionless `everything` solution while still generating the actual HL2MP `.vcxproj` files.----The Windows build now treats the solution as optional and builds the generated HL2MP client/server projects directly:----- `client*_hl2mp*.vcxproj`--- `server*_hl2mp*.vcxproj`----It also records generated projects and DLL locations in `artifacts/windows-build-debug`.--EOF----echo "[openvibe] git diff summary"--git diff --stat -- tools/build-sdk-windows.ps1 docs/WINDOWS_VPC_DIRECT_PROJECT_BUILD.md || true----git add tools/build-sdk-windows.ps1 docs/WINDOWS_VPC_DIRECT_PROJECT_BUILD.md "$0"--if ! git diff --cached --quiet; then-- git commit -m "Build Windows Source SDK HL2MP projects directly"--fi----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-- tools/gh-windows-build-and-install.sh--fiddeleted file mode 100755@@@ -1,392 +0,0 @@--#!/usr/bin/env bash--set -euo pipefail----ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--REPO="${OPENVIBE_REPO:-OpenVibers/OpenVibe.Games}"--REPO="${REPO%.git}"--WORKFLOW="${OPENVIBE_WINDOWS_WORKFLOW:-windows-source-sdk-dlls.yml}"--BRANCH="$(git -C "$ROOT" rev-parse --abbrev-ref HEAD)"--LOG_DIR="$ROOT/artifacts/windows-workflow-debug"----cd "$ROOT"----echo "[openvibe] fix Windows workflow diagnostics + rerun"--echo "[openvibe] root=$ROOT"--echo "[openvibe] repo=$REPO"--echo "[openvibe] workflow=$WORKFLOW"--echo "[openvibe] branch=$BRANCH"----if ! command -v gh >/dev/null 2>&1; then-- echo "[openvibe error] gh is required. Install/auth first:" >&2-- echo " sudo apt update && sudo apt install gh" >&2-- echo " gh auth login" >&2-- exit 1--fi----if ! gh auth status >/dev/null 2>&1; then-- echo "[openvibe error] gh is installed but not authenticated. Run:" >&2-- echo " gh auth login" >&2-- exit 1--fi----mkdir -p "$LOG_DIR"----latest_run="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId // empty')"--if [[ -n "$latest_run" ]]; then-- echo "[openvibe] latest workflow run=$latest_run"-- echo "[openvibe] saving failed/full logs into $LOG_DIR"-- gh run view "$latest_run" --repo "$REPO" --log-failed > "$LOG_DIR/run-${latest_run}-failed.log" 2>&1 || true-- gh run view "$latest_run" --repo "$REPO" --log > "$LOG_DIR/run-${latest_run}-full.log" 2>&1 || true---- echo "[openvibe] likely error snippets:"-- grep -Eina "error|fatal|exception|not found|cannot find|MSB[0-9]+|LNK[0-9]+|C[0-9]{4}|No such|missing|failed" \-- "$LOG_DIR/run-${latest_run}-failed.log" "$LOG_DIR/run-${latest_run}-full.log" \-- | tail -n 80 || true--else-- echo "[openvibe warn] no existing workflow run found yet"--fi----echo----echo "[openvibe] rewriting Windows workflow to always upload logs/artifacts"--mkdir -p .github/workflows--cat > .github/workflows/windows-source-sdk-dlls.yml <<'YAML'--name: Build Windows Source DLLs----on:-- workflow_dispatch:-- inputs:-- diagnostics:-- description: "Upload verbose build diagnostics"-- required: false-- default: "1"-- push:-- branches:-- - codex/openvibe-next-steps-- paths:-- - ".github/workflows/windows-source-sdk-dlls.yml"-- - "tools/build-sdk-windows.ps1"-- - "tools/build-quickjs-lib-windows.ps1"-- - "tools/apply-openvibe-sdk.sh"-- - "sdk/openvibe/**"-- - "game/openvibe.games/js/**"----permissions:-- contents: read-- actions: read----jobs:-- windows-dlls:-- name: Windows client.dll/server.dll-- runs-on: windows-2022-- defaults:-- run:-- shell: pwsh---- steps:-- - name: Checkout OpenVibe-- uses: actions/checkout@v4-- with:-- fetch-depth: 1---- - name: Configure git line endings-- run: |-- git config --global core.autocrlf false-- git config --global core.eol lf-- git status --short---- - name: Setup MSBuild-- uses: microsoft/setup-msbuild@v2---- - name: Show toolchain and tree-- run: |-- $ErrorActionPreference = 'Continue'-- Write-Host "pwd=$(Get-Location)"-- git rev-parse --short HEAD-- where.exe msbuild-- where.exe cl-- where.exe link-- where.exe bash-- Get-ChildItem -Force | Select-Object Mode,Length,Name-- if (Test-Path engine) { Get-ChildItem -Force engine | Select-Object Mode,Length,Name } else { Write-Host "[miss] engine directory" }-- if (Test-Path engine/source-sdk-2013/src) { Get-ChildItem -Force engine/source-sdk-2013/src | Select-Object Mode,Length,Name } else { Write-Host "[miss] engine/source-sdk-2013/src" }---- - name: Build Windows Source SDK DLLs-- id: build-- run: |-- $ErrorActionPreference = 'Stop'-- New-Item -ItemType Directory -Force -Path artifacts/windows-build-debug | Out-Null-- powershell -NoProfile -ExecutionPolicy Bypass -File tools/build-sdk-windows.ps1 *>&1 | Tee-Object -FilePath artifacts/windows-build-debug/build-sdk-windows.log---- - name: Stage build outputs and diagnostics-- if: always()-- run: |-- $ErrorActionPreference = 'Continue'-- New-Item -ItemType Directory -Force -Path artifacts/windows-dlls | Out-Null-- New-Item -ItemType Directory -Force -Path artifacts/windows-build-debug | Out-Null---- "=== repo ===" | Out-File artifacts/windows-build-debug/tree.txt-- Get-ChildItem -Force | Format-Table -AutoSize | Out-File artifacts/windows-build-debug/tree.txt -Append-- "=== dll search ===" | Out-File artifacts/windows-build-debug/dll-search.txt-- Get-ChildItem -Recurse -Force -Include client.dll,server.dll -ErrorAction SilentlyContinue |-- Select-Object FullName,Length,LastWriteTime |-- Format-List | Out-File artifacts/windows-build-debug/dll-search.txt -Append-- "=== vc/sln search ===" | Out-File artifacts/windows-build-debug/project-search.txt-- Get-ChildItem -Recurse -Force -Include *.sln,*.vcxproj,*.vcproj,*.log -ErrorAction SilentlyContinue |-- Select-Object FullName,Length,LastWriteTime |-- Format-List | Out-File artifacts/windows-build-debug/project-search.txt -Append---- $clientCandidates = @(-- "game/openvibe.games/bin/client.dll",-- "engine/source-sdk-2013/game/mod_hl2mp/bin/client.dll",-- "engine/source-sdk-2013/game/mod_hl2mp/bin/win64/client.dll",-- "engine/source-sdk-2013/game/mod_hl2mp/bin/Release/client.dll",-- "engine/source-sdk-2013/src/game/client/Release_hl2mp/client.dll",-- "engine/source-sdk-2013/src/game/client/Release/client.dll"-- )-- $serverCandidates = @(-- "game/openvibe.games/bin/server.dll",-- "engine/source-sdk-2013/game/mod_hl2mp/bin/server.dll",-- "engine/source-sdk-2013/game/mod_hl2mp/bin/win64/server.dll",-- "engine/source-sdk-2013/game/mod_hl2mp/bin/Release/server.dll",-- "engine/source-sdk-2013/src/game/server/Release_hl2mp/server.dll",-- "engine/source-sdk-2013/src/game/server/Release/server.dll"-- )---- $client = $clientCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1-- $server = $serverCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1-- if ($client) { Copy-Item $client artifacts/windows-dlls/client.dll -Force }-- if ($server) { Copy-Item $server artifacts/windows-dlls/server.dll -Force }---- if (Test-Path artifacts/windows-dlls/client.dll) {-- $clientText = (Select-String -Path artifacts/windows-dlls/client.dll -Pattern "ov_join|ov_menu|OpenVibe" -Encoding Byte -SimpleMatch -Quiet)-- Write-Host "client.dll staged"-- } else {-- Write-Host "[miss] no client.dll staged"-- }-- if (Test-Path artifacts/windows-dlls/server.dll) { Write-Host "server.dll staged" } else { Write-Host "[miss] no server.dll staged" }---- - name: Upload Windows DLL artifact-- if: always()-- uses: actions/upload-artifact@v4-- with:-- name: openvibe-windows-dlls-- path: artifacts/windows-dlls/**-- if-no-files-found: warn---- - name: Upload Windows build diagnostics-- if: always()-- uses: actions/upload-artifact@v4-- with:-- name: openvibe-windows-build-debug-- path: artifacts/windows-build-debug/**-- if-no-files-found: warn--YAML----cat > tools/build-sdk-windows.ps1 <<'PS1'--$ErrorActionPreference = "Stop"----$Root = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path--$Sdk = Join-Path $Root "engine/source-sdk-2013"--$Src = Join-Path $Sdk "src"--$Mod = Join-Path $Root "game/openvibe.games"--$OutBin = Join-Path $Mod "bin"--$Qjs = Join-Path $Src "game/shared/openvibe/third_party/quickjs"--$LogDir = Join-Path $Root "artifacts/windows-build-debug"--New-Item -ItemType Directory -Force -Path $LogDir | Out-Null----function Say($m) { Write-Host "[openvibe-win] $m" }--function Require($p, $msg) {-- if (!(Test-Path $p)) { throw "$msg`nMissing: $p" }--}----Say "root=$Root"--Say "sdk=$Sdk"--Say "src=$Src"--Require $Src "Source SDK checkout is missing from this runner. The workflow must have engine/source-sdk-2013 available in the repository/submodule before Windows DLLs can build."----# Apply OpenVibe source files using Git Bash when available. This reuses the Linux patcher on Windows runners.--$bash = (Get-Command bash -ErrorAction SilentlyContinue)--if ($bash) {-- Say "applying OpenVibe SDK patch through bash"-- & bash "$Root/tools/apply-openvibe-sdk.sh"-- if ($LASTEXITCODE -ne 0) { throw "apply-openvibe-sdk.sh failed with exit code $LASTEXITCODE" }--} else {-- throw "Git Bash is required on the Windows runner to apply the OpenVibe SDK patch."--}----# Build QuickJS as MSVC objects/lib. The Linux .a cannot be linked into Windows DLLs.--if (Test-Path (Join-Path $Root "tools/build-quickjs-lib-windows.ps1")) {-- Say "building QuickJS Windows static library"-- & powershell -NoProfile -ExecutionPolicy Bypass -File (Join-Path $Root "tools/build-quickjs-lib-windows.ps1")-- if ($LASTEXITCODE -ne 0) { throw "build-quickjs-lib-windows.ps1 failed with exit code $LASTEXITCODE" }--} else {-- Say "no build-quickjs-lib-windows.ps1 found; continuing"--}----Set-Location $Src----# Generate Visual Studio project files if needed.--$solutions = @(Get-ChildItem -Path $Src -Recurse -Filter "*.sln" -ErrorAction SilentlyContinue)--if ($solutions.Count -eq 0) {-- Say "no .sln files found; trying Source SDK project generators"-- $generators = @(-- (Join-Path $Src "creategameprojects.bat"),-- (Join-Path $Src "createallprojects.bat"),-- (Join-Path $Src "createprojects.bat")-- )-- $ran = $false-- foreach ($gen in $generators) {-- if (Test-Path $gen) {-- Say "running $gen"-- & cmd /c "`"$gen`"" | Tee-Object -FilePath (Join-Path $LogDir "project-generation.log")-- $ran = $true-- break-- }-- }---- if (-not $ran) {-- $vpc = Join-Path $Src "devtools/bin/vpc.exe"-- if (Test-Path $vpc) {-- Say "running vpc.exe fallback"-- & $vpc /hl2mp +game /mksln OpenVibe_HL2MP.sln | Tee-Object -FilePath (Join-Path $LogDir "vpc.log")-- $ran = $true-- }-- }---- $solutions = @(Get-ChildItem -Path $Src -Recurse -Filter "*.sln" -ErrorAction SilentlyContinue)-- if ($solutions.Count -eq 0) {-- Get-ChildItem -Path $Src -Force | Select-Object Mode,Length,Name | Format-Table -AutoSize | Out-File (Join-Path $LogDir "src-root-after-generator.txt")-- throw "No Visual Studio solution was generated. Check openvibe-windows-build-debug artifact."-- }--}----Say "solutions found:"--$solutions | ForEach-Object { Say " $($_.FullName)" }----# Pick the most likely HL2MP/game solution.--$solution = $solutions |-- Where-Object { $_.Name -match "hl2mp|game|sdk|everything|OpenVibe" } |-- Select-Object -First 1--if (-not $solution) { $solution = $solutions | Select-Object -First 1 }--Say "selected solution=$($solution.FullName)"----# Try common project targets/configurations. Source SDK projects vary by branch/version.--$configs = @("Release", "Release_HL2MP", "Release HL2MP")--$platforms = @("Win32", "x86")--$targets = @("client_hl2mp", "server_hl2mp", "client", "server", "Build")----$builtAny = $false--foreach ($cfg in $configs) {-- foreach ($plat in $platforms) {-- foreach ($target in $targets) {-- Say "msbuild cfg=$cfg platform=$plat target=$target"-- & msbuild $solution.FullName /m /p:Configuration=$cfg /p:Platform=$plat /t:$target /v:minimal /nologo 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "msbuild-$($cfg -replace '[^A-Za-z0-9]','_')-$plat-$target.log")-- if ($LASTEXITCODE -eq 0) {-- $builtAny = $true-- break-- }-- }-- if ($builtAny) { break }-- }-- if ($builtAny) { break }--}----if (-not $builtAny) {-- throw "MSBuild could not build any known Source SDK HL2MP target. Check uploaded msbuild logs."--}----New-Item -ItemType Directory -Force -Path $OutBin | Out-Null----$client = Get-ChildItem -Path $Sdk -Recurse -Filter client.dll -ErrorAction SilentlyContinue |-- Where-Object { $_.FullName -match "hl2mp|mod_hl2mp|client" } |-- Sort-Object LastWriteTime -Descending |-- Select-Object -First 1--$server = Get-ChildItem -Path $Sdk -Recurse -Filter server.dll -ErrorAction SilentlyContinue |-- Where-Object { $_.FullName -match "hl2mp|mod_hl2mp|server" } |-- Sort-Object LastWriteTime -Descending |-- Select-Object -First 1----if (-not $client) { throw "client.dll was not produced" }--if (-not $server) { throw "server.dll was not produced" }----Say "copy client=$($client.FullName)"--Say "copy server=$($server.FullName)"--Copy-Item $client.FullName (Join-Path $OutBin "client.dll") -Force--Copy-Item $server.FullName (Join-Path $OutBin "server.dll") -Force----Say "done"--PS1----# Add a local helper that downloads full logs/artifacts from the last run even when failed.--cat > tools/windows-workflow-debug-and-install.sh <<'SH'--#!/usr/bin/env bash--set -euo pipefail--ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"--REPO="${OPENVIBE_REPO:-OpenVibers/OpenVibe.Games}"--WORKFLOW="${OPENVIBE_WINDOWS_WORKFLOW:-windows-source-sdk-dlls.yml}"--BRANCH="$(git -C "$ROOT" rev-parse --abbrev-ref HEAD)"--OUT="$ROOT/artifacts/windows-workflow-debug"--cd "$ROOT"--mkdir -p "$OUT"--run="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"--echo "[openvibe] latest run=$run"--gh run view "$run" --repo "$REPO" --log > "$OUT/run-${run}.log" 2>&1 || true--gh run view "$run" --repo "$REPO" --log-failed > "$OUT/run-${run}-failed.log" 2>&1 || true--gh run download "$run" --repo "$REPO" --dir "$OUT/run-${run}-artifacts" || true--printf '\n[openvibe] tail of failed log:\n'--tail -n 160 "$OUT/run-${run}-failed.log" || true--printf '\n[openvibe] artifacts saved under: %s\n' "$OUT/run-${run}-artifacts"--if [[ -f "$OUT/run-${run}-artifacts/openvibe-windows-dlls/client.dll" && -f "$OUT/run-${run}-artifacts/openvibe-windows-dlls/server.dll" ]]; then-- mkdir -p game/openvibe.games/bin-- cp -f "$OUT/run-${run}-artifacts/openvibe-windows-dlls/client.dll" game/openvibe.games/bin/client.dll-- cp -f "$OUT/run-${run}-artifacts/openvibe-windows-dlls/server.dll" game/openvibe.games/bin/server.dll-- echo "[openvibe] installed DLL artifacts"-- tools/verify-openvibe-dll-content.sh || true--else-- echo "[openvibe] no DLL artifact available yet; check debug artifact/logs"--fi--SH--chmod +x tools/windows-workflow-debug-and-install.sh----git add .github/workflows/windows-source-sdk-dlls.yml tools/build-sdk-windows.ps1 tools/windows-workflow-debug-and-install.sh----if ! git diff --cached --quiet; then-- echo "[openvibe] committing workflow/build diagnostic patch"-- git commit -m "Improve Windows DLL workflow diagnostics"-- echo "[openvibe] pushing $BRANCH"-- git push origin "$BRANCH"--else-- echo "[openvibe] no workflow/build changes to commit"--fi----echo "[openvibe] triggering updated workflow"--gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$BRANCH"----echo "[openvibe] waiting for new workflow run to appear"--sleep 5--new_run="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId // empty')"--if [[ -z "$new_run" ]]; then-- echo "[openvibe error] could not find new workflow run" >&2-- exit 1--fi----echo "[openvibe] watching run $new_run"--set +e--gh run watch "$new_run" --repo "$REPO" --exit-status--status=$?--set -e----if [[ $status -eq 0 ]]; then-- echo "[openvibe] workflow passed; downloading artifacts"--else-- echo "[openvibe warn] workflow still failed; downloading diagnostics anyway"--fi----tools/windows-workflow-debug-and-install.sh----if [[ $status -ne 0 ]]; then-- echo-- echo "[openvibe] next: paste the tail printed above if you want me to patch the exact compile error."-- exit $status--fi
Binary files a/game/openvibe.games/bin/client.dll and b/game/openvibe.games/bin/client.dll differ
Binary files a/game/openvibe.games/bin/client.dll and b/game/openvibe.games/bin/client.dll differ
Binary files a/game/openvibe.games/bin/server.dll and b/game/openvibe.games/bin/server.dll differ
Binary files a/game/openvibe.games/bin/server.dll and b/game/openvibe.games/bin/server.dll differ
new file mode 100644
new file mode 100644
Binary files /dev/null and b/game/openvibe.games/bin/x64/client.dll differ
Binary files /dev/null and b/game/openvibe.games/bin/x64/client.dll differ
new file mode 100644
new file mode 100644
Binary files /dev/null and b/game/openvibe.games/bin/x64/server.dll differ
Binary files /dev/null and b/game/openvibe.games/bin/x64/server.dll differ
@@ -13,7 +13,7 @@ bind "e" "+use"
@@ -13,7 +13,7 @@ bind "e" "+use"
bind "f" "impulse 100"
bind "f" "impulse 100"
bind "g" "dropprimary"
bind "g" "dropprimary"
bind "p" "headtrack_reset_home_pos"
bind "p" "headtrack_reset_home_pos"
-bind "q" "lastinv"
+bind "q" "say !q"
bind "r" "+reload"
bind "r" "+reload"
bind "s" "+back"
bind "s" "+back"
bind "t" "impulse 201"
bind "t" "impulse 201"
@@ -25,7 +25,7 @@ bind "z" "+zoom"
@@ -25,7 +25,7 @@ bind "z" "+zoom"
bind "[" "invprev"
bind "[" "invprev"
bind "]" "invnext"
bind "]" "invnext"
bind "'" "+moveup"
bind "'" "+moveup"
-bind "`" "toggleconsole"
+bind "`" "ov_console"
bind "," "+moveleft"
bind "," "+moveleft"
bind "." "+moveright"
bind "." "+moveright"
bind "/" "+movedown"
bind "/" "+movedown"
@@ -47,19 +47,34 @@ bind "UPARROW" "+forward"
@@ -47,19 +47,34 @@ bind "UPARROW" "+forward"
bind "LEFTARROW" "+left"
bind "LEFTARROW" "+left"
bind "DOWNARROW" "+back"
bind "DOWNARROW" "+back"
bind "RIGHTARROW" "+right"
bind "RIGHTARROW" "+right"
+bind "F1" "ov_menu_main"
bind "F3" "askconnect_accept"
bind "F3" "askconnect_accept"
bind "F5" "jpeg"
bind "F5" "jpeg"
bind "F6" "save quick"
bind "F6" "save quick"
bind "F7" "load quick"
bind "F7" "load quick"
bind "F9" "vr_toggle"
bind "F9" "vr_toggle"
-bind "F10" "quit prompt"
+bind "F10" "ov_console"
bind "MOUSE1" "+attack"
bind "MOUSE1" "+attack"
bind "MOUSE2" "+attack2"
bind "MOUSE2" "+attack2"
bind "MWHEELUP" "invprev"
bind "MWHEELUP" "invprev"
bind "MWHEELDOWN" "invnext"
bind "MWHEELDOWN" "invnext"
+r_lightmap_bicubic_set "1"
+cl_chatfilter_version "1"
+m_pitch "0.022000"
+m_rawinput_onetime_reset "1"
+mat_software_aa_strength "0.000000"
+mat_software_aa_strength_vgui "1.000000"
+cl_playermodel "models/combine_soldier.mdl"
+ov_join_token "local-dev-hub"
+sv_skyname "sky_day01_01"
+snd_surround_speakers "2"
+snd_mute_losefocus "0"
name "Goosely"
name "Goosely"
-cl_logofile "materials/vgui/logos/spray.vtf"
+cl_logofile "materials/vgui/logos/spray_canned.vtf"
mat_colorcorrection "1"
mat_colorcorrection "1"
+con_enable "1"
r_shadowrendertotexture "1"
r_shadowrendertotexture "1"
r_waterforceexpensive "1"
r_waterforceexpensive "1"
mat_motion_blur_enabled "1"
mat_motion_blur_enabled "1"
+mat_queue_mode "0"
+mat_hdr_level "0"
new file mode 100644
new file mode 100644
Binary files /dev/null and b/game/openvibe.games/maps/graphs/ov_hub.ain differ
Binary files /dev/null and b/game/openvibe.games/maps/graphs/ov_hub.ain differ
@@ -10,16 +10,23 @@ fi
@@ -10,16 +10,23 @@ fi
"$ROOT/tools/dev-db-up.sh"
"$ROOT/tools/dev-db-up.sh"
-if tmux has-session -t ov-api 2>/dev/null; then
- tmux kill-session -t ov-api
-fi
-if tmux has-session -t ov-client-ui 2>/dev/null; then
- tmux kill-session -t ov-client-ui
-fi
+for session in ov-api ov-client-ui ov-runtime-server ov-runtime-client; do
+ if tmux has-session -t "$session" 2>/dev/null; then
+ tmux kill-session -t "$session"
+ fi
+done
tmux new-session -d -s ov-api "$ROOT/tools/dev-api.sh"
tmux new-session -d -s ov-api "$ROOT/tools/dev-api.sh"
tmux new-session -d -s ov-client-ui "$ROOT/tools/run-client-ui.sh"
tmux new-session -d -s ov-client-ui "$ROOT/tools/run-client-ui.sh"
+# GModJS Node runtimes: back the GUI console (SSE logs + eval + npm on
+# 41997/41996) and serve as the game bridge when ov_js_backend is "node".
+OV_RUNTIME_MODE="${OPENVIBE_RUNTIME_MODE:-hub}"
+tmux new-session -d -s ov-runtime-server \
+ "node '$ROOT/engine/openvibe-js-runtime/ov-runtime.js' --realm server --mode $OV_RUNTIME_MODE --root '$ROOT'"
+tmux new-session -d -s ov-runtime-client \
+ "node '$ROOT/engine/openvibe-js-runtime/ov-runtime.js' --realm client --mode $OV_RUNTIME_MODE --root '$ROOT'"
+
echo "[openvibe] waiting for api"
echo "[openvibe] waiting for api"
for _ in {1..60}; do
for _ in {1..60}; do
if curl -fsS http://127.0.0.1:3000/health >/dev/null 2>&1; then
if curl -fsS http://127.0.0.1:3000/health >/dev/null 2>&1; then
deleted file mode 100755
deleted file mode 100755
@@ -1,441 +0,0 @@
@@ -1,441 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"
-STAMP="$(date +%Y%m%d-%H%M%S)"
-
-cd "$ROOT"
-
-backup_file() {
- local file="$1"
- [[ -f "$file" ]] && cp "$file" "$file.bak.$STAMP"
-}
-
-echo "[openvibe] fixing duplicate ov_js_cmd and canonicalizing embedded JS server bridge"
-
-mkdir -p sdk/openvibe/server/hl2mp
-
-backup_file sdk/openvibe/server/hl2mp/openvibe_js_server.cpp
-backup_file sdk/openvibe/server/hl2mp/openvibe_server.cpp
-
-cat > sdk/openvibe/server/hl2mp/openvibe_js_server.cpp <<'CPP'
-#include "cbase.h"
-#include "hl2mp_player.h"
-#include "openvibe_js_server.h"
-#include "openvibe/ov_js_runtime.h"
-#include "openvibe/ov_js_player.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",
- "1",
- FCVAR_GAMEDLL,
- "Enable OpenVibe JavaScript runtime." );
-
-static COpenVibeJSRuntime g_OVServerJS;
-static bool g_OVServerJSStarted = false;
-
-static bool OpenVibeJS_IsRunning()
-{
- return ov_js_enabled.GetBool() && g_OVServerJS.Context() != nullptr;
-}
-
-static void OpenVibeJS_EnsureStarted()
-{
- if ( g_OVServerJSStarted )
- return;
-
- g_OVServerJSStarted = true;
-
- if ( !ov_js_enabled.GetBool() )
- {
- Msg( "[OV JS] disabled by ov_js_enabled=0\n" );
- return;
- }
-
- if ( g_OVServerJS.Init( true, ov_mode.GetString() ) )
- {
- Msg( "[OV JS] server runtime initialized for mode '%s'\n", ov_mode.GetString() );
- g_OVServerJS.CallHookVoid( "Initialize" );
-
- JSContext *ctx = g_OVServerJS.Context();
- JSValue mapName = JS_NewString( ctx, gpGlobals ? STRING( gpGlobals->mapname ) : "" );
- JSValueConst argv[] = { mapName };
- g_OVServerJS.CallHookVoid( "MapInitialize", 1, argv );
- JS_FreeValue( ctx, mapName );
- }
- else
- {
- Warning( "[OV JS] server runtime failed to initialize\n" );
- }
-}
-
-static void OpenVibeJS_CallConsoleCommand( const char *pszCommand )
-{
- OpenVibeJS_EnsureStarted();
-
- if ( !OpenVibeJS_IsRunning() )
- {
- Warning( "[OV JS] runtime is not running; command ignored: %s\n", pszCommand ? pszCommand : "" );
- return;
- }
-
- JSContext *ctx = g_OVServerJS.Context();
- JSValue command = JS_NewString( ctx, pszCommand ? pszCommand : "" );
- JSValueConst argv[] = { command };
-
- g_OVServerJS.CallHookVoid( "ConsoleCommand", 1, argv );
-
- JS_FreeValue( ctx, command );
-}
-
-void OpenVibeJS_ServerInit()
-{
- OpenVibeJS_EnsureStarted();
-}
-
-void OpenVibeJS_ServerShutdown()
-{
- if ( OpenVibeJS_IsRunning() )
- g_OVServerJS.CallHookVoid( "Shutdown" );
-
- g_OVServerJS.Shutdown();
- g_OVServerJSStarted = false;
-}
-
-void OpenVibeJS_ServerThink()
-{
- OpenVibeJS_EnsureStarted();
-
- if ( !OpenVibeJS_IsRunning() )
- return;
-
- g_OVServerJS.CallHookVoid( "Think" );
-}
-
-void OpenVibeJS_Server_PlayerInitialSpawn( CHL2MP_Player *player )
-{
- OpenVibeJS_EnsureStarted();
-
- if ( !OpenVibeJS_IsRunning() || !player )
- return;
-
- JSContext *ctx = g_OVServerJS.Context();
- JSValue ply = OVJS_NewPlayer( ctx, player );
-
- JSValueConst argv[] = { ply };
- g_OVServerJS.CallHookVoid( "PlayerInitialSpawn", 1, argv );
-
- JS_FreeValue( ctx, ply );
-}
-
-void OpenVibeJS_Server_PlayerSpawn( CHL2MP_Player *player )
-{
- OpenVibeJS_EnsureStarted();
-
- if ( !OpenVibeJS_IsRunning() || !player )
- return;
-
- JSContext *ctx = g_OVServerJS.Context();
- JSValue ply = OVJS_NewPlayer( ctx, player );
-
- JSValueConst argv[] = { ply };
- g_OVServerJS.CallHookVoid( "PlayerSpawn", 1, argv );
-
- JS_FreeValue( ctx, ply );
-}
-
-void OpenVibeJS_Server_PlayerDeath( CHL2MP_Player *victim, CBaseEntity *attacker, CBaseEntity *inflictor )
-{
- OpenVibeJS_EnsureStarted();
-
- if ( !OpenVibeJS_IsRunning() || !victim )
- return;
-
- JSContext *ctx = g_OVServerJS.Context();
-
- JSValue jsVictim = OVJS_NewPlayer( ctx, victim );
- JSValue jsAttacker = JS_NULL;
-
- if ( attacker && attacker->IsPlayer() )
- jsAttacker = OVJS_NewPlayer( ctx, ToHL2MPPlayer( static_cast<CBasePlayer *>( attacker ) ) );
-
- JSValueConst argv[] = { jsVictim, jsAttacker };
- g_OVServerJS.CallHookVoid( "PlayerDeath", 2, argv );
-
- JS_FreeValue( ctx, jsVictim );
- JS_FreeValue( ctx, jsAttacker );
-}
-
-void OpenVibeJS_Server_PlayerDisconnected( CHL2MP_Player *player )
-{
- OpenVibeJS_EnsureStarted();
-
- if ( !OpenVibeJS_IsRunning() || !player )
- return;
-
- JSContext *ctx = g_OVServerJS.Context();
- JSValue ply = OVJS_NewPlayer( ctx, player );
-
- JSValueConst argv[] = { ply };
- g_OVServerJS.CallHookVoid( "PlayerDisconnected", 1, argv );
-
- JS_FreeValue( ctx, ply );
-}
-
-bool OpenVibeJS_Server_PlayerSay( CHL2MP_Player *player, const char *text )
-{
- OpenVibeJS_EnsureStarted();
-
- if ( !OpenVibeJS_IsRunning() || !player || !text )
- return false;
-
- JSContext *ctx = g_OVServerJS.Context();
-
- JSValue ply = OVJS_NewPlayer( ctx, player );
- JSValue msg = JS_NewString( ctx, text );
-
- JSValueConst argv[] = { ply, msg };
-
- bool value = false;
- bool returned = g_OVServerJS.CallHookBool( "PlayerSay", &value, 2, argv );
-
- JS_FreeValue( ctx, ply );
- JS_FreeValue( ctx, msg );
-
- return returned && value == false;
-}
-
-static void OV_JSReload_f()
-{
- OpenVibeJS_ServerShutdown();
- OpenVibeJS_ServerInit();
- Msg( "[OV JS] reloaded\n" );
-}
-
-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=%d running=%d mode=%s\n",
- ov_js_enabled.GetBool() ? 1 : 0,
- g_OVServerJSStarted ? 1 : 0,
- OpenVibeJS_IsRunning() ? 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 )
-{
- if ( args.ArgC() < 2 )
- {
- Msg( "Usage: ov_js_fire <HookName>\n" );
- return;
- }
-
- OpenVibeJS_EnsureStarted();
-
- if ( !OpenVibeJS_IsRunning() )
- {
- Warning( "[OV JS] runtime is not running\n" );
- return;
- }
-
- g_OVServerJS.CallHookVoid( args[1] );
-}
-
-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 )
-{
- if ( args.ArgC() < 2 )
- {
- Msg( "Usage: ov_js_cmd <text>\n" );
- return;
- }
-
- OpenVibeJS_CallConsoleCommand( 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
-);
-CPP
-
-echo "[openvibe] canonical openvibe_js_server.cpp written"
-
-echo "[openvibe] removing stale legacy Node bridge remnants from openvibe_server.cpp"
-
-python3 <<'PY'
-from pathlib import Path
-import re
-
-p = Path("sdk/openvibe/server/hl2mp/openvibe_server.cpp")
-s = p.read_text()
-
-# Remove legacy COpenVibeJsBridge class, if present.
-s = re.sub(
- r'\n// ={10,}\n// OpenVibe\.JS - Scripting Engine C\+\+ Bridge\n// ={10,}\n\nclass COpenVibeJsBridge\b.*?\nstatic COpenVibeJsBridge g_OpenVibeJsBridge;\n',
- '\n',
- s,
- flags=re.S,
-)
-
-# More tolerant fallback if banner text differs.
-s = re.sub(
- r'\nclass COpenVibeJsBridge\b.*?\nstatic COpenVibeJsBridge g_OpenVibeJsBridge;\n',
- '\n',
- s,
- flags=re.S,
-)
-
-# Remove old Node-bridge ov_js_cmd command if it survived.
-s = re.sub(
- r'\nstatic void OV_JsCmd_f\s*\([^)]*\)\s*\{.*?\n\}\n\nstatic ConCommand ov_js_cmd\s*\(.*?\);\n',
- '\n',
- s,
- flags=re.S,
-)
-
-# Remove obvious unused Node/socket includes from our tracked patch file.
-for inc in [
- '#include <thread>\n',
- '#include <mutex>\n',
- '#include <queue>\n',
- '#include <string>\n',
- '#include <sstream>\n',
- '#include <sys/socket.h>\n',
- '#include <sys/un.h>\n',
- '#include <unistd.h>\n',
-]:
- s = s.replace(inc, '')
-
-# Ensure embedded JS include exists.
-if '#include "openvibe_js_server.h"' not in s:
- s = re.sub(
- r'(#include\s+"hl2mp_player\.h"\s*\n)',
- r'\1#include "openvibe_js_server.h"\n',
- s,
- count=1,
- )
-
-p.write_text(s)
-PY
-
-echo "[openvibe] removing duplicate ov_js_cmd from already-applied SDK file if present"
-
-SDK_SERVER_JS="$SDK/src/game/server/hl2mp/openvibe_js_server.cpp"
-if [[ -f "$SDK_SERVER_JS" ]]; then
- cp "$SDK_SERVER_JS" "$SDK_SERVER_JS.bak.$STAMP"
- cp sdk/openvibe/server/hl2mp/openvibe_js_server.cpp "$SDK_SERVER_JS"
-fi
-
-echo "[openvibe] ensure JS ConsoleCommand handlers are present"
-
-mkdir -p game/openvibe.games/js/gamemodes/base game/openvibe.games/js/gamemodes/hub
-
-python3 <<'PY'
-from pathlib import Path
-
-def ensure_console_handler(path: str, marker: str, handler_src: str):
- p = Path(path)
- s = p.read_text() if p.exists() else "const GM = {\n};\n\ngamemode.set(GM);\n"
- if marker in s:
- return
- idx = s.rfind("};")
- if idx == -1:
- s += "\n" + handler_src + "\n"
- else:
- before = s[:idx].rstrip()
- after = s[idx:]
- if before.endswith("{"):
- before = before + "\n" + handler_src.strip()
- else:
- before = before + ",\n\n" + handler_src.strip()
- s = before + "\n" + after
- p.write_text(s)
-
-ensure_console_handler(
- "game/openvibe.games/js/gamemodes/base/server.js",
- "Base ConsoleCommand",
- '''ConsoleCommand(text) {
- OV.log(`Base ConsoleCommand: ${text}`);
- }'''
-)
-
-ensure_console_handler(
- "game/openvibe.games/js/gamemodes/hub/server.js",
- "Hub ConsoleCommand",
- '''ConsoleCommand(text) {
- OV.log(`Hub ConsoleCommand: ${text}`);
- if (text === "smoke") {
- OV.broadcast("Embedded JS smoke command worked.");
- }
- }'''
-)
-PY
-
-echo "[openvibe] apply SDK patch"
-tools/apply-openvibe-sdk.sh
-
-echo "[openvibe] verify only one ov_js_cmd exists in SDK copied source"
-grep -n "ConCommand ov_js_cmd" "$SDK/src/game/server/hl2mp/openvibe_js_server.cpp" || true
-count="$(grep -c "ConCommand ov_js_cmd" "$SDK/src/game/server/hl2mp/openvibe_js_server.cpp" || true)"
-if [[ "$count" != "1" ]]; then
- echo "[openvibe] ERROR: expected exactly one ov_js_cmd, got $count" >&2
- exit 1
-fi
-
-echo "[openvibe] build SDK"
-tools/build-sdk-linux.sh 2>&1 | tee "$HOME/ov-build.log"
-
-echo "[openvibe] setup OpenVibe bin"
-if [[ -x tools/setup-openvibe-bin.sh ]]; then
- tools/setup-openvibe-bin.sh
-else
- echo "[openvibe] WARNING: tools/setup-openvibe-bin.sh not found/executable; skipping"
-fi
-
-echo
-echo "[openvibe] phase build complete."
-echo
-echo "Next runtime test:"
-echo " OPENVIBE_SRCDS_MAP_DELAY=3 tools/dev-up.sh"
-echo
-echo "Server console tests:"
-echo " ov_js_status"
-echo " ov_js_fire Initialize"
-echo " ov_js_cmd smoke"
-echo
-echo "Client tests:"
-echo " connect 127.0.0.1:27015"
-echo " say !js"
-echo " say !hp"
-echo " say !players"
deleted file mode 100755
deleted file mode 100755
@@ -1,245 +0,0 @@
@@ -1,245 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-say() { echo "[openvibe] $*"; }
-warn() { echo "[openvibe warn] $*" >&2; }
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-cd "$ROOT"
-BRANCH="$(git rev-parse --abbrev-ref HEAD)"
-WORKFLOW="windows-source-sdk-dlls.yml"
-
-say "fix GitHub repo slug + clean committed diagnostics + rerun Windows DLL build"
-say "root=$ROOT"
-say "branch=$BRANCH"
-
-mkdir -p tools game/openvibe.games/bin
-
-cat > tools/openvibe-gh-repo.sh <<'EOS'
-#!/usr/bin/env bash
-set -euo pipefail
-url="${1:-$(git config --get remote.origin.url)}"
-url="${url#[email protected]:}"
-url="${url#ssh://[email protected]/}"
-url="${url#https://github.com/}"
-url="${url#http://github.com/}"
-url="${url%.git}"
-url="${url%/}"
-if [[ ! "$url" =~ ^[^/]+/[^/]+$ ]]; then
- echo "Could not normalize GitHub repo from remote.origin.url: ${url}" >&2
- exit 1
-fi
-echo "$url"
-EOS
-chmod +x tools/openvibe-gh-repo.sh
-
-REPO="$(tools/openvibe-gh-repo.sh)"
-say "repo=$REPO"
-
-# Make sure debug outputs never get committed again.
-touch .gitignore
-python3 - <<'PY'
-from pathlib import Path
-p = Path('.gitignore')
-lines = p.read_text().splitlines() if p.exists() else []
-want = [
- '',
- '# OpenVibe local/generated build diagnostics',
- 'artifacts/',
- '_deps/',
- '*.tmp',
- '*.log',
-]
-existing = set(lines)
-changed = False
-for line in want:
- if line and line not in existing:
- lines.append(line)
- existing.add(line)
- changed = True
- elif line == '' and (not lines or lines[-1] != ''):
- lines.append(line)
- changed = True
-if changed:
- p.write_text('\n'.join(lines).rstrip() + '\n')
-PY
-
-# Remove previously committed workflow debug artifacts from git, but keep local files if present.
-if git ls-files artifacts | grep -q .; then
- say "removing committed artifacts/ from git index"
- git rm -r --cached artifacts >/dev/null || true
-fi
-
-cat > tools/windows-workflow-debug-and-install.sh <<'EOS'
-#!/usr/bin/env bash
-set -euo pipefail
-say() { echo "[openvibe] $*"; }
-warn() { echo "[openvibe warn] $*" >&2; }
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-cd "$ROOT"
-WORKFLOW="${OPENVIBE_WORKFLOW:-windows-source-sdk-dlls.yml}"
-BRANCH="${OPENVIBE_BRANCH:-$(git rev-parse --abbrev-ref HEAD)}"
-REPO="${OPENVIBE_REPO:-$(tools/openvibe-gh-repo.sh)}"
-RUN_ID="${1:-}"
-
-if ! command -v gh >/dev/null 2>&1; then
- echo "GitHub CLI gh is missing. Install/auth it first: sudo apt install gh && gh auth login" >&2
- exit 1
-fi
-
-if [[ -z "$RUN_ID" ]]; then
- RUN_ID="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"
-fi
-if [[ -z "$RUN_ID" || "$RUN_ID" == "null" ]]; then
- echo "Could not find a workflow run for $REPO $WORKFLOW on $BRANCH" >&2
- exit 1
-fi
-
-OUT="$ROOT/artifacts/windows-workflow-debug/run-${RUN_ID}-artifacts"
-rm -rf "$OUT"
-mkdir -p "$OUT"
-
-say "repo=$REPO"
-say "run=$RUN_ID"
-say "downloading logs/artifacts into $OUT"
-
-gh run view "$RUN_ID" --repo "$REPO" --log > "$OUT/full-run.log" 2>&1 || true
-gh run download "$RUN_ID" --repo "$REPO" --dir "$OUT" >/dev/null 2>&1 || true
-find "$OUT" -type f | sort > "$OUT/file-list.txt"
-
-say "likely errors:"
-if ! grep -RInE "error |error:|fatal:|failed|Exception|No patched|MSB[0-9]+|LNK[0-9]+|C[0-9]{4}" "$OUT" | head -n 80; then
- warn "no obvious errors found in downloaded logs"
-fi
-
-say "artifact files:"
-cat "$OUT/file-list.txt"
-
-CLIENT=""
-SERVER=""
-while IFS= read -r f; do
- case "$(basename "$f")" in
- client.dll) CLIENT="$f" ;;
- server.dll) SERVER="$f" ;;
- esac
-done < <(find "$OUT" -type f \( -iname 'client.dll' -o -iname 'server.dll' \) | sort)
-
-if [[ -n "$CLIENT" && -n "$SERVER" ]]; then
- say "candidate client=$CLIENT"
- say "candidate server=$SERVER"
- if strings "$CLIENT" | grep -Eq 'ov_join|ov_menu|OpenVibe'; then
- mkdir -p game/openvibe.games/bin
- cp -f "$CLIENT" game/openvibe.games/bin/client.dll
- cp -f "$SERVER" game/openvibe.games/bin/server.dll
- say "installed patched Windows DLLs"
- if [[ -x tools/verify-openvibe-dll-content.sh ]]; then
- tools/verify-openvibe-dll-content.sh || true
- else
- strings game/openvibe.games/bin/client.dll | grep -E 'ov_join|ov_menu|OpenVibe' | head || true
- fi
- exit 0
- else
- warn "DLLs were present but do not contain OpenVibe strings; not installing stale/stock DLLs"
- fi
-else
- warn "no client.dll/server.dll artifact available yet"
-fi
-
-say "next useful tails:"
-for f in \
- "$OUT/full-run.log" \
- "$OUT/openvibe-windows-build-debug/bootstrap-source-sdk-2013.log" \
- "$OUT/openvibe-windows-build-debug/bootstrap-curl-master-zip.log" \
- "$OUT/openvibe-windows-build-debug/bootstrap-git-clone-master.log" \
- "$OUT/openvibe-windows-build-debug/build-sdk-windows.log"; do
- if [[ -f "$f" ]]; then
- echo "----- $f -----"
- tail -n 80 "$f" || true
- fi
-done
-exit 2
-EOS
-chmod +x tools/windows-workflow-debug-and-install.sh
-
-cat > tools/trigger-windows-dll-build-clean.sh <<'EOS'
-#!/usr/bin/env bash
-set -euo pipefail
-say() { echo "[openvibe] $*"; }
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-cd "$ROOT"
-WORKFLOW="${OPENVIBE_WORKFLOW:-windows-source-sdk-dlls.yml}"
-BRANCH="${OPENVIBE_BRANCH:-$(git rev-parse --abbrev-ref HEAD)}"
-REPO="${OPENVIBE_REPO:-$(tools/openvibe-gh-repo.sh)}"
-
-if ! command -v gh >/dev/null 2>&1; then
- echo "GitHub CLI gh is missing. Install/auth it first: sudo apt install gh && gh auth login" >&2
- exit 1
-fi
-
-gh auth status >/dev/null
-say "repo=$REPO"
-say "workflow=$WORKFLOW"
-say "branch=$BRANCH"
-
-git push
-say "triggering workflow"
-gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$BRANCH" -f diagnostics=1
-
-say "waiting for new run to appear"
-sleep 8
-RUN_ID="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"
-say "watching run $RUN_ID"
-if gh run watch "$RUN_ID" --repo "$REPO" --exit-status; then
- say "workflow passed"
-else
- warn_msg="workflow failed; downloading diagnostics anyway"
- echo "[openvibe warn] $warn_msg" >&2
-fi
-
-tools/windows-workflow-debug-and-install.sh "$RUN_ID" || true
-EOS
-chmod +x tools/trigger-windows-dll-build-clean.sh
-
-# Try to patch older helper scripts so the .git suffix never breaks gh API calls again.
-for f in \
- tools/request-windows-dll-build.sh \
- tools/gh-windows-build-and-install.sh \
- tools/next-phase-platform-build-and-fetch.sh \
- tools/fix-windows-bootstrap-download-master-and-rerun.sh \
- tools/fix-windows-bootstrap-mp-branch-and-rerun.sh \
- tools/fix-windows-sdk-bootstrap-and-real-dlls.sh \
- tools/fix-windows-workflow-diagnostics-and-rerun.sh; do
- [[ -f "$f" ]] || continue
- python3 - "$f" <<'PY'
-from pathlib import Path
-import re, sys
-p = Path(sys.argv[1])
-s = p.read_text()
-# Add a harmless normalization immediately after simple REPO assignments derived from remote.origin.url.
-s = re.sub(r'(REPO="\$\([^\n]*remote\.origin\.url[^\n]*\)"\n)', r'\1REPO="${REPO%.git}"\n', s)
-s = re.sub(r"(REPO='\$\([^\n]*remote\.origin\.url[^\n]*\)'\n)", r"\1REPO=\"${REPO%.git}\"\n", s)
-# Also strip .git after any existing REPO assignment block if not already nearby.
-if 'REPO="${REPO%.git}"' not in s and 'gh workflow' in s:
- s = s.replace('WORKFLOW=', 'REPO="${REPO%.git}"\nWORKFLOW=', 1)
-p.write_text(s)
-PY
- chmod +x "$f" 2>/dev/null || true
-done
-
-say "git status after cleanup/patch"
-git status --short
-
-if ! git diff --quiet || ! git diff --cached --quiet; then
- git add .gitignore tools/openvibe-gh-repo.sh tools/windows-workflow-debug-and-install.sh tools/trigger-windows-dll-build-clean.sh tools/*.sh 2>/dev/null || true
- if git diff --cached --quiet; then
- say "nothing staged to commit"
- else
- git commit -m "Fix GitHub workflow repo slug and ignore diagnostics"
- fi
-else
- say "no cleanup changes to commit"
-fi
-
-git push
-say "running clean Windows DLL workflow trigger"
-tools/trigger-windows-dll-build-clean.sh
deleted file mode 100755
deleted file mode 100755
@@ -1,713 +0,0 @@
@@ -1,713 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"
-STAMP="$(date +%Y%m%d-%H%M%S)"
-
-cd "$ROOT"
-
-echo "[openvibe] fix JS global-scope redeclare and continue command/timer phase"
-echo "[openvibe] root=$ROOT"
-echo "[openvibe] sdk=$SDK"
-
-backup_file() {
- local file="$1"
- [[ -f "$file" ]] && cp "$file" "$file.bak.$STAMP"
-}
-
-mkdir -p \
- game/openvibe.games/js/core \
- game/openvibe.games/js/gamemodes/base \
- game/openvibe.games/js/gamemodes/hub \
- game/openvibe.games/js/gamemodes/prophunt \
- game/openvibe.games/js/gamemodes/deathrun \
- game/openvibe.games/js/gamemodes/fortwars \
- game/openvibe.games/js/gamemodes/traitortown \
- tools
-
-echo "[openvibe] writing canonical command.js"
-backup_file game/openvibe.games/js/core/command.js
-cat > game/openvibe.games/js/core/command.js <<'JS'
-(function () {
- const commands = new Map();
-
- function say(target, message) {
- const text = String(message);
- if (target && typeof target.chat === "function") {
- target.chat(text);
- } else {
- OV.log(text);
- }
- }
-
- function normalizeName(name) {
- return String(name || "").trim().replace(/^[!/]+/, "").toLowerCase();
- }
-
- function parse(text) {
- const raw = String(text || "").trim();
- const withoutPrefix = raw.replace(/^[!/]+/, "");
- const parts = withoutPrefix.length ? withoutPrefix.split(/\s+/) : [];
- const name = normalizeName(parts.shift() || "");
- return { raw, name, args: parts };
- }
-
- globalThis.command = {
- add(name, descriptionOrFn, maybeFn) {
- const key = normalizeName(name);
- if (!key) throw new Error("command.add requires command name");
-
- let description = "";
- let fn = maybeFn;
-
- if (typeof descriptionOrFn === "function") {
- fn = descriptionOrFn;
- } else {
- description = String(descriptionOrFn || "");
- }
-
- if (typeof fn !== "function") throw new Error(`command '${key}' requires function`);
- commands.set(key, { name: key, description, fn });
- },
-
- remove(name) {
- return commands.delete(normalizeName(name));
- },
-
- has(name) {
- return commands.has(normalizeName(name));
- },
-
- list() {
- return Array.from(commands.values()).map((entry) => ({
- name: entry.name,
- description: entry.description
- }));
- },
-
- run(source, text, ...extra) {
- const parsed = parse(text);
- if (!parsed.name) return undefined;
-
- const entry = commands.get(parsed.name);
- if (!entry) {
- say(source, `Unknown command: ${parsed.name}. Try !help.`);
- return source ? false : undefined;
- }
-
- try {
- const result = entry.fn(source, parsed.args, parsed.raw, ...extra);
- return result === undefined ? false : result;
- } catch (err) {
- OV.error(`[command:${parsed.name}] ${err && err.stack ? err.stack : err}`);
- say(source, `Command failed: ${parsed.name}`);
- return false;
- }
- }
- };
-
- hook.add("ConsoleCommand", "OpenVibeCommandConsole", function (text) {
- return command.run(null, text);
- });
-
- hook.add("PlayerSay", "OpenVibeCommandChat", function (ply, text) {
- const msg = String(text || "").trim();
- if (!msg.startsWith("!")) return undefined;
- return command.run(ply, msg.slice(1));
- });
-})();
-JS
-
-echo "[openvibe] writing canonical timer.js"
-backup_file game/openvibe.games/js/core/timer.js
-cat > game/openvibe.games/js/core/timer.js <<'JS'
-(function () {
- const timers = new Map();
- let nextId = 1;
-
- function now() {
- if (typeof OV !== "undefined" && typeof OV.time === "function") return Number(OV.time()) || 0;
- return Date.now() / 1000;
- }
-
- function makeId() {
- return `timer_${nextId++}`;
- }
-
- globalThis.timer = {
- create(id, delay, repetitions, fn) {
- if (typeof id !== "string" || !id) throw new Error("timer.create requires id");
- if (typeof fn !== "function") throw new Error("timer.create requires callback");
-
- const safeDelay = Math.max(0, Number(delay) || 0);
- const reps = repetitions === undefined ? 1 : Number(repetitions);
-
- timers.set(id, {
- id,
- delay: safeDelay,
- repetitions: reps,
- fn,
- next: now() + safeDelay,
- running: true
- });
-
- return id;
- },
-
- simple(delay, fn) {
- return timer.create(makeId(), delay, 1, fn);
- },
-
- remove(id) {
- return timers.delete(String(id));
- },
-
- exists(id) {
- return timers.has(String(id));
- },
-
- count() {
- return timers.size;
- },
-
- tick() {
- const t = now();
-
- for (const [id, item] of Array.from(timers.entries())) {
- if (!item.running || t < item.next) continue;
-
- try {
- item.fn();
- } catch (err) {
- OV.error(`[timer:${id}] ${err && err.stack ? err.stack : err}`);
- }
-
- if (item.repetitions > 0) item.repetitions -= 1;
-
- if (item.repetitions === 0) {
- timers.delete(id);
- } else {
- item.next = t + item.delay;
- }
- }
- },
-
- clear() {
- timers.clear();
- }
- };
-
- hook.add("Think", "OpenVibeTimerThink", function () {
- timer.tick();
- return undefined;
- });
-})();
-JS
-
-echo "[openvibe] writing scoped base gamemode"
-backup_file game/openvibe.games/js/gamemodes/base/server.js
-cat > game/openvibe.games/js/gamemodes/base/server.js <<'JS'
-(function () {
- function say(target, message) {
- if (target && typeof target.chat === "function") target.chat(String(message));
- else OV.log(String(message));
- }
-
- command.add("help", "List available OpenVibe commands.", function (source) {
- const names = command.list().map((entry) => `!${entry.name}`).sort().join(", ");
- say(source, `Commands: ${names}`);
- return false;
- });
-
- command.add("smoke", "Run embedded JS smoke test.", function (source) {
- const message = "OpenVibe embedded JS smoke test passed.";
- if (source) say(source, message);
- OV.broadcast(message);
- return false;
- });
-
- command.add("timer_smoke", "Run timer smoke test.", function (source) {
- timer.simple(0, function () {
- OV.broadcast("OpenVibe timer smoke fired.");
- });
- timer.tick();
- say(source, "Timer smoke scheduled.");
- return false;
- });
-
- const GM = {
- mode: "base",
- name: "OpenVibe Base",
-
- Initialize() {
- OV.log("Base Initialize fired");
- },
-
- MapInitialize(mapName) {
- OV.log(`Base MapInitialize: ${mapName}`);
- },
-
- PlayerInitialSpawn(ply) {
- ply.chat("Welcome to OpenVibe: Source.");
- },
-
- PlayerSpawn(_ply) {},
-
- PlayerDeath(_victim, _attacker) {},
-
- PlayerDisconnected(_ply) {},
-
- PlayerSay(ply, text) {
- const msg = String(text || "").trim();
- if (msg.startsWith("!")) return command.run(ply, msg.slice(1));
- return undefined;
- },
-
- ConsoleCommand(text) {
- return command.run(null, text);
- },
-
- Think() {
- timer.tick();
- }
- };
-
- gamemode.set(GM);
-})();
-JS
-
-echo "[openvibe] writing scoped hub gamemode"
-backup_file game/openvibe.games/js/gamemodes/hub/server.js
-cat > game/openvibe.games/js/gamemodes/hub/server.js <<'JS'
-(function () {
- command.add("hub_status", "Print hub runtime status.", function (source) {
- const message = `Hub mode online. Map=${OV.getMapName()} Players=${OV.players().length}`;
- if (source && typeof source.chat === "function") source.chat(message);
- else OV.log(message);
- return false;
- });
-
- command.add("where", "Show current map/mode.", function (source) {
- const message = `You are on ${OV.getMapName()} in ${OV.getMode()} mode.`;
- if (source && typeof source.chat === "function") source.chat(message);
- else OV.log(message);
- return false;
- });
-
- const GM = {
- mode: "hub",
- name: "OpenVibe Hub",
-
- Initialize() {
- OV.log("Hub Initialize fired");
- },
-
- MapInitialize(mapName) {
- OV.log(`Map initialized: ${mapName}`);
- },
-
- PlayerInitialSpawn(ply) {
- ply.chat("Welcome to OpenVibe: Source JS runtime.");
- OV.broadcast(`${ply.name()} joined the hub.`);
- },
-
- PlayerSpawn(_ply) {},
-
- PlayerSay(ply, text) {
- const msg = String(text || "").trim();
-
- if (msg === "!js") {
- ply.chat("JavaScript hooks are working.");
- return false;
- }
-
- if (msg === "!hp") {
- ply.chat(`Health: ${ply.health()}`);
- return false;
- }
-
- if (msg === "!players") {
- ply.chat(`Players online: ${OV.players().length}`);
- return false;
- }
-
- if (msg.startsWith("!")) return command.run(ply, msg.slice(1));
- return undefined;
- },
-
- ConsoleCommand(text) {
- OV.log(`Hub ConsoleCommand: ${text}`);
- return command.run(null, text);
- },
-
- Think() {
- timer.tick();
- }
- };
-
- gamemode.set(GM);
-})();
-JS
-
-echo "[openvibe] writing scoped prototype gamemodes"
-cat > game/openvibe.games/js/gamemodes/prophunt/server.js <<'JS'
-(function () {
- const props = ["crate", "barrel", "chair", "bucket"];
-
- function randomProp() {
- return props[Math.floor(Math.random() * props.length)];
- }
-
- command.add("prop", "Prop Hunt disguise command.", function (ply, args) {
- if (!ply) {
- OV.log("prop command requires player");
- return false;
- }
-
- const prop = args[0] || randomProp();
- ply.runCommand(`ov_prophunt_disguise ${prop}`);
- return false;
- });
-
- const GM = {
- mode: "prophunt",
- name: "OpenVibe Prop Hunt",
-
- Initialize() {
- OV.log("Prop Hunt Initialize fired");
- },
-
- PlayerInitialSpawn(ply) {
- ply.chat("Prop Hunt: hide as props or hunt them down.");
- },
-
- PlayerSay(ply, text) {
- const msg = String(text || "").trim();
- if (msg.startsWith("!")) return command.run(ply, msg.slice(1));
- return undefined;
- },
-
- Think() {
- timer.tick();
- }
- };
-
- gamemode.set(GM);
-})();
-JS
-
-cat > game/openvibe.games/js/gamemodes/deathrun/server.js <<'JS'
-(function () {
- command.add("finish", "Deathrun finish smoke command.", function (ply) {
- if (ply) ply.chat("Deathrun finish test.");
- return false;
- });
-
- const GM = {
- mode: "deathrun",
- name: "OpenVibe Deathrun",
-
- Initialize() {
- OV.log("Deathrun Initialize fired");
- },
-
- PlayerInitialSpawn(ply) {
- ply.chat("Deathrun: survive the traps and reach the finish.");
- },
-
- PlayerSay(ply, text) {
- const msg = String(text || "").trim();
- if (msg.startsWith("!")) return command.run(ply, msg.slice(1));
- return undefined;
- },
-
- Think() {
- timer.tick();
- }
- };
-
- gamemode.set(GM);
-})();
-JS
-
-cat > game/openvibe.games/js/gamemodes/fortwars/server.js <<'JS'
-(function () {
- const allowed = new Set(["crate", "barrel", "pallet", "fence", "sheet"]);
-
- command.add("build", "Fort Wars build command.", function (ply, args) {
- if (!ply) {
- OV.log("build command requires player");
- return false;
- }
-
- const part = args[0] || "crate";
- if (!allowed.has(part)) {
- ply.chat("Allowed: crate, barrel, pallet, fence, sheet");
- return false;
- }
-
- ply.runCommand(`ov_fortwars_spawn ${part}`);
- return false;
- });
-
- const GM = {
- mode: "fortwars",
- name: "OpenVibe Fort Wars",
-
- Initialize() {
- OV.log("Fort Wars Initialize fired");
- },
-
- PlayerInitialSpawn(ply) {
- ply.chat("Fort Wars: build first, fight second.");
- },
-
- PlayerSay(ply, text) {
- const msg = String(text || "").trim();
- if (msg.startsWith("!")) return command.run(ply, msg.slice(1));
- return undefined;
- },
-
- Think() {
- timer.tick();
- }
- };
-
- gamemode.set(GM);
-})();
-JS
-
-cat > game/openvibe.games/js/gamemodes/traitortown/server.js <<'JS'
-(function () {
- const roles = new Map();
-
- command.add("role", "Show your Traitor Town role.", function (ply) {
- if (!ply) {
- OV.log("role command requires player");
- return false;
- }
-
- ply.chat(`Your role: ${roles.get(ply.userId()) || "innocent"}`);
- return false;
- });
-
- const GM = {
- mode: "traitortown",
- name: "OpenVibe Traitor Town",
-
- Initialize() {
- OV.log("Traitor Town Initialize fired");
- },
-
- PlayerInitialSpawn(ply) {
- ply.chat("Traitor Town: find the traitors before they find you.");
- roles.set(ply.userId(), "innocent");
- },
-
- PlayerSay(ply, text) {
- const msg = String(text || "").trim();
- if (msg.startsWith("!")) return command.run(ply, msg.slice(1));
- return undefined;
- },
-
- PlayerDeath(victim, _attacker) {
- OV.broadcast(`${victim.name()} died.`);
- },
-
- Think() {
- timer.tick();
- }
- };
-
- gamemode.set(GM);
-})();
-JS
-
-echo "[openvibe] patching runtime core load order"
-backup_file sdk/openvibe/shared/ov_js_runtime.cpp
-python3 <<'PY'
-from pathlib import Path
-import re
-
-p = Path("sdk/openvibe/shared/ov_js_runtime.cpp")
-s = p.read_text()
-
-new_func = r'''bool COpenVibeJSRuntime::LoadCoreFiles()
-{
- if (!LoadFile("js/core/hook.js"))
- return false;
-
- if (!LoadFile("js/core/gamemode.js"))
- return false;
-
- if (!LoadFile("js/bridge.js"))
- return false;
-
- if (!LoadFile("js/core/command.js"))
- return false;
-
- if (!LoadFile("js/core/timer.js"))
- return false;
-
- return true;
-}'''
-
-s2 = re.sub(
- r'bool\s+COpenVibeJSRuntime::LoadCoreFiles\s*\(\s*\)\s*\{.*?\n\}',
- new_func,
- s,
- count=1,
- flags=re.S,
-)
-
-if s2 == s:
- raise SystemExit("Could not patch COpenVibeJSRuntime::LoadCoreFiles")
-
-p.write_text(s2)
-PY
-
-echo "[openvibe] writing robust Node JS core smoke test"
-backup_file tools/smoke-js-core-node.mjs
-cat > tools/smoke-js-core-node.mjs <<'MJS'
-import fs from "node:fs";
-import path from "node:path";
-import vm from "node:vm";
-
-const root = process.env.OPENVIBE_ROOT || process.cwd();
-let fakeTime = 0;
-const logs = [];
-const broadcasts = [];
-const chats = [];
-const serverCommands = [];
-
-const context = {
- console,
- globalThis: null,
- OV: {
- log: (msg) => logs.push(String(msg)),
- warn: (msg) => logs.push(`WARN ${String(msg)}`),
- error: (msg) => logs.push(`ERROR ${String(msg)}`),
- getMode: () => "hub",
- getMapName: () => "ov_hub",
- time: () => fakeTime,
- broadcast: (msg) => broadcasts.push(String(msg)),
- players: () => [mockPlayer],
- playerByUserId: (id) => Number(id) === 1 ? mockPlayer : null,
- serverCommand: (cmd) => serverCommands.push(String(cmd)),
- fireHook: (...args) => context.gamemode.call(...args),
- reward: () => undefined,
- endMatch: () => undefined
- }
-};
-
-const mockPlayer = {
- userId: () => 1,
- entIndex: () => 1,
- steamId: () => "STEAM_0:1:1",
- name: () => "SmokePlayer",
- health: () => 100,
- setHealth: () => undefined,
- team: () => 0,
- setTeam: () => undefined,
- chat: (msg) => chats.push(String(msg)),
- runCommand: (cmd) => serverCommands.push(String(cmd))
-};
-
-context.globalThis = context;
-vm.createContext(context);
-
-function load(relativePath) {
- const full = path.join(root, relativePath);
- const source = fs.readFileSync(full, "utf8");
- vm.runInContext(source, context, { filename: relativePath });
-}
-
-for (const file of [
- "game/openvibe.games/js/core/hook.js",
- "game/openvibe.games/js/core/gamemode.js",
- "game/openvibe.games/js/bridge.js",
- "game/openvibe.games/js/core/command.js",
- "game/openvibe.games/js/core/timer.js",
- "game/openvibe.games/js/gamemodes/base/server.js",
- "game/openvibe.games/js/gamemodes/hub/server.js"
-]) {
- load(file);
-}
-
-if (!context.hook) throw new Error("hook global missing");
-if (!context.gamemode) throw new Error("gamemode global missing");
-if (!context.command) throw new Error("command global missing");
-if (!context.timer) throw new Error("timer global missing");
-if (!context.gamemode.get()) throw new Error("gamemode was not set");
-
-context.gamemode.call("Initialize");
-context.gamemode.call("MapInitialize", "ov_hub");
-context.gamemode.call("ConsoleCommand", "help");
-context.gamemode.call("ConsoleCommand", "smoke");
-context.gamemode.call("ConsoleCommand", "timer_smoke");
-context.gamemode.call("ConsoleCommand", "hub_status");
-context.gamemode.call("PlayerInitialSpawn", mockPlayer);
-context.gamemode.call("PlayerSay", mockPlayer, "!help");
-context.gamemode.call("PlayerSay", mockPlayer, "!where");
-context.gamemode.call("PlayerSay", mockPlayer, "!js");
-context.gamemode.call("PlayerSay", mockPlayer, "!hp");
-context.gamemode.call("PlayerSay", mockPlayer, "!players");
-fakeTime += 1;
-context.gamemode.call("Think");
-context.timer.tick();
-
-const combined = [...logs, ...broadcasts, ...chats, ...serverCommands].join("\n");
-
-for (const expected of [
- "Hub Initialize fired",
- "OpenVibe embedded JS smoke test passed.",
- "OpenVibe timer smoke fired.",
- "JavaScript hooks are working.",
- "Health: 100",
- "Players online: 1",
- "You are on ov_hub in hub mode."
-]) {
- if (!combined.includes(expected)) {
- console.error(combined);
- throw new Error(`missing expected smoke output: ${expected}`);
- }
-}
-
-console.log("[openvibe-smoke] JS core command/timer smoke passed");
-console.log(`[openvibe-smoke] logs=${logs.length} broadcasts=${broadcasts.length} chats=${chats.length}`);
-MJS
-
-echo "[openvibe] run JS core smoke test"
-node tools/smoke-js-core-node.mjs
-
-echo "[openvibe] apply SDK patch"
-tools/apply-openvibe-sdk.sh
-
-echo "[openvibe] build SDK"
-tools/build-sdk-linux.sh 2>&1 | tee "$HOME/ov-build.log"
-
-echo "[openvibe] setup OpenVibe bin"
-tools/setup-openvibe-bin.sh
-
-echo
-printf '%s\n' "[openvibe] JS command/timer phase complete."
-printf '%s\n' ""
-printf '%s\n' "Runtime test:"
-printf '%s\n' " OPENVIBE_SRCDS_MAP_DELAY=3 tools/dev-up.sh"
-printf '%s\n' ""
-printf '%s\n' "Server console tests:"
-printf '%s\n' " ov_js_status"
-printf '%s\n' " ov_js_fire Initialize"
-printf '%s\n' " ov_js_cmd help"
-printf '%s\n' " ov_js_cmd smoke"
-printf '%s\n' " ov_js_cmd timer_smoke"
-printf '%s\n' " ov_js_cmd hub_status"
-printf '%s\n' ""
-printf '%s\n' "Client tests:"
-printf '%s\n' " connect 127.0.0.1:27015"
-printf '%s\n' " say !help"
-printf '%s\n' " say !js"
-printf '%s\n' " say !hp"
-printf '%s\n' " say !players"
-printf '%s\n' " say !where"
deleted file mode 100755
deleted file mode 100755
@@ -1,344 +0,0 @@
@@ -1,344 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-cd "$ROOT"
-
-echo "[openvibe] next phase: launcher-aware Source loading + client DLL reality guard"
-echo "[openvibe] root=$ROOT"
-
-backup() {
- local f="$1"
- [[ -f "$f" ]] && cp "$f" "$f.bak.$(date +%Y%m%d-%H%M%S)"
-}
-
-backup launcher/main.js
-backup launcher/preload.js
-backup launcher/renderer.js
-backup launcher/index.html
-backup launcher/styles.css
-backup tools/run-client-proton.sh
-
-mkdir -p docs game/openvibe.games/resource game/openvibe.games/cfg launcher/assets
-
-cat > docs/CLIENT_DLL_AND_LOADING.md <<'DOC'
-# OpenVibe client DLL + loading screen notes
-
-The Proton launcher runs Windows `hl2.exe`. That Windows executable will not load the Linux
-`game/openvibe.games/bin/linux64/client.so` produced by the current Source SDK Linux build.
-
-That means native client commands such as `ov_join`, `ov_menu`, `ov_ui`, and the in-game
-CEF/HTML menu only exist when a matching client DLL is actually loaded by the game client.
-Until we build a Windows `client.dll` or switch to a native Linux client path, the Electron
-launcher is the reliable custom Chromium shell.
-
-This phase keeps the Electron launcher visible during Source startup, shows a clear launch
-state, and focuses the Source window only after it has appeared and stayed stable for a few
-seconds. It also installs best-effort Source resource overrides for the classic loading dialog.
-DOC
-
-cat > game/openvibe.games/cfg/autoexec.cfg <<'CFG'
-// OpenVibe: Source client defaults
-con_enable 1
-developer 1
-con_timestamp 1
-cl_showfps 0
-
-// These are server-side commands unless a matching client DLL is loaded.
-echo "[OpenVibe] autoexec loaded. If ov_join/ov_menu are unknown, the custom client DLL is not loaded."
-CFG
-
-cat > game/openvibe.games/resource/LoadingDialog.res <<'RES'
-"Resource/LoadingDialog.res"
-{
- "LoadingDialog"
- {
- "ControlName" "Frame"
- "fieldName" "LoadingDialog"
- "xpos" "0"
- "ypos" "0"
- "wide" "f0"
- "tall" "f0"
- "autoResize" "0"
- "pinCorner" "0"
- "visible" "1"
- "enabled" "1"
- "tabPosition" "0"
- "settitlebarvisible" "0"
- "PaintBackgroundType" "2"
- "bgcolor_override" "7 8 14 255"
- }
-
- "OpenVibeTitle"
- {
- "ControlName" "Label"
- "fieldName" "OpenVibeTitle"
- "xpos" "c-260"
- "ypos" "c-72"
- "wide" "520"
- "tall" "42"
- "visible" "1"
- "enabled" "1"
- "labelText" "OPENVIBE: SOURCE"
- "textAlignment" "center"
- "font" "DefaultVeryLarge"
- "fgcolor_override" "0 220 255 255"
- }
-
- "OpenVibeStatus"
- {
- "ControlName" "Label"
- "fieldName" "OpenVibeStatus"
- "xpos" "c-300"
- "ypos" "c-20"
- "wide" "600"
- "tall" "36"
- "visible" "1"
- "enabled" "1"
- "labelText" "Loading OpenVibe shell, servers, and scripts..."
- "textAlignment" "center"
- "font" "DefaultLarge"
- "fgcolor_override" "220 235 255 255"
- }
-}
-RES
-
-cat > game/openvibe.games/resource/LoadingProgress.res <<'RES'
-"Resource/LoadingProgress.res"
-{
- "LoadingProgress"
- {
- "ControlName" "Frame"
- "fieldName" "LoadingProgress"
- "xpos" "0"
- "ypos" "0"
- "wide" "f0"
- "tall" "f0"
- "visible" "1"
- "enabled" "1"
- "settitlebarvisible" "0"
- "PaintBackgroundType" "2"
- "bgcolor_override" "7 8 14 255"
- }
-}
-RES
-
-python3 <<'PY'
-from pathlib import Path
-import re
-
-p = Path('launcher/main.js')
-s = p.read_text()
-
-if 'OPENVIBE_GAME_WINDOW_READY_TIMEOUT_MS' not in s:
- marker = "let appIsQuitting = false;\n"
- helpers = r'''
-
-const GAME_WINDOW_TITLE = process.env.OPENVIBE_GAME_WINDOW_TITLE || 'OpenVibe: Source';
-const GAME_READY_TIMEOUT_MS = Number(process.env.OPENVIBE_GAME_WINDOW_READY_TIMEOUT_MS || 45000);
-const GAME_STABLE_MS = Number(process.env.OPENVIBE_GAME_WINDOW_STABLE_MS || 8000);
-const HIDE_LAUNCHER_ON_GAME_READY = process.env.OPENVIBE_HIDE_LAUNCHER_ON_GAME_READY === '1';
-
-function sleep(ms) {
- return new Promise((resolve) => setTimeout(resolve, ms));
-}
-
-function emitLaunchPhase(phase, message, extra = {}) {
- if (mainWindow && !mainWindow.isDestroyed()) {
- mainWindow.webContents.send('game-launch-phase', { phase, message, ...extra });
- }
-}
-
-function safeExec(command) {
- try {
- return execSync(command, { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
- } catch {
- return '';
- }
-}
-
-function findGameWindowId() {
- // Linux desktop helper. If xdotool is not installed, the launcher simply stays visible.
- const escaped = GAME_WINDOW_TITLE.replace(/"/g, '\\"');
- const out = safeExec(`xdotool search --name "${escaped}" 2>/dev/null | tail -n 1`);
- return out || null;
-}
-
-function focusGameWindow() {
- const win = findGameWindowId();
- if (!win) return false;
- safeExec(`xdotool windowactivate ${win} 2>/dev/null || true`);
- return true;
-}
-
-async function waitForGameWindowReady(timeoutMs = GAME_READY_TIMEOUT_MS) {
- const started = Date.now();
- let firstSeen = 0;
-
- while (Date.now() - started < timeoutMs) {
- const win = findGameWindowId();
- if (win) {
- if (!firstSeen) {
- firstSeen = Date.now();
- emitLaunchPhase('window-found', 'Source window found. Waiting for it to stop thrashing before switching focus...', { windowId: win });
- }
-
- if (Date.now() - firstSeen >= GAME_STABLE_MS) {
- return { ready: true, windowId: win, reason: 'window-stable' };
- }
- }
-
- await sleep(500);
- }
-
- return { ready: false, windowId: findGameWindowId(), reason: 'timeout' };
-}
-'''
- s = s.replace(marker, marker + helpers)
-
-old = """ mainWindow?.webContents.send('game-started', gameProcess.pid);\n setTimeout(() => {\n if (gameProcess && mainWindow && !mainWindow.isDestroyed()) mainWindow.hide();\n }, 750);\n"""
-new = """ emitLaunchPhase(\n 'spawned',\n 'Source has started. OpenVibe will keep this launcher visible until the game window is detected and stable.'\n );\n mainWindow?.webContents.send('game-started', gameProcess.pid);\n\n waitForGameWindowReady().then((state) => {\n if (!gameProcess) return;\n\n if (state.ready) {\n emitLaunchPhase(\n 'ready',\n HIDE_LAUNCHER_ON_GAME_READY\n ? 'Source window looks ready. Focusing game and hiding launcher.'\n : 'Source window looks ready. Launcher will stay open so you are not stuck behind a frozen loading screen.',\n state\n );\n focusGameWindow();\n\n if (HIDE_LAUNCHER_ON_GAME_READY) {\n if (mainWindow && !mainWindow.isDestroyed()) mainWindow.hide();\n } else {\n if (mainWindow && !mainWindow.isDestroyed()) {\n mainWindow.show();\n mainWindow.focus();\n }\n }\n } else {\n emitLaunchPhase(\n 'slow',\n 'Source is taking longer than expected. The launcher is staying visible; use Focus Game when the window appears.',\n state\n );\n if (mainWindow && !mainWindow.isDestroyed()) {\n mainWindow.show();\n mainWindow.focus();\n }\n }\n });\n"""
-if old in s:
- s = s.replace(old, new)
-elif "setTimeout(() =>" in s and "mainWindow.hide" in s:
- s = re.sub(r"\s*mainWindow\?\.webContents\.send\('game-started', gameProcess\.pid\);\s*setTimeout\(\(\) => \{\s*if \(gameProcess && mainWindow && !mainWindow\.isDestroyed\(\)\) mainWindow\.hide\(\);\s*\}, 750\);", "\n" + new, s, count=1, flags=re.S)
-
-if "ipcMain.handle('game:focus'" not in s:
- insert_after = "ipcMain.handle('game:status', () => ({\n running: gameProcess !== null,\n pid: gameProcess?.pid ?? null,\n}));\n"
- addition = """
-
-ipcMain.handle('game:focus', async () => {
- return focusGameWindow();
-});
-
-ipcMain.handle('launcher:show', async () => {
- if (mainWindow && !mainWindow.isDestroyed()) {
- mainWindow.show();
- mainWindow.focus();
- }
- return true;
-});
-"""
- if insert_after in s:
- s = s.replace(insert_after, insert_after + addition)
- else:
- s += addition
-
-p.write_text(s)
-
-p = Path('launcher/preload.js')
-s = p.read_text()
-if 'focusGame:' not in s:
- s = s.replace(" gameStatus: () => ipcRenderer.invoke('game:status'),", " gameStatus: () => ipcRenderer.invoke('game:status'),\n focusGame: () => ipcRenderer.invoke('game:focus'),")
-if 'showLauncher:' not in s:
- s = s.replace(" focusGame: () => ipcRenderer.invoke('game:focus'),", " focusGame: () => ipcRenderer.invoke('game:focus'),\n showLauncher: () => ipcRenderer.invoke('launcher:show'),")
-if 'onLaunchPhase:' not in s:
- s = s.replace(" onGameStart: (cb) => ipcRenderer.on('game-started', (_e, pid) => cb(pid)),", " onGameStart: (cb) => ipcRenderer.on('game-started', (_e, pid) => cb(pid)),\n onLaunchPhase: (cb) => ipcRenderer.on('game-launch-phase', (_e, info) => cb(info)),")
-p.write_text(s)
-
-p = Path('launcher/index.html')
-s = p.read_text()
-if 'launch-help' not in s:
- s = s.replace(
- '<div class="launch-label" id="launch-label">Launching…</div>\n <button class="launch-cancel" id="launch-cancel">Cancel</button>',
- '<div class="launch-label" id="launch-label">Launching…</div>\n <div class="launch-help" id="launch-help">OpenVibe will keep this launcher visible until Source is actually ready.</div>\n <div class="launch-actions">\n <button class="launch-cancel" id="launch-focus-game">Focus Game Window</button>\n <button class="launch-cancel" id="launch-cancel">Dismiss</button>\n </div>'
- )
-p.write_text(s)
-
-p = Path('launcher/styles.css')
-s = p.read_text()
-if '.launch-help' not in s:
- s += r'''
-
-/* OpenVibe launch guard */
-.launch-help {
- margin-top: 10px;
- max-width: 620px;
- color: rgba(220, 235, 255, 0.78);
- font-size: 14px;
- line-height: 1.35;
- text-align: center;
-}
-.launch-actions {
- display: flex;
- gap: 10px;
- justify-content: center;
- flex-wrap: wrap;
- margin-top: 18px;
-}
-'''
-p.write_text(s)
-
-p = Path('launcher/renderer.js')
-s = p.read_text()
-if 'focusGame: () =>' not in s:
- s = s.replace(" gameStatus: () => isElectron ? window.OV.gameStatus() : Promise.resolve({ running: true, pid: null }),", " gameStatus: () => isElectron ? window.OV.gameStatus() : Promise.resolve({ running: true, pid: null }),\n focusGame: () => isElectron ? window.OV.focusGame() : Promise.resolve(false),")
-
-if "launch-focus-game" not in s:
- s = s.replace("document.getElementById('launch-cancel')?.addEventListener('click', () => {\n launchOverlay.classList.remove('show');\n});", "document.getElementById('launch-cancel')?.addEventListener('click', () => {\n launchOverlay.classList.remove('show');\n});\n\ndocument.getElementById('launch-focus-game')?.addEventListener('click', async () => {\n const ok = await Bridge.focusGame();\n toast(ok ? 'Focused Source window.' : 'Source window not found yet.', !ok);\n});")
-
-if 'onLaunchPhase' not in s:
- s = s.replace(" window.OV.onGameStart?.(() => {\n updateGameStatus(true);\n });", " window.OV.onGameStart?.(() => {\n updateGameStatus(true);\n });\n window.OV.onLaunchPhase?.((info) => {\n const label = document.getElementById('launch-label');\n const help = document.getElementById('launch-help');\n if (label && info?.message) label.textContent = info.message;\n if (help) {\n if (info?.phase === 'ready') {\n help.textContent = 'The game window is available. Because the Proton client may not load the custom client DLL, keep this launcher open as the reliable OpenVibe UI.';\n } else if (info?.phase === 'slow') {\n help.textContent = 'The game may still be loading or frozen. The launcher is staying visible so you can retry, focus the game, or inspect logs.';\n } else {\n help.textContent = 'OpenVibe is waiting for the Source window to appear and stabilize before switching focus.';\n }\n }\n launchOverlay?.classList.add('show');\n });")
-p.write_text(s)
-PY
-
-python3 <<'PY'
-from pathlib import Path
-p = Path('tools/run-client-proton.sh')
-s = p.read_text()
-if '-condebug' not in s:
- s = s.replace('-console -dev -novid -sw -w 1280 -h 720 \\\n', '-console -condebug -dev -novid -sw -w 1280 -h 720 \\\n')
-if '+con_enable 1' not in s:
- s = s.replace('-port 27115 -clientport 27105 \\\n', '-port 27115 -clientport 27105 +con_enable 1 +developer 1 \\\n')
-if 'client.so' not in s and 'Windows hl2.exe will not load Linux client.so' not in s:
- s = s.replace('echo "Launching OpenVibe: Source..."', 'echo "Launching OpenVibe: Source..."\necho "NOTE: Proton Windows hl2.exe will not load Linux client.so; Electron launcher remains the reliable custom UI."')
-p.write_text(s)
-PY
-
-cat > tools/check-openvibe-client-mode.sh <<'CHK'
-#!/usr/bin/env bash
-set -euo pipefail
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-
-echo "[openvibe] client mode diagnostic"
-echo "Windows/Proton launcher: $ROOT/tools/run-client-proton.sh"
-echo "Linux client module expected at: $ROOT/game/openvibe.games/bin/linux64/client.so"
-
-if [[ -f "$ROOT/game/openvibe.games/bin/linux64/client.so" ]]; then
- echo "[ok] Linux client.so exists"
-else
- echo "[warn] Linux client.so missing; run tools/build-sdk-linux.sh and tools/setup-openvibe-bin.sh"
-fi
-
-cat <<'MSG'
-
-If the in-game console says unknown command "ov_join" or "ov_menu", the OpenVibe client DLL is not loaded.
-That is expected when launching Windows hl2.exe through Proton while only Linux client.so exists.
-
-Current reliable UI path:
- - Electron launcher / local Chromium UI
- - server-side GameDLL commands
-
-Full in-game HTML menu requires one of:
- 1. Run a native Linux Source client that loads bin/linux64/client.so, or
- 2. Build a Windows client.dll/server.dll and place them in game/openvibe.games/bin/ for Proton hl2.exe.
-MSG
-CHK
-chmod +x tools/check-openvibe-client-mode.sh
-
-if [[ -x tools/setup-openvibe-bin.sh ]]; then
- tools/setup-openvibe-bin.sh || true
-fi
-
-echo
-node -c launcher/main.js
-node -c launcher/preload.js
-node -c launcher/renderer.js
-
-echo
-echo "[openvibe] done. Run diagnostics:"
-echo " tools/check-openvibe-client-mode.sh"
-echo
-echo "[openvibe] restart Electron/game. By default the launcher will stay visible."
-echo "[openvibe] To auto-hide only after Source window is stable:"
-echo " OPENVIBE_HIDE_LAUNCHER_ON_GAME_READY=1 npm run dev"
deleted file mode 100755
deleted file mode 100755
@@ -1,676 +0,0 @@
@@ -1,676 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"
-STAMP="$(date +%Y%m%d-%H%M%S)"
-
-cd "$ROOT"
-
-echo "[openvibe] fix LoadCoreFiles patch + finish JS command/timer phase"
-echo "[openvibe] root=$ROOT"
-echo "[openvibe] sdk=$SDK"
-
-backup_file() {
- local f="$1"
- [[ -f "$f" ]] && cp "$f" "$f.bak.$STAMP"
-}
-
-mkdir -p game/openvibe.games/js/core
-mkdir -p game/openvibe.games/js/gamemodes/base
-mkdir -p game/openvibe.games/js/gamemodes/hub
-mkdir -p game/openvibe.games/js/gamemodes/prophunt
-mkdir -p game/openvibe.games/js/gamemodes/deathrun
-mkdir -p game/openvibe.games/js/gamemodes/fortwars
-mkdir -p game/openvibe.games/js/gamemodes/traitortown
-mkdir -p tools
-
-echo "[openvibe] writing canonical JS core files"
-
-cat > game/openvibe.games/js/core/hook.js <<'JS'
-(function () {
- const buckets = Object.create(null);
-
- function ensure(name) {
- name = String(name || "");
- if (!buckets[name]) buckets[name] = [];
- return buckets[name];
- }
-
- globalThis.hook = {
- add(name, id, fn) {
- if (!name || !id || typeof fn !== "function") {
- throw new Error("hook.add(name, id, fn) requires a hook name, id, and function");
- }
-
- const list = ensure(name);
- const sid = String(id);
- for (let i = list.length - 1; i >= 0; --i) {
- if (list[i].id === sid) list.splice(i, 1);
- }
- list.push({ id: sid, fn });
- },
-
- remove(name, id) {
- const list = buckets[String(name || "")];
- if (!list) return;
- const sid = String(id || "");
- for (let i = list.length - 1; i >= 0; --i) {
- if (list[i].id === sid) list.splice(i, 1);
- }
- },
-
- run(name, ...args) {
- const list = buckets[String(name || "")];
- if (!list) return undefined;
-
- const snapshot = list.slice();
- for (const item of snapshot) {
- const result = item.fn(...args);
- if (result !== undefined) return result;
- }
-
- return undefined;
- },
-
- list(name) {
- if (name) return (buckets[String(name)] || []).map((item) => item.id);
- const out = Object.create(null);
- for (const key of Object.keys(buckets)) out[key] = buckets[key].map((item) => item.id);
- return out;
- }
- };
-})();
-JS
-
-cat > game/openvibe.games/js/core/gamemode.js <<'JS'
-(function () {
- let current = null;
-
- globalThis.gamemode = {
- set(gm) {
- if (!gm || typeof gm !== "object") throw new Error("gamemode.set(gm) requires an object");
- current = gm;
- return gm;
- },
-
- get() {
- return current;
- },
-
- call(name, ...args) {
- const hookResult = hook.run(name, ...args);
- if (hookResult !== undefined) return hookResult;
-
- if (current && typeof current[name] === "function") {
- return current[name](...args);
- }
-
- return undefined;
- }
- };
-})();
-JS
-
-cat > game/openvibe.games/js/bridge.js <<'JS'
-(function () {
- if (globalThis.OV && typeof OV.log === "function") {
- OV.log("bridge.js loaded");
- }
-})();
-JS
-
-cat > game/openvibe.games/js/core/command.js <<'JS'
-(function () {
- const commands = Object.create(null);
-
- function normalize(name) {
- return String(name || "").trim().replace(/^!+/, "").toLowerCase();
- }
-
- function split(text) {
- const raw = String(text || "").trim();
- if (!raw) return { name: "", args: [], raw: "" };
- const parts = raw.split(/\s+/g);
- const name = normalize(parts.shift());
- return { name, args: parts, raw };
- }
-
- function reply(ply, msg) {
- if (ply && typeof ply.chat === "function") ply.chat(String(msg));
- else if (globalThis.OV && typeof OV.broadcast === "function") OV.broadcast(String(msg));
- else if (globalThis.OV && typeof OV.log === "function") OV.log(String(msg));
- }
-
- function listNames() {
- return Object.keys(commands).sort();
- }
-
- globalThis.command = {
- add(name, helpOrFn, maybeFn) {
- const key = normalize(name);
- if (!key) throw new Error("command.add requires a command name");
-
- let help = "";
- let fn = helpOrFn;
- if (typeof helpOrFn === "string") {
- help = helpOrFn;
- fn = maybeFn;
- }
-
- if (typeof fn !== "function") throw new Error("command.add requires a function");
- commands[key] = { help, fn };
- },
-
- remove(name) {
- delete commands[normalize(name)];
- },
-
- has(name) {
- return !!commands[normalize(name)];
- },
-
- list: listNames,
-
- run(ply, text) {
- const parsed = split(text);
- if (!parsed.name) return undefined;
-
- const entry = commands[parsed.name];
- if (!entry) {
- reply(ply, `Unknown command: ${parsed.name}. Try !help`);
- return false;
- }
-
- const result = entry.fn({ ply, name: parsed.name, args: parsed.args, raw: parsed.raw, reply });
- return result === undefined ? false : result;
- },
-
- dispatchConsole(text) {
- return this.run(null, text);
- }
- };
-
- hook.add("PlayerSay", "OpenVibeCommandRegistry", function (ply, text) {
- const msg = String(text || "").trim();
- if (!msg.startsWith("!")) return undefined;
- return command.run(ply, msg.slice(1));
- });
-
- hook.add("ConsoleCommand", "OpenVibeConsoleCommandRegistry", function (text) {
- return command.dispatchConsole(text);
- });
-
- command.add("help", "List OpenVibe JS commands", function ({ reply, ply }) {
- reply(ply, `Commands: ${listNames().map((name) => `!${name}`).join(", ")}`);
- return false;
- });
-
- command.add("smoke", "Run the embedded JS smoke test", function ({ reply, ply }) {
- reply(ply, "OpenVibe embedded JS smoke test passed.");
- return false;
- });
-
- command.add("timer_smoke", "Run the JS timer smoke test", function ({ reply, ply }) {
- reply(ply, "Timer smoke scheduled.");
- if (globalThis.timer && typeof timer.simple === "function") {
- timer.simple(0.05, function () {
- if (globalThis.OV && typeof OV.broadcast === "function") {
- OV.broadcast("OpenVibe JS timer smoke fired.");
- }
- });
- }
- return false;
- });
-})();
-JS
-
-cat > game/openvibe.games/js/core/timer.js <<'JS'
-(function () {
- const timers = Object.create(null);
- let nextAutoId = 1;
-
- function now() {
- if (globalThis.OV && typeof OV.time === "function") return Number(OV.time()) || 0;
- return Date.now() / 1000;
- }
-
- function create(id, delay, reps, fn) {
- if (!id) id = `timer_${nextAutoId++}`;
- if (typeof fn !== "function") throw new Error("timer.create requires a callback");
-
- delay = Math.max(0, Number(delay) || 0);
- reps = Number(reps);
- if (!Number.isFinite(reps)) reps = 1;
-
- timers[String(id)] = {
- id: String(id),
- delay,
- reps,
- count: 0,
- next: now() + delay,
- fn
- };
-
- return String(id);
- }
-
- function remove(id) {
- delete timers[String(id || "")];
- }
-
- function tick() {
- const t = now();
- for (const id of Object.keys(timers)) {
- const item = timers[id];
- if (!item || t < item.next) continue;
-
- item.count += 1;
- try {
- item.fn(item.count, id);
- } catch (err) {
- if (globalThis.OV && typeof OV.error === "function") OV.error(`timer ${id}: ${err && err.message ? err.message : err}`);
- delete timers[id];
- continue;
- }
-
- if (item.reps > 0 && item.count >= item.reps) {
- delete timers[id];
- } else {
- item.next = t + item.delay;
- }
- }
- }
-
- globalThis.timer = {
- create,
- remove,
- exists(id) {
- return !!timers[String(id || "")];
- },
- simple(delay, fn) {
- return create(`simple_${nextAutoId++}`, delay, 1, fn);
- },
- tick,
- list() {
- return Object.keys(timers).sort();
- }
- };
-
- hook.add("Think", "OpenVibeTimerThink", tick);
-})();
-JS
-
-echo "[openvibe] writing scoped gamemode scripts"
-
-cat > game/openvibe.games/js/gamemodes/base/server.js <<'JS'
-(function () {
- const BaseServerGM = {
- mode: "base",
- name: "OpenVibe Base",
-
- Initialize() {
- OV.log("Base Initialize fired");
- },
-
- MapInitialize(mapName) {
- OV.log(`Base MapInitialize: ${mapName}`);
- },
-
- PlayerInitialSpawn(ply) {
- ply.chat("Welcome to OpenVibe: Source.");
- },
-
- PlayerSpawn(_ply) {},
- PlayerDeath(_victim, _attacker) {},
- PlayerDisconnected(_ply) {},
- Think() {}
- };
-
- gamemode.set(BaseServerGM);
-})();
-JS
-
-cat > game/openvibe.games/js/gamemodes/hub/server.js <<'JS'
-(function () {
- function registerHubCommands() {
- if (!globalThis.command) return;
-
- command.add("js", "Confirm JavaScript hooks are working", function ({ ply, reply }) {
- reply(ply, "JavaScript hooks are working.");
- return false;
- });
-
- command.add("hp", "Show current health", function ({ ply, reply }) {
- if (!ply) return false;
- reply(ply, `Health: ${ply.health()}`);
- return false;
- });
-
- command.add("players", "Show player count", function ({ ply, reply }) {
- reply(ply, `Players online: ${OV.players().length}`);
- return false;
- });
-
- command.add("where", "Show current mode and map", function ({ ply, reply }) {
- reply(ply, `Mode=${OV.getMode()} map=${OV.getMapName()}`);
- return false;
- });
-
- command.add("hub_status", "Broadcast hub status", function ({ ply, reply }) {
- reply(ply, `Hub OK. mode=${OV.getMode()} map=${OV.getMapName()} players=${OV.players().length}`);
- return false;
- });
- }
-
- const HubServerGM = {
- mode: "hub",
- name: "OpenVibe Hub",
-
- Initialize() {
- OV.log("Hub Initialize fired");
- registerHubCommands();
- },
-
- MapInitialize(mapName) {
- OV.log(`Hub MapInitialize: ${mapName}`);
- },
-
- PlayerInitialSpawn(ply) {
- ply.chat("Welcome to OpenVibe: Source JS runtime.");
- OV.broadcast(`${ply.name()} joined the hub.`);
- },
-
- PlayerSpawn(ply) {
- ply.chat("PlayerSpawn hook fired.");
- },
-
- PlayerSay(_ply, _text) {
- return undefined;
- },
-
- ConsoleCommand(text) {
- OV.log(`Hub ConsoleCommand fallback: ${text}`);
- return undefined;
- },
-
- Think() {}
- };
-
- gamemode.set(HubServerGM);
-})();
-JS
-
-cat > game/openvibe.games/js/gamemodes/prophunt/server.js <<'JS'
-(function () {
- function registerCommands() {
- if (!globalThis.command) return;
- command.add("ph_status", "Show Prop Hunt status", function ({ ply, reply }) {
- reply(ply, `Prop Hunt JS online. map=${OV.getMapName()} players=${OV.players().length}`);
- return false;
- });
- command.add("disguise", "Disguise as an allowlisted prop", function ({ args, ply, reply }) {
- const choice = args[0] || "crate";
- if (!ply) return false;
- ply.runCommand(`ov_prophunt_disguise ${choice}`);
- reply(ply, `Trying prop disguise: ${choice}`);
- return false;
- });
- }
-
- const PropHuntGM = {
- mode: "prophunt",
- name: "OpenVibe Prop Hunt",
- Initialize() { OV.log("Prop Hunt Initialize fired"); registerCommands(); },
- PlayerInitialSpawn(ply) { ply.chat("Prop Hunt JS loaded. Try !ph_status or !disguise crate"); },
- Think() {}
- };
- gamemode.set(PropHuntGM);
-})();
-JS
-
-cat > game/openvibe.games/js/gamemodes/deathrun/server.js <<'JS'
-(function () {
- function registerCommands() {
- if (!globalThis.command) return;
- command.add("dr_status", "Show Deathrun status", function ({ ply, reply }) {
- reply(ply, `Deathrun JS online. map=${OV.getMapName()} players=${OV.players().length}`);
- return false;
- });
- }
-
- const DeathrunGM = {
- mode: "deathrun",
- name: "OpenVibe Deathrun",
- Initialize() { OV.log("Deathrun Initialize fired"); registerCommands(); },
- PlayerInitialSpawn(ply) { ply.chat("Deathrun JS loaded. Try !dr_status"); },
- Think() {}
- };
- gamemode.set(DeathrunGM);
-})();
-JS
-
-cat > game/openvibe.games/js/gamemodes/fortwars/server.js <<'JS'
-(function () {
- function registerCommands() {
- if (!globalThis.command) return;
- command.add("fw_status", "Show Fort Wars status", function ({ ply, reply }) {
- reply(ply, `Fort Wars JS online. map=${OV.getMapName()} players=${OV.players().length}`);
- return false;
- });
- command.add("build", "Spawn an allowlisted Fort Wars prop", function ({ args, ply, reply }) {
- const choice = args[0] || "crate";
- if (!ply) return false;
- ply.runCommand(`ov_fortwars_spawn ${choice}`);
- reply(ply, `Trying Fort Wars prop: ${choice}`);
- return false;
- });
- }
-
- const FortWarsGM = {
- mode: "fortwars",
- name: "OpenVibe Fort Wars",
- Initialize() { OV.log("Fort Wars Initialize fired"); registerCommands(); },
- PlayerInitialSpawn(ply) { ply.chat("Fort Wars JS loaded. Try !fw_status or !build crate"); },
- Think() {}
- };
- gamemode.set(FortWarsGM);
-})();
-JS
-
-cat > game/openvibe.games/js/gamemodes/traitortown/server.js <<'JS'
-(function () {
- function registerCommands() {
- if (!globalThis.command) return;
- command.add("ttt_status", "Show Traitor Town status", function ({ ply, reply }) {
- reply(ply, `Traitor Town JS online. map=${OV.getMapName()} players=${OV.players().length}`);
- return false;
- });
- }
-
- const TraitorTownGM = {
- mode: "traitortown",
- name: "OpenVibe Traitor Town",
- Initialize() { OV.log("Traitor Town Initialize fired"); registerCommands(); },
- PlayerInitialSpawn(ply) { ply.chat("Traitor Town JS loaded. Try !ttt_status"); },
- Think() {}
- };
- gamemode.set(TraitorTownGM);
-})();
-JS
-
-echo "[openvibe] robustly replacing COpenVibeJSRuntime::LoadCoreFiles"
-
-backup_file sdk/openvibe/shared/ov_js_runtime.cpp
-
-python3 <<'PY'
-from pathlib import Path
-
-p = Path("sdk/openvibe/shared/ov_js_runtime.cpp")
-s = p.read_text()
-
-sig = "bool COpenVibeJSRuntime::LoadCoreFiles()"
-idx = s.find(sig)
-if idx < 0:
- raise SystemExit("Could not find COpenVibeJSRuntime::LoadCoreFiles signature")
-
-brace = s.find("{", idx)
-if brace < 0:
- raise SystemExit("Could not find COpenVibeJSRuntime::LoadCoreFiles opening brace")
-
-depth = 0
-end = None
-for i in range(brace, len(s)):
- ch = s[i]
- if ch == "{":
- depth += 1
- elif ch == "}":
- depth -= 1
- if depth == 0:
- end = i + 1
- break
-
-if end is None:
- raise SystemExit("Could not find COpenVibeJSRuntime::LoadCoreFiles closing brace")
-
-replacement = '''bool COpenVibeJSRuntime::LoadCoreFiles()
-{
- if (!LoadFile("js/core/hook.js"))
- return false;
-
- if (!LoadFile("js/core/gamemode.js"))
- return false;
-
- if (!LoadFile("js/bridge.js"))
- return false;
-
- if (!LoadFile("js/core/command.js"))
- return false;
-
- if (!LoadFile("js/core/timer.js"))
- return false;
-
- return true;
-}'''
-
-s = s[:idx] + replacement + s[end:]
-p.write_text(s)
-print("[openvibe] LoadCoreFiles replaced")
-PY
-
-echo "[openvibe] writing Node JS smoke test"
-
-cat > tools/smoke-js-core-node.mjs <<'JS'
-import fs from "node:fs";
-import vm from "node:vm";
-
-let fakeTime = 0;
-const messages = [];
-
-function player() {
- return {
- userId: 1,
- entIndex: 1,
- steamId: () => "STEAM_0:1:1",
- name: () => "SmokePlayer",
- health: () => 87,
- setHealth: () => undefined,
- chat: (msg) => messages.push(`[chat] ${msg}`),
- team: () => 0,
- setTeam: () => undefined,
- runCommand: (cmd) => messages.push(`[runCommand] ${cmd}`)
- };
-}
-
-const context = vm.createContext({
- console,
- globalThis: {},
- OV: {
- log: (msg) => messages.push(`[log] ${msg}`),
- warn: (msg) => messages.push(`[warn] ${msg}`),
- error: (msg) => messages.push(`[error] ${msg}`),
- getMode: () => "hub",
- getMapName: () => "ov_hub",
- time: () => fakeTime,
- broadcast: (msg) => messages.push(`[broadcast] ${msg}`),
- players: () => [player()],
- playerByUserId: () => player(),
- serverCommand: (cmd) => messages.push(`[serverCommand] ${cmd}`),
- reward: () => undefined,
- endMatch: () => undefined
- }
-});
-context.globalThis = context;
-
-const files = [
- "game/openvibe.games/js/core/hook.js",
- "game/openvibe.games/js/core/gamemode.js",
- "game/openvibe.games/js/bridge.js",
- "game/openvibe.games/js/core/command.js",
- "game/openvibe.games/js/core/timer.js",
- "game/openvibe.games/js/gamemodes/base/server.js",
- "game/openvibe.games/js/gamemodes/hub/server.js"
-];
-
-for (const file of files) {
- const code = fs.readFileSync(file, "utf8");
- vm.runInContext(code, context, { filename: file });
-}
-
-context.gamemode.call("Initialize");
-context.gamemode.call("MapInitialize", "ov_hub");
-context.gamemode.call("PlayerInitialSpawn", player());
-
-const blocked = context.gamemode.call("PlayerSay", player(), "!js");
-if (blocked !== false) throw new Error("!js did not block default chat");
-
-context.gamemode.call("ConsoleCommand", "smoke");
-context.gamemode.call("ConsoleCommand", "hub_status");
-context.gamemode.call("ConsoleCommand", "timer_smoke");
-fakeTime += 1;
-context.gamemode.call("Think");
-
-if (!messages.some((line) => line.includes("JavaScript hooks are working"))) throw new Error("missing !js response");
-if (!messages.some((line) => line.includes("OpenVibe embedded JS smoke test passed"))) throw new Error("missing smoke response");
-if (!messages.some((line) => line.includes("OpenVibe JS timer smoke fired"))) throw new Error("missing timer smoke response");
-
-console.log(messages.join("\n"));
-console.log("[openvibe-smoke] JS command/timer smoke passed");
-JS
-
-echo "[openvibe] run JS smoke test"
-node tools/smoke-js-core-node.mjs
-
-echo "[openvibe] apply SDK patch"
-tools/apply-openvibe-sdk.sh
-
-echo "[openvibe] build SDK"
-if [[ "${RUN_BUILD:-1}" = "1" ]]; then
- tools/build-sdk-linux.sh 2>&1 | tee "$HOME/ov-build.log"
-fi
-
-echo "[openvibe] setup OpenVibe bin"
-tools/setup-openvibe-bin.sh
-
-echo
-
-echo "[openvibe] JS command/timer phase complete."
-echo
-
-echo "Runtime test:"
-echo " OPENVIBE_SRCDS_MAP_DELAY=3 tools/dev-up.sh"
-echo
-echo "Server console tests:"
-echo " ov_js_status"
-echo " ov_js_cmd help"
-echo " ov_js_cmd smoke"
-echo " ov_js_cmd timer_smoke"
-echo " ov_js_cmd hub_status"
-echo
-echo "Client tests:"
-echo " connect 127.0.0.1:27015"
-echo " say !help"
-echo " say !js"
-echo " say !hp"
-echo " say !players"
-echo " say !where"
deleted file mode 100755
deleted file mode 100755
@@ -1,396 +0,0 @@
@@ -1,396 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-cd "$ROOT"
-
-echo "[openvibe] next phase: Proton-aware loading UX + fallback menu aliases"
-echo "[openvibe] root=$ROOT"
-
-mkdir -p game/openvibe.games/cfg game/openvibe.games/resource game/openvibe.games/materialsrc/console game/openvibe.games/materials/console launcher
-
-# -----------------------------------------------------------------------------
-# 1) Proton/client fallback aliases.
-# Windows hl2.exe cannot load the Linux client.so, so these aliases prevent
-# confusing Unknown command spam and keep raw connect flows usable.
-# -----------------------------------------------------------------------------
-cat > game/openvibe.games/cfg/openvibe_proton_client.cfg <<'CFG'
-// OpenVibe Proton client fallback aliases.
-// This file is executed by tools/run-client-proton.sh.
-// Proton Windows hl2.exe does not load Linux bin/linux64/client.so, so the
-// native OpenVibe client DLL commands are not available in this mode.
-
-echo "[OpenVibe] Proton fallback cfg loaded. Electron is the custom menu in Proton mode."
-
-alias ov_help "echo OpenVibe Proton fallback: use ov_join_hub / ov_join_prophunt / ov_join_deathrun / ov_join_fortwars / ov_join_traitortown. Electron remains the full HTML/CSS/JS menu."
-alias ov_menu "echo OpenVibe HTML menu is hosted by Electron in Proton mode. Alt-tab to the launcher or run npm run dev."
-alias ov_ui "ov_menu"
-alias ov_main_menu "ov_menu"
-alias ov_menu_main "ov_menu"
-alias ov_menu_servers "echo Server browser is in Electron. Quick connects: ov_join_hub, ov_join_prophunt, ov_join_deathrun, ov_join_fortwars, ov_join_traitortown."
-alias ov_menu_leaderboard "echo Leaderboard UI is in Electron in Proton mode."
-alias ov_menu_inventory "echo Inventory UI is in Electron in Proton mode."
-alias ov_menu_shop "echo Shop UI is in Electron in Proton mode."
-alias ov_menu_settings "echo Settings UI is in Electron in Proton mode."
-alias ov_menu_reload "echo Reload the Electron window with Ctrl+R or restart npm run dev."
-alias ov_menu_close "echo Close/minimize the Electron launcher window."
-
-alias ov_join "echo Usage in Proton fallback: ov_join_hub / ov_join_prophunt / ov_join_deathrun / ov_join_fortwars / ov_join_traitortown. The argument-taking ov_join command needs OpenVibe client.dll/client.so."
-alias ov_join_hub "connect 127.0.0.1:27015"
-alias ov_join_prophunt "connect 127.0.0.1:27016"
-alias ov_join_deathrun "connect 127.0.0.1:27017"
-alias ov_join_fortwars "connect 127.0.0.1:27018"
-alias ov_join_traitortown "connect 127.0.0.1:27019"
-
-alias ov_js_status "cmd ov_js_status"
-alias ov_js_reload "cmd ov_js_reload"
-alias ov_js_cmd "cmd ov_js_cmd"
-alias ov_js_fire "cmd ov_js_fire"
-
-alias ov_prophunt_disguise "cmd ov_prophunt_disguise"
-alias ov_prophunt_reset_disguise "cmd ov_prophunt_reset_disguise"
-alias ov_fortwars_spawn "cmd ov_fortwars_spawn"
-CFG
-
-cat > game/openvibe.games/cfg/autoexec.cfg <<'CFG'
-// OpenVibe autoexec. Safe in both native and Proton modes.
-exec openvibe_proton_client.cfg
-CFG
-
-# Patch Proton launcher to exec aliases early.
-python3 <<'PY'
-from pathlib import Path
-p = Path('tools/run-client-proton.sh')
-s = p.read_text()
-if '+exec openvibe_proton_client.cfg' not in s:
- s = s.replace(
- '-console -dev -novid -sw -w 1280 -h 720 \\\n',
- '-console -dev -novid -sw -w 1280 -h 720 +exec openvibe_proton_client.cfg \\\n'
- )
-if 'OPENVIBE_PROTON_FALLBACK' not in s:
- s = s.replace('export PROTON_LOG=0\n', 'export PROTON_LOG=0\nexport OPENVIBE_PROTON_FALLBACK=1\n')
-p.write_text(s)
-PY
-chmod +x tools/run-client-proton.sh
-
-# -----------------------------------------------------------------------------
-# 2) Best-effort loading dialog override resources.
-# This won't replace the HL2 background material until VTF files are supplied,
-# but it customizes text/layout and adds docs + source art placeholders.
-# -----------------------------------------------------------------------------
-cat > game/openvibe.games/resource/LoadingDialog.res <<'RES'
-"Resource/LoadingDialog.res"
-{
- "LoadingDialog"
- {
- "ControlName" "Frame"
- "fieldName" "LoadingDialog"
- "xpos" "0"
- "ypos" "0"
- "wide" "f0"
- "tall" "f0"
- "visible" "1"
- "enabled" "1"
- "title" "OpenVibe: Source"
- "proportionaltoparent" "1"
- "PaintBackgroundType" "2"
- "bgcolor_override" "7 8 14 245"
- }
- "LoadingLabel"
- {
- "ControlName" "Label"
- "fieldName" "LoadingLabel"
- "xpos" "cs-260"
- "ypos" "rs1-92"
- "wide" "520"
- "tall" "28"
- "visible" "1"
- "enabled" "1"
- "labelText" "OPENVIBE: SOURCE"
- "textAlignment" "center"
- "font" "DefaultLarge"
- "fgcolor_override" "0 216 255 255"
- }
- "ProgressLabel"
- {
- "ControlName" "Label"
- "fieldName" "ProgressLabel"
- "xpos" "cs-260"
- "ypos" "rs1-58"
- "wide" "520"
- "tall" "24"
- "visible" "1"
- "enabled" "1"
- "labelText" "Loading OpenVibe session..."
- "textAlignment" "center"
- "font" "Default"
- "fgcolor_override" "230 236 255 255"
- }
-}
-RES
-
-cat > game/openvibe.games/materialsrc/console/openvibe-loading.svg <<'SVG'
-<svg xmlns="http://www.w3.org/2000/svg" width="1920" height="1080" viewBox="0 0 1920 1080">
- <defs>
- <radialGradient id="g" cx="50%" cy="45%" r="75%">
- <stop offset="0" stop-color="#16334a"/>
- <stop offset="0.45" stop-color="#0b1020"/>
- <stop offset="1" stop-color="#05060b"/>
- </radialGradient>
- <linearGradient id="line" x1="0" x2="1">
- <stop offset="0" stop-color="#00d8ff" stop-opacity="0"/>
- <stop offset="0.5" stop-color="#00d8ff" stop-opacity="1"/>
- <stop offset="1" stop-color="#a855f7" stop-opacity="0"/>
- </linearGradient>
- </defs>
- <rect width="1920" height="1080" fill="url(#g)"/>
- <g opacity="0.18" stroke="#00d8ff" fill="none">
- <path d="M0 770 C300 630 520 890 850 730 C1190 560 1390 810 1920 610"/>
- <path d="M0 830 C330 700 560 960 910 780 C1240 610 1480 860 1920 680"/>
- </g>
- <circle cx="960" cy="450" r="155" fill="none" stroke="#00d8ff" stroke-width="8" opacity="0.8"/>
- <circle cx="960" cy="450" r="112" fill="none" stroke="#a855f7" stroke-width="4" opacity="0.7"/>
- <text x="960" y="465" text-anchor="middle" font-family="Orbitron, Rajdhani, sans-serif" font-size="84" fill="#eaf8ff" font-weight="900">OPENVIBE</text>
- <text x="960" y="530" text-anchor="middle" font-family="Rajdhani, sans-serif" font-size="38" fill="#00d8ff" font-weight="700">SOURCE</text>
- <rect x="660" y="620" width="600" height="6" rx="3" fill="url(#line)"/>
- <text x="960" y="680" text-anchor="middle" font-family="Inter, sans-serif" font-size="28" fill="#b9c7e8">Loading connected game session...</text>
-</svg>
-SVG
-
-cat > game/openvibe.games/materials/console/README.md <<'MD'
-# OpenVibe loading background
-
-Source's full-screen loading background is a VTF material, normally:
-
-- `materials/console/background01.vtf`
-- `materials/console/background01_widescreen.vtf`
-
-This repo includes source art at:
-
-- `materialsrc/console/openvibe-loading.svg`
-
-To fully replace the HL2 loading image, convert it to the two VTF filenames above using VTFEdit/VTFCmd or Valve's `vtex`, then place the VTFs in this folder.
-
-Until those VTF files exist, `resource/LoadingDialog.res` can restyle text/layout, but the old HL2 image may still appear.
-MD
-
-# -----------------------------------------------------------------------------
-# 3) Electron launch UX: keep launcher visible, expose focus button, and only
-# hide when Source window is stable if env var opts in.
-# -----------------------------------------------------------------------------
-python3 <<'PY'
-from pathlib import Path
-p = Path('launcher/main.js')
-s = p.read_text()
-
-if 'function sourceWindowVisible' not in s:
- insert_after = "let appIsQuitting = false;\n"
- block = r'''
-let lastLaunchState = { phase: 'idle', message: 'Ready', pid: null };
-
-function broadcastLaunchState(state) {
- lastLaunchState = { ...lastLaunchState, ...state, at: Date.now() };
- if (mainWindow && !mainWindow.isDestroyed()) {
- mainWindow.webContents.send('launch-state', lastLaunchState);
- }
-}
-
-function sourceWindowVisible() {
- try {
- const out = execSync('wmctrl -lx 2>/dev/null || true', { encoding: 'utf8' });
- return /OpenVibe: Source|Source SDK Base 2013|hl2\.exe|hl2/i.test(out);
- } catch {
- return false;
- }
-}
-
-function focusSourceWindow() {
- const commands = [
- "wmctrl -a 'OpenVibe: Source'",
- "wmctrl -a 'Source SDK Base 2013'",
- "wmctrl -a 'hl2.exe'",
- ];
- for (const cmd of commands) {
- try {
- execSync(cmd, { stdio: 'ignore' });
- return true;
- } catch {}
- }
- return false;
-}
-
-function waitForStableSourceWindow(timeoutMs = 60000) {
- const started = Date.now();
- let stableTicks = 0;
- return new Promise((resolve) => {
- const tick = () => {
- if (sourceWindowVisible()) stableTicks += 1;
- else stableTicks = 0;
-
- broadcastLaunchState({
- phase: stableTicks > 0 ? 'window-detected' : 'starting',
- message: stableTicks > 0
- ? `Source window detected (${stableTicks}/5 stable checks)...`
- : 'Starting Source through Proton...',
- });
-
- if (stableTicks >= 5) return resolve(true);
- if (Date.now() - started >= timeoutMs) return resolve(false);
- setTimeout(tick, 1000);
- };
- tick();
- });
-}
-'''
- s = s.replace(insert_after, insert_after + block + "\n")
-
-old = """ mainWindow?.webContents.send('game-started', gameProcess.pid);\n setTimeout(() => {\n if (gameProcess && mainWindow && !mainWindow.isDestroyed()) mainWindow.hide();\n }, 750);\n"""
-new = """ mainWindow?.webContents.send('game-started', gameProcess.pid);\n broadcastLaunchState({\n phase: 'spawned',\n pid: gameProcess.pid,\n message: 'Source spawned. Keeping launcher visible until the game window is stable.',\n });\n\n waitForStableSourceWindow().then((ready) => {\n broadcastLaunchState({\n phase: ready ? 'ready' : 'timeout',\n pid: gameProcess ? gameProcess.pid : null,\n message: ready\n ? 'Source window appears stable. You can focus the game now.'\n : 'Source is still not confirmed stable. Launcher stayed visible to avoid confusion.',\n });\n\n const shouldAutoHide = process.env.OPENVIBE_HIDE_LAUNCHER_ON_GAME_READY === '1';\n if (ready && shouldAutoHide && gameProcess && mainWindow && !mainWindow.isDestroyed()) {\n mainWindow.hide();\n focusSourceWindow();\n }\n });\n"""
-if old in s:
- s = s.replace(old, new)
-
-if "ipcMain.handle('game:focus'" not in s:
- s = s.replace("ipcMain.handle('game:status', () => ({\n running: gameProcess !== null,\n pid: gameProcess?.pid ?? null,\n}));\n",
- "ipcMain.handle('game:status', () => ({\n running: gameProcess !== null,\n pid: gameProcess?.pid ?? null,\n launchState: lastLaunchState,\n}));\n\nipcMain.handle('game:focus', async () => focusSourceWindow());\n")
-
-p.write_text(s)
-PY
-
-python3 <<'PY'
-from pathlib import Path
-p = Path('launcher/preload.js')
-s = p.read_text()
-if "focusGame" not in s:
- s = s.replace("gameStatus: () => ipcRenderer.invoke('game:status'),",
- "gameStatus: () => ipcRenderer.invoke('game:status'),\n focusGame: () => ipcRenderer.invoke('game:focus'),")
-if "onLaunchState" not in s:
- s = s.replace("onGameStart: (cb) => ipcRenderer.on('game-started', (_e, pid) => cb(pid)),",
- "onGameStart: (cb) => ipcRenderer.on('game-started', (_e, pid) => cb(pid)),\n onLaunchState: (cb) => ipcRenderer.on('launch-state', (_e, state) => cb(state)),")
-p.write_text(s)
-PY
-
-python3 <<'PY'
-from pathlib import Path
-p = Path('launcher/renderer.js')
-s = p.read_text()
-if 'focusGame:' not in s:
- s = s.replace("gameStatus: () => isElectron ? window.OV.gameStatus() : Promise.resolve({ running: true, pid: null }),",
- "gameStatus: () => isElectron ? window.OV.gameStatus() : Promise.resolve({ running: true, pid: null }),\n focusGame: () => isElectron ? window.OV.focusGame() : Promise.resolve(false),")
-if 'launch-focus' not in s:
- s = s.replace("document.getElementById('launch-cancel')?.addEventListener('click', () => {\n launchOverlay.classList.remove('show');\n});",
- "document.getElementById('launch-cancel')?.addEventListener('click', () => {\n launchOverlay.classList.remove('show');\n});\n\ndocument.getElementById('launch-focus')?.addEventListener('click', async () => {\n const ok = await Bridge.focusGame();\n toast(ok ? 'Focused Source window.' : 'Could not find Source window yet.', !ok);\n});")
-if 'onLaunchState' not in s:
- s += r'''
-
-// Launcher-aware Source startup status. This keeps Electron useful while Proton
-// shows the default Source loading window and before a native client DLL exists.
-if (isElectron && window.OV.onLaunchState) {
- window.OV.onLaunchState((state) => {
- const label = document.getElementById('launch-label');
- const sub = document.getElementById('launch-sub');
- if (label && state?.message) label.textContent = state.message;
- if (sub) {
- sub.textContent = state?.phase === 'ready'
- ? 'Game window is ready. Use Focus Game Window, or keep Electron open as the custom menu.'
- : 'Electron remains open so you are not left staring at a frozen/default Source loading screen.';
- }
- if (state?.phase && state.phase !== 'idle') launchOverlay?.classList.add('show');
- });
-}
-'''
-p.write_text(s)
-PY
-
-python3 <<'PY'
-from pathlib import Path
-p = Path('launcher/index.html')
-s = p.read_text()
-if 'id="launch-focus"' not in s:
- s = s.replace(' <button class="launch-cancel" id="launch-cancel">Cancel</button>\n',
- ' <button class="launch-cancel" id="launch-cancel">Cancel</button>\n <button class="launch-focus" id="launch-focus">Focus Game Window</button>\n <div class="launch-sub" id="launch-sub">Electron stays visible until Source is stable.</div>\n')
-p.write_text(s)
-PY
-
-cat >> launcher/styles.css <<'CSS'
-
-/* OpenVibe launcher-aware Source loading UX */
-.launch-focus {
- margin-top: 10px;
- border: 1px solid rgba(0, 216, 255, 0.55);
- background: rgba(0, 216, 255, 0.14);
- color: #eaf8ff;
- border-radius: 10px;
- padding: 9px 16px;
- font-weight: 700;
- cursor: pointer;
-}
-.launch-focus:hover {
- background: rgba(0, 216, 255, 0.22);
-}
-.launch-sub {
- margin-top: 10px;
- max-width: 560px;
- color: rgba(230, 236, 255, 0.76);
- font-size: 13px;
- line-height: 1.35;
- text-align: center;
-}
-CSS
-
-# -----------------------------------------------------------------------------
-# 4) Diagnostic.
-# -----------------------------------------------------------------------------
-cat > tools/check-openvibe-client-mode.sh <<'SH'
-#!/usr/bin/env bash
-set -euo pipefail
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-cd "$ROOT"
-
-echo "[openvibe] client mode diagnostic"
-echo "Windows/Proton launcher: $ROOT/tools/run-client-proton.sh"
-echo "Linux client module: $ROOT/game/openvibe.games/bin/linux64/client.so"
-echo "Fallback aliases: $ROOT/game/openvibe.games/cfg/openvibe_proton_client.cfg"
-echo
-[[ -f game/openvibe.games/bin/linux64/client.so ]] && echo "[ok] Linux client.so exists" || echo "[missing] Linux client.so missing"
-[[ -f game/openvibe.games/cfg/openvibe_proton_client.cfg ]] && echo "[ok] Proton fallback aliases exist" || echo "[missing] Proton fallback aliases missing"
-
-echo
-cat <<'TXT'
-Important:
- Proton Windows hl2.exe loads Windows client.dll, not Linux bin/linux64/client.so.
- If the console says unknown command ov_join/ov_menu, the OpenVibe client DLL is not loaded.
-
-Current reliable custom UI:
- Electron launcher + local Chromium UI.
-
-Current in-game fallback:
- autoexec.cfg loads openvibe_proton_client.cfg aliases.
- Try: ov_help, ov_join_hub, ov_join_prophunt, ov_join_deathrun.
-
-Full in-game HTML/CSS/JS menu requires:
- 1) native Linux Source client loading bin/linux64/client.so, or
- 2) a Windows client.dll build for Proton hl2.exe.
-
-Full loading image replacement:
- Convert materialsrc/console/openvibe-loading.svg to:
- materials/console/background01.vtf
- materials/console/background01_widescreen.vtf
-TXT
-SH
-chmod +x tools/check-openvibe-client-mode.sh
-
-# Keep setup links current, if build exists.
-if [[ -x tools/setup-openvibe-bin.sh ]]; then
- tools/setup-openvibe-bin.sh || true
-fi
-
-echo
-echo "[openvibe] done. Re-test:"
-echo " tools/check-openvibe-client-mode.sh"
-echo " cd launcher && npm run dev"
-echo
-echo "In Proton client console, try:"
-echo " exec openvibe_proton_client.cfg"
-echo " ov_help"
-echo " ov_join_hub"
-echo " ov_menu_servers"
deleted file mode 100755
deleted file mode 100755
@@ -1,146 +0,0 @@
@@ -1,146 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"
-
-cd "$ROOT"
-
-echo "[openvibe] replacing tools/build-quickjs-lib.sh"
-
-cat > tools/build-quickjs-lib.sh <<'BUILDQJS'
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"
-
-SRC_QJS="$ROOT/sdk/openvibe/third_party/quickjs"
-SDK_QJS="$SDK/src/game/shared/openvibe/third_party/quickjs"
-OUT="$SDK_QJS/build"
-
-if [[ ! -f "$SRC_QJS/quickjs.c" ]]; then
- echo "Missing $SRC_QJS/quickjs.c" >&2
- echo "Run: tools/vendor-quickjs.sh" >&2
- exit 1
-fi
-
-mkdir -p "$SDK_QJS"
-
-# Important: exclude build/ so rsync does not delete our object output dir.
-rsync -a --delete --exclude='build/' "$SRC_QJS/" "$SDK_QJS/"
-
-mkdir -p "$OUT"
-
-sources=(
- quickjs.c
- libregexp.c
- libunicode.c
- cutils.c
-)
-
-[[ -f "$SDK_QJS/dtoa.c" ]] && sources+=(dtoa.c)
-[[ -f "$SDK_QJS/libbf.c" ]] && sources+=(libbf.c)
-
-rm -f "$OUT"/*.o "$OUT"/libquickjs_openvibe.a
-
-for src in "${sources[@]}"; do
- obj="$OUT/${src%.c}.o"
- echo "[openvibe-qjs] cc $src -> $obj"
-
- cc \
- -std=gnu11 \
- -O2 \
- -fPIC \
- -D_GNU_SOURCE \
- -DCONFIG_VERSION=\"openvibe\" \
- -I"$SDK_QJS" \
- -c "$SDK_QJS/$src" \
- -o "$obj"
-done
-
-ar rcs "$OUT/libquickjs_openvibe.a" "$OUT"/*.o
-
-echo "[openvibe-qjs] built $OUT/libquickjs_openvibe.a"
-ls -lh "$OUT/libquickjs_openvibe.a"
-BUILDQJS
-
-chmod +x tools/build-quickjs-lib.sh
-
-echo "[openvibe] removing direct QuickJS .c files from server_hl2mp.vpc"
-
-SERVER_VPC="$SDK/src/game/server/server_hl2mp.vpc"
-
-if [[ -f "$SERVER_VPC" ]]; then
- cp "$SERVER_VPC" "$SERVER_VPC.bak.$(date +%Y%m%d-%H%M%S)"
-
- perl -0pi -e '
- s/^.*quickjs\\quickjs\.c.*\n//mg;
- s/^.*quickjs\\libregexp\.c.*\n//mg;
- s/^.*quickjs\\libunicode\.c.*\n//mg;
- s/^.*quickjs\\cutils\.c.*\n//mg;
- s/^.*quickjs\\dtoa\.c.*\n//mg;
- s/^.*quickjs\\libbf\.c.*\n//mg;
- ' "$SERVER_VPC"
-
- if ! grep -q 'libquickjs_openvibe.a' "$SERVER_VPC"; then
- perl -0pi -e 's/(\$File\s+"hl2mp\\openvibe_js_server\.cpp"\n)/$1\t\t\t\$Lib\t"..\\shared\\openvibe\\third_party\\quickjs\\build\\libquickjs_openvibe.a"\n/s' "$SERVER_VPC"
- fi
-fi
-
-echo "[openvibe] patching apply-openvibe-sdk.sh so future applies keep this fix"
-
-python3 <<'PY'
-from pathlib import Path
-import re
-
-p = Path("tools/apply-openvibe-sdk.sh")
-s = p.read_text()
-
-# Make sure apply script calls static library builder after copying QuickJS.
-if '"$ROOT/tools/build-quickjs-lib.sh"' not in s:
- needle = 'copy_tree "$ROOT/sdk/openvibe/third_party/quickjs" \\\n "$SDK/src/game/shared/openvibe/third_party/quickjs"'
- if needle in s:
- s = s.replace(needle, needle + '\n\n"$ROOT/tools/build-quickjs-lib.sh"')
- else:
- s += '\n\n"$ROOT/tools/build-quickjs-lib.sh"\n'
-
-# Add a strong cleanup block near the end.
-marker = "OPENVIBE_REMOVE_DIRECT_QUICKJS_C"
-if marker not in s:
- s += r'''
-
-# OPENVIBE_REMOVE_DIRECT_QUICKJS_C
-if [[ -f "$SERVER_VPC" ]]; then
- perl -0pi -e '
- s/^.*quickjs\\quickjs\.c.*\n//mg;
- s/^.*quickjs\\libregexp\.c.*\n//mg;
- s/^.*quickjs\\libunicode\.c.*\n//mg;
- s/^.*quickjs\\cutils\.c.*\n//mg;
- s/^.*quickjs\\dtoa\.c.*\n//mg;
- s/^.*quickjs\\libbf\.c.*\n//mg;
- ' "$SERVER_VPC"
-
- if ! grep -q 'libquickjs_openvibe.a' "$SERVER_VPC"; then
- perl -0pi -e 's/(\$File\s+"hl2mp\\openvibe_js_server\.cpp"\n)/$1\t\t\t\$Lib\t"..\\shared\\openvibe\\third_party\\quickjs\\build\\libquickjs_openvibe.a"\n/s' "$SERVER_VPC"
- fi
-fi
-'''
-
-p.write_text(s)
-PY
-
-echo "[openvibe] building QuickJS static lib"
-tools/build-quickjs-lib.sh
-
-echo
-echo "[openvibe] checking VPC for bad direct QuickJS C entries"
-if [[ -f "$SERVER_VPC" ]]; then
- grep -nE 'quickjs\\(quickjs|libregexp|libunicode|cutils|dtoa|libbf)\.c' "$SERVER_VPC" || true
- grep -n 'libquickjs_openvibe.a' "$SERVER_VPC" || true
-fi
-
-echo
-echo "[openvibe] fixed. Now run:"
-echo " tools/apply-openvibe-sdk.sh"
-echo " tools/build-sdk-linux.sh 2>&1 | tee ~/ov-build.log"
deleted file mode 100755
deleted file mode 100755
@@ -1,174 +0,0 @@
@@ -1,174 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"
-STAMP="$(date +%Y%m%d-%H%M%S)"
-
-cd "$ROOT"
-
-backup_file() {
- local file="$1"
- [[ -f "$file" ]] && cp "$file" "$file.bak.$STAMP"
-}
-
-echo "[openvibe] writing QuickJS static library builder"
-
-cat > tools/build-quickjs-lib.sh <<'BUILDQJS'
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"
-
-SRC_QJS="$ROOT/sdk/openvibe/third_party/quickjs"
-SDK_QJS="$SDK/src/game/shared/openvibe/third_party/quickjs"
-OUT="$SDK_QJS/build"
-
-mkdir -p "$OUT"
-
-if [[ ! -f "$SRC_QJS/quickjs.c" ]]; then
- echo "Missing $SRC_QJS/quickjs.c" >&2
- echo "Run: tools/vendor-quickjs.sh" >&2
- exit 1
-fi
-
-# Make sure SDK copy exists. apply-openvibe-sdk.sh also does this,
-# but this makes the script usable directly.
-mkdir -p "$SDK_QJS"
-rsync -a --delete "$SRC_QJS/" "$SDK_QJS/"
-
-sources=(
- quickjs.c
- libregexp.c
- libunicode.c
- cutils.c
-)
-
-[[ -f "$SDK_QJS/dtoa.c" ]] && sources+=(dtoa.c)
-[[ -f "$SDK_QJS/libbf.c" ]] && sources+=(libbf.c)
-
-rm -f "$OUT"/*.o "$OUT"/libquickjs_openvibe.a
-
-for src in "${sources[@]}"; do
- obj="$OUT/${src%.c}.o"
- echo "[openvibe-qjs] cc $src"
- cc \
- -std=gnu11 \
- -O2 \
- -fPIC \
- -D_GNU_SOURCE \
- -DCONFIG_VERSION=\"openvibe\" \
- -I"$SDK_QJS" \
- -c "$SDK_QJS/$src" \
- -o "$obj"
-done
-
-ar rcs "$OUT/libquickjs_openvibe.a" "$OUT"/*.o
-
-echo "[openvibe-qjs] built $OUT/libquickjs_openvibe.a"
-ls -lh "$OUT/libquickjs_openvibe.a"
-BUILDQJS
-
-chmod +x tools/build-quickjs-lib.sh
-
-echo "[openvibe] patching apply-openvibe-sdk.sh to link libquickjs_openvibe.a instead of compiling QuickJS C files"
-
-backup_file tools/apply-openvibe-sdk.sh
-
-python3 <<'PY'
-from pathlib import Path
-import re
-
-p = Path("tools/apply-openvibe-sdk.sh")
-s = p.read_text()
-
-# Remove QuickJS .c cleanup patterns from perl patch block if they exist.
-for name in ["quickjs", "libregexp", "libunicode", "cutils", "dtoa", "libbf"]:
- s = re.sub(
- rf'\s*s/\^\.\*openvibe\\\\third_party\\\\quickjs\\\\{name}\\\.c\.\*\\n//mg;\n?',
- '\n',
- s,
- )
- s = re.sub(
- rf'\s*s/\^\.\*openvibe\\\\\\\\third_party\\\\\\\\quickjs\\\\\\\\{name}\\\\\.c\.\*\\n//mg;\n?',
- '\n',
- s,
- )
-
-# Remove inserted QuickJS .c $File entries from replacement text.
-for name in ["quickjs", "libregexp", "libunicode", "cutils", "dtoa", "libbf"]:
- s = s.replace(
- f'\\n\\t\\t\\t\\$File\\t"..\\\\shared\\\\openvibe\\\\third_party\\\\quickjs\\\\{name}.c"',
- ''
- )
- s = s.replace(
- f'\n\t\t\t\\$File\t"..\\\\shared\\\\openvibe\\\\third_party\\\\quickjs\\\\{name}.c"',
- ''
- )
- s = s.replace(
- f'\n\t\t\t$File\t"..\\\\shared\\\\openvibe\\\\third_party\\\\quickjs\\\\{name}.c"',
- ''
- )
-
-# Remove any actual already-written lines from the script text.
-s = re.sub(r'^.*quickjs\\\\(quickjs|libregexp|libunicode|cutils|dtoa|libbf)\.c.*\n', '', s, flags=re.M)
-s = re.sub(r'^.*quickjs\\(quickjs|libregexp|libunicode|cutils|dtoa|libbf)\.c.*\n', '', s, flags=re.M)
-
-# Ensure build-quickjs-lib is called after copy_tree.
-if "tools/build-quickjs-lib.sh" not in s:
- needle = 'copy_tree "$ROOT/sdk/openvibe/third_party/quickjs" \\\n "$SDK/src/game/shared/openvibe/third_party/quickjs"'
- replacement = needle + '\n\n"$ROOT/tools/build-quickjs-lib.sh"'
- if needle in s:
- s = s.replace(needle, replacement)
- else:
- s += '\n\n"$ROOT/tools/build-quickjs-lib.sh"\n'
-
-# Add static lib to server_hl2mp.vpc patch.
-# Try to insert a $Lib line near the openvibe source files, only once.
-lib_line = '\t\t\t$Lib\t"..\\\\shared\\\\openvibe\\\\third_party\\\\quickjs\\\\build\\\\libquickjs_openvibe.a"'
-if 'libquickjs_openvibe.a' not in s:
- # Add after openvibe_js_server.cpp if present in replacement text.
- s = s.replace(
- '\t\t\t\\$File\t"hl2mp\\\\openvibe_js_server.cpp"',
- '\t\t\t\\$File\t"hl2mp\\\\openvibe_js_server.cpp"\\n' + lib_line.replace('$', '\\$')
- )
- # If that escaped replacement did not hit, add a direct post-patch insertion block.
- if 'libquickjs_openvibe.a' not in s:
- s += r'''
-
-# Link prebuilt QuickJS C static library. QuickJS must be compiled as C, not C++.
-if ! grep -q 'libquickjs_openvibe.a' "$SERVER_VPC"; then
- perl -0pi -e 's/(\$File\s+"hl2mp\\openvibe_js_server\.cpp"\n)/$1\t\t\t\$Lib\t"..\\shared\\openvibe\\third_party\\quickjs\\build\\libquickjs_openvibe.a"\n/s' "$SERVER_VPC"
- echo "[openvibe-sdk] linked QuickJS static library"
-fi
-'''
-
-p.write_text(s)
-PY
-
-echo "[openvibe] also patching already-applied SDK VPC now, if it exists"
-
-SERVER_VPC="$SDK/src/game/server/server_hl2mp.vpc"
-
-if [[ -f "$SERVER_VPC" ]]; then
- backup_file "$SERVER_VPC"
-
- # Remove direct QuickJS C files from VPC.
- perl -0pi -e 's/^.*openvibe\\\\third_party\\\\quickjs\\\\(quickjs|libregexp|libunicode|cutils|dtoa|libbf)\.c.*\n//mg; s/^.*openvibe\\third_party\\quickjs\\(quickjs|libregexp|libunicode|cutils|dtoa|libbf)\.c.*\n//mg' "$SERVER_VPC"
-
- if ! grep -q 'libquickjs_openvibe.a' "$SERVER_VPC"; then
- perl -0pi -e 's/(\$File\s+"hl2mp\\openvibe_js_server\.cpp"\n)/$1\t\t\t\$Lib\t"..\\shared\\openvibe\\third_party\\quickjs\\build\\libquickjs_openvibe.a"\n/s' "$SERVER_VPC"
- fi
-fi
-
-echo "[openvibe] building QuickJS C static library"
-tools/build-quickjs-lib.sh
-
-echo
-echo "[openvibe] fixed QuickJS C/C++ build split."
-echo
-echo "Now run:"
-echo " tools/apply-openvibe-sdk.sh"
-echo " tools/build-sdk-linux.sh 2>&1 | tee ~/ov-build.log"
-echo " tail -160 ~/ov-build.log"
deleted file mode 100755
deleted file mode 100755
@@ -1,327 +0,0 @@
@@ -1,327 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-cd "$ROOT"
-BRANCH="$(git branch --show-current 2>/dev/null || echo codex/openvibe-next-steps)"
-
-say(){ echo "[openvibe] $*"; }
-
-say "fix QuickJS MSVC GNU builtins/attribute/pthread compat + rerun Windows DLL workflow"
-say "root=$ROOT"
-say "branch=$BRANCH"
-
-mkdir -p tools docs
-
-cat > tools/build-quickjs-lib-windows.ps1 <<'PS1'
-$ErrorActionPreference = 'Stop'
-
-$Root = if ($env:OPENVIBE_ROOT) { (Resolve-Path $env:OPENVIBE_ROOT).Path } else { (Resolve-Path (Join-Path $PSScriptRoot '..')).Path }
-$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root 'engine/source-sdk-2013' }
-
-$SrcQjs = Join-Path $Root 'sdk/openvibe/third_party/quickjs'
-$SdkQjs = Join-Path $Sdk 'src/game/shared/openvibe/third_party/quickjs'
-$Out = Join-Path $SdkQjs 'build'
-$LogDir = Join-Path $Root 'artifacts/windows-build-debug'
-
-function Say($m) { Write-Host "[openvibe-qjs-win] $m" }
-
-if (!(Test-Path (Join-Path $SrcQjs 'quickjs.c'))) {
- throw "Missing $SrcQjs/quickjs.c. Run tools/vendor-quickjs.sh first."
-}
-if (!(Get-Command cl.exe -ErrorAction SilentlyContinue)) {
- throw "cl.exe not found. Run this from a Visual Studio Developer PowerShell or Developer Command Prompt."
-}
-if (!(Get-Command lib.exe -ErrorAction SilentlyContinue)) {
- throw "lib.exe not found. Run this from a Visual Studio Developer PowerShell or Developer Command Prompt."
-}
-
-New-Item -ItemType Directory -Force -Path $SdkQjs, $Out, $LogDir | Out-Null
-Copy-Item -Path (Join-Path $SrcQjs '*') -Destination $SdkQjs -Recurse -Force
-
-$CompatInclude = Join-Path $Out 'compat/include'
-$CompatSys = Join-Path $CompatInclude 'sys'
-New-Item -ItemType Directory -Force -Path $CompatInclude, $CompatSys | Out-Null
-
-# Upstream QuickJS is mostly GCC/clang oriented. MSVC needs a forced include for:
-# - GCC __attribute__((...)) syntax used in cutils.h
-# - GCC __builtin_* helpers used in cutils.h
-# - a few libc aliases
-$MsvcCompat = Join-Path $CompatInclude 'openvibe_qjs_msvc_compat.h'
-@'
-#ifndef OPENVIBE_QJS_MSVC_COMPAT_H
-#define OPENVIBE_QJS_MSVC_COMPAT_H
-
-#if defined(_MSC_VER)
-#ifndef WIN32_LEAN_AND_MEAN
-#define WIN32_LEAN_AND_MEAN
-#endif
-#ifndef NOMINMAX
-#define NOMINMAX
-#endif
-#include <stdint.h>
-#include <stdlib.h>
-#include <intrin.h>
-
-#ifndef __attribute__
-#define __attribute__(x)
-#endif
-#ifndef __builtin_expect
-#define __builtin_expect(x, y) (x)
-#endif
-
-static __inline int openvibe_qjs_clz32(unsigned int v)
-{
- unsigned long idx;
- if (_BitScanReverse(&idx, v)) return 31 - (int)idx;
- return 32;
-}
-
-static __inline int openvibe_qjs_ctz32(unsigned int v)
-{
- unsigned long idx;
- if (_BitScanForward(&idx, v)) return (int)idx;
- return 32;
-}
-
-static __inline int openvibe_qjs_clz64(uint64_t v)
-{
- uint32_t hi = (uint32_t)(v >> 32);
- uint32_t lo = (uint32_t)v;
- if (hi) return openvibe_qjs_clz32(hi);
- return 32 + openvibe_qjs_clz32(lo);
-}
-
-static __inline int openvibe_qjs_ctz64(uint64_t v)
-{
- uint32_t hi = (uint32_t)(v >> 32);
- uint32_t lo = (uint32_t)v;
- if (lo) return openvibe_qjs_ctz32(lo);
- return 32 + openvibe_qjs_ctz32(hi);
-}
-
-#ifndef __builtin_clz
-#define __builtin_clz(x) openvibe_qjs_clz32((unsigned int)(x))
-#endif
-#ifndef __builtin_ctz
-#define __builtin_ctz(x) openvibe_qjs_ctz32((unsigned int)(x))
-#endif
-#ifndef __builtin_clzll
-#define __builtin_clzll(x) openvibe_qjs_clz64((uint64_t)(x))
-#endif
-#ifndef __builtin_ctzll
-#define __builtin_ctzll(x) openvibe_qjs_ctz64((uint64_t)(x))
-#endif
-
-#ifndef alloca
-#define alloca _alloca
-#endif
-#ifndef strdup
-#define strdup _strdup
-#endif
-
-#endif /* _MSC_VER */
-#endif /* OPENVIBE_QJS_MSVC_COMPAT_H */
-'@ | Set-Content -Encoding ascii $MsvcCompat
-
-# Upstream QuickJS includes <sys/time.h>. MSVC does not provide it.
-$TimeHeader = Join-Path $CompatSys 'time.h'
-@'
-#ifndef OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H
-#define OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H
-
-#if defined(_WIN32)
-#ifndef WIN32_LEAN_AND_MEAN
-#define WIN32_LEAN_AND_MEAN
-#endif
-#ifndef NOMINMAX
-#define NOMINMAX
-#endif
-#include <windows.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _TIMEVAL_DEFINED
-#define _TIMEVAL_DEFINED
-typedef long suseconds_t;
-struct timeval {
- long tv_sec;
- long tv_usec;
-};
-#endif
-
-static __inline int gettimeofday(struct timeval *tv, void *tz)
-{
- FILETIME ft;
- ULARGE_INTEGER uli;
- unsigned long long usec;
- const unsigned long long unix_epoch = 116444736000000000ULL;
- (void)tz;
- if (!tv) return -1;
- GetSystemTimeAsFileTime(&ft);
- uli.LowPart = ft.dwLowDateTime;
- uli.HighPart = ft.dwHighDateTime;
- usec = (uli.QuadPart - unix_epoch) / 10ULL;
- tv->tv_sec = (long)(usec / 1000000ULL);
- tv->tv_usec = (long)(usec % 1000000ULL);
- return 0;
-}
-
-#ifdef __cplusplus
-}
-#endif
-#else
-#include_next <sys/time.h>
-#endif
-
-#endif /* OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H */
-'@ | Set-Content -Encoding ascii $TimeHeader
-
-# Stub enough pthread API for accidental includes. CONFIG_ATOMICS is disabled below,
-# so this should not be exercised, but the header keeps MSVC moving if upstream includes it.
-$PThreadHeader = Join-Path $CompatInclude 'pthread.h'
-@'
-#ifndef OPENVIBE_QUICKJS_COMPAT_PTHREAD_H
-#define OPENVIBE_QUICKJS_COMPAT_PTHREAD_H
-#if defined(_WIN32)
-typedef int pthread_mutex_t;
-typedef int pthread_t;
-typedef int pthread_attr_t;
-#define PTHREAD_MUTEX_INITIALIZER 0
-static __inline int pthread_mutex_init(pthread_mutex_t *m, const void *a) { (void)a; if (m) *m = 0; return 0; }
-static __inline int pthread_mutex_destroy(pthread_mutex_t *m) { (void)m; return 0; }
-static __inline int pthread_mutex_lock(pthread_mutex_t *m) { (void)m; return 0; }
-static __inline int pthread_mutex_unlock(pthread_mutex_t *m) { (void)m; return 0; }
-static __inline int pthread_create(pthread_t *t, const pthread_attr_t *a, void *(*fn)(void *), void *arg) { (void)t; (void)a; (void)fn; (void)arg; return -1; }
-static __inline int pthread_join(pthread_t t, void **ret) { (void)t; if (ret) *ret = 0; return 0; }
-#else
-#include_next <pthread.h>
-#endif
-#endif /* OPENVIBE_QUICKJS_COMPAT_PTHREAD_H */
-'@ | Set-Content -Encoding ascii $PThreadHeader
-
-# Disable QuickJS atomics/workers for this embedded game runtime on MSVC.
-# That avoids needing a real pthread implementation and is fine for our scripts.
-$QuickJsC = Join-Path $SdkQjs 'quickjs.c'
-if (Test-Path $QuickJsC) {
- $q = Get-Content -Raw $QuickJsC
- $q2 = [regex]::Replace($q, '(?m)^\s*#\s*define\s+CONFIG_ATOMICS\s*$', '/* OpenVibe MSVC build: CONFIG_ATOMICS disabled */')
- if ($q2 -ne $q) {
- Set-Content -Encoding ascii $QuickJsC $q2
- Say 'disabled CONFIG_ATOMICS in SDK quickjs.c copy'
- } else {
- Say 'CONFIG_ATOMICS define not found in quickjs.c copy; pthread shim remains available'
- }
-}
-
-Say "compat include=$CompatInclude"
-Say "compat forced include=$MsvcCompat"
-Say "compat sys/time.h=$TimeHeader"
-Say "compat pthread.h=$PThreadHeader"
-
-$sources = @('quickjs.c','libregexp.c','libunicode.c','cutils.c')
-if (Test-Path (Join-Path $SdkQjs 'dtoa.c')) { $sources += 'dtoa.c' }
-if (Test-Path (Join-Path $SdkQjs 'libbf.c')) { $sources += 'libbf.c' }
-
-Remove-Item (Join-Path $Out '*.obj') -Force -ErrorAction SilentlyContinue
-Remove-Item (Join-Path $Out 'libquickjs_openvibe.lib') -Force -ErrorAction SilentlyContinue
-
-$common = @(
- '/nologo',
- '/TC',
- '/O2',
- '/MT',
- '/W3',
- '/D_CRT_SECURE_NO_WARNINGS',
- '/D_CRT_NONSTDC_NO_WARNINGS',
- '/DWIN32',
- '/D_WINDOWS',
- '/DNOMINMAX',
- '/DWIN32_LEAN_AND_MEAN',
- "/DCONFIG_VERSION=`\`"openvibe`\`"",
- "/FI$MsvcCompat",
- "/I$CompatInclude",
- "/I$SdkQjs"
-)
-
-$objects = @()
-foreach ($src in $sources) {
- $srcPath = Join-Path $SdkQjs $src
- $obj = Join-Path $Out ([IO.Path]::GetFileNameWithoutExtension($src) + '.obj')
- Say "cl $src -> $obj"
- & cl.exe @common /c "$srcPath" "/Fo$obj"
- if ($LASTEXITCODE -ne 0) { throw "cl failed for $src" }
- if (!(Test-Path $obj)) { throw "cl reported success but did not produce $obj" }
- $objects += $obj
-}
-
-$outLib = Join-Path $Out 'libquickjs_openvibe.lib'
-Say "lib -> $outLib"
-& lib.exe /nologo "/OUT:$outLib" $objects
-if ($LASTEXITCODE -ne 0) { throw "lib.exe failed" }
-if (!(Test-Path $outLib)) { throw "lib.exe did not produce $outLib" }
-
-Say "built $outLib"
-PS1
-
-cat > docs/WINDOWS_QUICKJS_MSVC.md <<'MD'
-# Windows QuickJS MSVC Build Note
-
-The vendored QuickJS source is GCC/clang oriented. On Windows/MSVC it needs compatibility for:
-
-- POSIX `sys/time.h`
-- accidental `pthread.h` includes
-- GNU `__attribute__((...))`
-- GNU `__builtin_clz`, `__builtin_ctz`, and `__builtin_expect`
-
-`tools/build-quickjs-lib-windows.ps1` now generates build-local compatibility headers under:
-
-```text
-engine/source-sdk-2013/src/game/shared/openvibe/third_party/quickjs/build/compat/include
-```
-
-It also force-includes `openvibe_qjs_msvc_compat.h` for every QuickJS source file and disables `CONFIG_ATOMICS` in the copied SDK-side `quickjs.c` before compiling. Linux builds are unchanged.
-MD
-
-# Keep generated diagnostics/build deps out of git.
-if [[ -f .gitignore ]]; then
- grep -qxF 'artifacts/' .gitignore || echo 'artifacts/' >> .gitignore
- grep -qxF '_deps/' .gitignore || echo '_deps/' >> .gitignore
- grep -qxF 'engine/source-sdk-2013/' .gitignore || echo 'engine/source-sdk-2013/' >> .gitignore
-else
- cat > .gitignore <<'GI'
-artifacts/
-_deps/
-engine/source-sdk-2013/
-GI
-fi
-
-git rm -r --cached artifacts >/dev/null 2>&1 || true
-
-git add .gitignore docs/WINDOWS_QUICKJS_MSVC.md tools/build-quickjs-lib-windows.ps1 "$0" 2>/dev/null || \
- git add .gitignore docs/WINDOWS_QUICKJS_MSVC.md tools/build-quickjs-lib-windows.ps1
-
-say "git diff summary"
-git diff --cached --stat || true
-
-if ! git diff --cached --quiet; then
- git commit -m "Fix QuickJS MSVC GNU compatibility"
- git push
-else
- say "no changes to commit"
-fi
-
-if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then
- say "triggering clean Windows DLL build"
- tools/trigger-windows-dll-build-clean.sh
-else
- REPO="$(git remote get-url origin | sed -E 's#^[email protected]:##; s#^https://github.com/##; s#\.git$##')"
- say "triggering workflow repo=$REPO"
- gh workflow run windows-source-sdk-dlls.yml --repo "$REPO" --ref "$BRANCH"
- sleep 5
- gh run list --repo "$REPO" --workflow windows-source-sdk-dlls.yml --limit 3
-fi
-
-say "done"
deleted file mode 100755
deleted file mode 100755
@@ -1,199 +0,0 @@
@@ -1,199 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-BRANCH="$(git -C "$ROOT" branch --show-current 2>/dev/null || true)"
-cd "$ROOT"
-
-echo "[openvibe] fix QuickJS MSVC sys/time.h shim + rerun Windows DLL workflow"
-echo "[openvibe] root=$ROOT"
-echo "[openvibe] branch=${BRANCH:-unknown}"
-
-mkdir -p tools docs
-
-cat > tools/build-quickjs-lib-windows.ps1 <<'PS1'
-$ErrorActionPreference = 'Stop'
-
-$Root = if ($env:OPENVIBE_ROOT) { (Resolve-Path $env:OPENVIBE_ROOT).Path } else { (Resolve-Path (Join-Path $PSScriptRoot '..')).Path }
-$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root 'engine/source-sdk-2013' }
-
-$SrcQjs = Join-Path $Root 'sdk/openvibe/third_party/quickjs'
-$SdkQjs = Join-Path $Sdk 'src/game/shared/openvibe/third_party/quickjs'
-$Out = Join-Path $SdkQjs 'build'
-$LogDir = Join-Path $Root 'artifacts/windows-build-debug'
-
-function Say($m) { Write-Host "[openvibe-qjs-win] $m" }
-
-if (!(Test-Path (Join-Path $SrcQjs 'quickjs.c'))) {
- throw "Missing $SrcQjs/quickjs.c. Run tools/vendor-quickjs.sh first."
-}
-if (!(Get-Command cl.exe -ErrorAction SilentlyContinue)) {
- throw "cl.exe not found. Run this from a Visual Studio Developer PowerShell or Developer Command Prompt."
-}
-if (!(Get-Command lib.exe -ErrorAction SilentlyContinue)) {
- throw "lib.exe not found. Run this from a Visual Studio Developer PowerShell or Developer Command Prompt."
-}
-
-New-Item -ItemType Directory -Force -Path $SdkQjs, $Out, $LogDir | Out-Null
-Copy-Item -Path (Join-Path $SrcQjs '*') -Destination $SdkQjs -Recurse -Force
-
-# Upstream QuickJS includes <sys/time.h>. MSVC does not ship that POSIX header.
-# Provide a tiny compatibility header that exposes timeval + gettimeofday for quickjs.c.
-$CompatInclude = Join-Path $Out 'compat/include'
-$CompatSys = Join-Path $CompatInclude 'sys'
-New-Item -ItemType Directory -Force -Path $CompatSys | Out-Null
-$TimeHeader = Join-Path $CompatSys 'time.h'
-@'
-#ifndef OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H
-#define OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H
-
-#if defined(_WIN32)
-#ifndef WIN32_LEAN_AND_MEAN
-#define WIN32_LEAN_AND_MEAN
-#endif
-#ifndef NOMINMAX
-#define NOMINMAX
-#endif
-#include <windows.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _TIMEVAL_DEFINED
-#define _TIMEVAL_DEFINED
-typedef long suseconds_t;
-struct timeval {
- long tv_sec;
- long tv_usec;
-};
-#endif
-
-static __inline int gettimeofday(struct timeval *tv, void *tz)
-{
- FILETIME ft;
- ULARGE_INTEGER uli;
- unsigned long long usec;
- const unsigned long long unix_epoch = 116444736000000000ULL;
- (void)tz;
- if (!tv) return -1;
- GetSystemTimeAsFileTime(&ft);
- uli.LowPart = ft.dwLowDateTime;
- uli.HighPart = ft.dwHighDateTime;
- usec = (uli.QuadPart - unix_epoch) / 10ULL;
- tv->tv_sec = (long)(usec / 1000000ULL);
- tv->tv_usec = (long)(usec % 1000000ULL);
- return 0;
-}
-
-#ifdef __cplusplus
-}
-#endif
-#else
-#include_next <sys/time.h>
-#endif
-
-#endif /* OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H */
-'@ | Set-Content -Encoding ascii $TimeHeader
-
-Say "compat include=$CompatInclude"
-Say "compat header=$TimeHeader"
-
-$sources = @('quickjs.c','libregexp.c','libunicode.c','cutils.c')
-if (Test-Path (Join-Path $SdkQjs 'dtoa.c')) { $sources += 'dtoa.c' }
-if (Test-Path (Join-Path $SdkQjs 'libbf.c')) { $sources += 'libbf.c' }
-
-Remove-Item (Join-Path $Out '*.obj') -Force -ErrorAction SilentlyContinue
-Remove-Item (Join-Path $Out 'libquickjs_openvibe.lib') -Force -ErrorAction SilentlyContinue
-
-$common = @(
- '/nologo',
- '/TC',
- '/O2',
- '/MT',
- '/W3',
- '/D_CRT_SECURE_NO_WARNINGS',
- '/DWIN32',
- '/D_WINDOWS',
- '/DNOMINMAX',
- '/DWIN32_LEAN_AND_MEAN',
- "/DCONFIG_VERSION=`\`"openvibe`\`"",
- "/I$CompatInclude",
- "/I$SdkQjs"
-)
-
-$objects = @()
-foreach ($src in $sources) {
- $srcPath = Join-Path $SdkQjs $src
- $obj = Join-Path $Out ([IO.Path]::GetFileNameWithoutExtension($src) + '.obj')
- Say "cl $src -> $obj"
- & cl.exe @common /c "$srcPath" "/Fo$obj"
- if ($LASTEXITCODE -ne 0) { throw "cl failed for $src" }
- if (!(Test-Path $obj)) { throw "cl reported success but did not produce $obj" }
- $objects += $obj
-}
-
-$outLib = Join-Path $Out 'libquickjs_openvibe.lib'
-Say "lib -> $outLib"
-& lib.exe /nologo "/OUT:$outLib" $objects
-if ($LASTEXITCODE -ne 0) { throw "lib.exe failed" }
-if (!(Test-Path $outLib)) { throw "lib.exe did not produce $outLib" }
-
-Say "built $outLib"
-PS1
-
-cat > docs/WINDOWS_QUICKJS_MSVC.md <<'MD'
-# Windows QuickJS MSVC Build Note
-
-The vendored QuickJS C source includes POSIX `<sys/time.h>`, which MSVC does not provide. The Windows QuickJS build helper now generates a tiny compatibility include directory at build time:
-
-```text
-engine/source-sdk-2013/src/game/shared/openvibe/third_party/quickjs/build/compat/include/sys/time.h
-```
-
-That shim defines `struct timeval` and `gettimeofday()` using `GetSystemTimeAsFileTime()`, then passes the compatibility include directory to `cl.exe` before compiling QuickJS.
-
-This keeps Linux builds unchanged while allowing the Windows GitHub Actions runner to produce `libquickjs_openvibe.lib` for `client.dll` / `server.dll` builds.
-MD
-
-# Keep local generated diagnostics out of future commits.
-if [[ -f .gitignore ]]; then
- grep -qxF 'artifacts/' .gitignore || echo 'artifacts/' >> .gitignore
- grep -qxF '_deps/' .gitignore || echo '_deps/' >> .gitignore
- grep -qxF 'engine/source-sdk-2013/' .gitignore || echo 'engine/source-sdk-2013/' >> .gitignore
-else
- cat > .gitignore <<'GI'
-artifacts/
-_deps/
-engine/source-sdk-2013/
-GI
-fi
-
-# Avoid staging downloaded workflow diagnostics again.
-git rm -r --cached artifacts >/dev/null 2>&1 || true
-
-git add .gitignore docs/WINDOWS_QUICKJS_MSVC.md tools/build-quickjs-lib-windows.ps1 "$0" 2>/dev/null || \
- git add .gitignore docs/WINDOWS_QUICKJS_MSVC.md tools/build-quickjs-lib-windows.ps1
-
-echo "[openvibe] git diff summary"
-git diff --cached --stat || true
-
-if ! git diff --cached --quiet; then
- git commit -m "Fix QuickJS MSVC compatibility build"
- git push
-else
- echo "[openvibe] no changes to commit"
-fi
-
-if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then
- echo "[openvibe] triggering clean Windows DLL build"
- tools/trigger-windows-dll-build-clean.sh
-else
- REPO="$(git remote get-url origin | sed -E 's#^[email protected]:##; s#^https://github.com/##; s#\.git$##')"
- echo "[openvibe] triggering workflow repo=$REPO"
- gh workflow run windows-source-sdk-dlls.yml --repo "$REPO" --ref "${BRANCH:-codex/openvibe-next-steps}"
- sleep 5
- gh run list --repo "$REPO" --workflow windows-source-sdk-dlls.yml --limit 3
-fi
-
-echo "[openvibe] done"
deleted file mode 100755
deleted file mode 100755
@@ -1,342 +0,0 @@
@@ -1,342 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
-cd "$ROOT"
-BRANCH="$(git branch --show-current)"
-
-say() { echo "[openvibe] $*"; }
-
-say "fix QuickJS Windows build: use clang-cl for GNU/C99 QuickJS objects"
-say "root=$ROOT"
-say "branch=$BRANCH"
-
-mkdir -p tools docs
-
-cat > tools/build-quickjs-lib-windows.ps1 <<'PS1'
-$ErrorActionPreference = 'Stop'
-
-$Root = if ($env:OPENVIBE_ROOT) { (Resolve-Path $env:OPENVIBE_ROOT).Path } else { (Resolve-Path (Join-Path $PSScriptRoot '..')).Path }
-$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root 'engine/source-sdk-2013' }
-
-$SrcQjs = Join-Path $Root 'sdk/openvibe/third_party/quickjs'
-$SdkQjs = Join-Path $Sdk 'src/game/shared/openvibe/third_party/quickjs'
-$Out = Join-Path $SdkQjs 'build'
-$LogDir = Join-Path $Root 'artifacts/windows-build-debug'
-
-function Say($m) { Write-Host "[openvibe-qjs-win] $m" }
-function FirstExistingCommand([string[]]$names) {
- foreach ($name in $names) {
- $cmd = Get-Command $name -ErrorAction SilentlyContinue
- if ($cmd) { return $cmd.Source }
- }
- return $null
-}
-function FirstExistingPath([string[]]$paths) {
- foreach ($p in $paths) {
- if ($p -and (Test-Path $p)) { return (Resolve-Path $p).Path }
- }
- return $null
-}
-
-if (!(Test-Path (Join-Path $SrcQjs 'quickjs.c'))) {
- throw "Missing $SrcQjs/quickjs.c. Run tools/vendor-quickjs.sh first."
-}
-if (!(Get-Command lib.exe -ErrorAction SilentlyContinue)) {
- throw "lib.exe not found. Run this from a Visual Studio Developer PowerShell or Developer Command Prompt."
-}
-
-New-Item -ItemType Directory -Force -Path $SdkQjs, $Out, $LogDir | Out-Null
-Copy-Item -Path (Join-Path $SrcQjs '*') -Destination $SdkQjs -Recurse -Force
-
-$CompatInclude = Join-Path $Out 'compat/include'
-$CompatSys = Join-Path $CompatInclude 'sys'
-New-Item -ItemType Directory -Force -Path $CompatInclude, $CompatSys | Out-Null
-
-# QuickJS is written for GCC/clang C, not plain MSVC C. Prefer clang-cl because it
-# emits MSVC-compatible COFF .obj files while accepting GNU/C99 constructs that
-# QuickJS uses heavily. Keep cl.exe only as a last-resort fallback.
-$clangCandidates = @(
- (FirstExistingCommand @('clang-cl.exe','clang-cl')),
- (Join-Path ${env:ProgramFiles} 'LLVM/bin/clang-cl.exe'),
- (Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/2022/Enterprise/VC/Tools/Llvm/x86/bin/clang-cl.exe'),
- (Join-Path ${env:ProgramFiles} 'Microsoft Visual Studio/2022/Enterprise/VC/Tools/Llvm/x86/bin/clang-cl.exe'),
- (Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/2022/BuildTools/VC/Tools/Llvm/x86/bin/clang-cl.exe'),
- (Join-Path ${env:ProgramFiles} 'Microsoft Visual Studio/2022/BuildTools/VC/Tools/Llvm/x86/bin/clang-cl.exe')
-)
-$ClangCl = FirstExistingPath $clangCandidates
-$ClExe = FirstExistingCommand @('cl.exe')
-if ($ClangCl) { Say "using clang-cl=$ClangCl" }
-elseif ($ClExe) { Say "clang-cl not found; falling back to cl.exe=$ClExe" }
-else { throw "Neither clang-cl.exe nor cl.exe was found." }
-
-# Windows sys/time.h shim for upstream QuickJS.
-$TimeHeader = Join-Path $CompatSys 'time.h'
-@'
-#ifndef OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H
-#define OPENVIBE_QUICKJS_COMPAT_SYS_TIME_H
-#if defined(_WIN32)
-#ifndef WIN32_LEAN_AND_MEAN
-#define WIN32_LEAN_AND_MEAN
-#endif
-#ifndef NOMINMAX
-#define NOMINMAX
-#endif
-#include <windows.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-#ifndef _TIMEVAL_DEFINED
-#define _TIMEVAL_DEFINED
-typedef long suseconds_t;
-struct timeval {
- long tv_sec;
- long tv_usec;
-};
-#endif
-static __inline int gettimeofday(struct timeval *tv, void *tz)
-{
- FILETIME ft;
- ULARGE_INTEGER uli;
- unsigned long long usec;
- const unsigned long long unix_epoch = 116444736000000000ULL;
- (void)tz;
- if (!tv) return -1;
- GetSystemTimeAsFileTime(&ft);
- uli.LowPart = ft.dwLowDateTime;
- uli.HighPart = ft.dwHighDateTime;
- usec = (uli.QuadPart - unix_epoch) / 10ULL;
- tv->tv_sec = (long)(usec / 1000000ULL);
- tv->tv_usec = (long)(usec % 1000000ULL);
- return 0;
-}
-#ifdef __cplusplus
-}
-#endif
-#else
-#include_next <sys/time.h>
-#endif
-#endif
-'@ | Set-Content -Encoding ascii $TimeHeader
-
-# pthread.h shim. We also disable CONFIG_ATOMICS below, so this is only to satisfy
-# accidental includes from vendored QuickJS variants.
-$PThreadHeader = Join-Path $CompatInclude 'pthread.h'
-@'
-#ifndef OPENVIBE_QUICKJS_COMPAT_PTHREAD_H
-#define OPENVIBE_QUICKJS_COMPAT_PTHREAD_H
-#if defined(_WIN32)
-typedef int pthread_mutex_t;
-typedef int pthread_t;
-typedef int pthread_attr_t;
-#define PTHREAD_MUTEX_INITIALIZER 0
-static __inline int pthread_mutex_init(pthread_mutex_t *m, const void *a) { (void)a; if (m) *m = 0; return 0; }
-static __inline int pthread_mutex_destroy(pthread_mutex_t *m) { (void)m; return 0; }
-static __inline int pthread_mutex_lock(pthread_mutex_t *m) { (void)m; return 0; }
-static __inline int pthread_mutex_unlock(pthread_mutex_t *m) { (void)m; return 0; }
-static __inline int pthread_create(pthread_t *t, const pthread_attr_t *a, void *(*fn)(void *), void *arg) { (void)t; (void)a; (void)fn; (void)arg; return -1; }
-static __inline int pthread_join(pthread_t t, void **ret) { (void)t; if (ret) *ret = 0; return 0; }
-#else
-#include_next <pthread.h>
-#endif
-#endif
-'@ | Set-Content -Encoding ascii $PThreadHeader
-
-# Forced include for clang-cl. Do NOT redefine __attribute__ or __builtin_* here;
-# clang handles those and QuickJS needs them.
-$ClangCompat = Join-Path $CompatInclude 'openvibe_qjs_clangcl_compat.h'
-@'
-#ifndef OPENVIBE_QJS_CLANGCL_COMPAT_H
-#define OPENVIBE_QJS_CLANGCL_COMPAT_H
-#if defined(_WIN32)
-#ifndef WIN32_LEAN_AND_MEAN
-#define WIN32_LEAN_AND_MEAN
-#endif
-#ifndef NOMINMAX
-#define NOMINMAX
-#endif
-#include <stdint.h>
-#include <stdlib.h>
-#include <malloc.h>
-#ifndef alloca
-#define alloca _alloca
-#endif
-#ifndef strdup
-#define strdup _strdup
-#endif
-#endif
-#endif
-'@ | Set-Content -Encoding ascii $ClangCompat
-
-# Forced include for emergency cl.exe fallback only.
-$MsvcCompat = Join-Path $CompatInclude 'openvibe_qjs_msvc_compat.h'
-@'
-#ifndef OPENVIBE_QJS_MSVC_COMPAT_H
-#define OPENVIBE_QJS_MSVC_COMPAT_H
-#if defined(_MSC_VER) && !defined(__clang__)
-#ifndef WIN32_LEAN_AND_MEAN
-#define WIN32_LEAN_AND_MEAN
-#endif
-#ifndef NOMINMAX
-#define NOMINMAX
-#endif
-#include <stdint.h>
-#include <stdlib.h>
-#include <malloc.h>
-#include <intrin.h>
-#ifndef __attribute__
-#define __attribute__(x)
-#endif
-#ifndef __builtin_expect
-#define __builtin_expect(x, y) (x)
-#endif
-#ifndef __builtin_frame_address
-#define __builtin_frame_address(x) ((void*)0)
-#endif
-static __inline int openvibe_qjs_clz32(unsigned int v) { unsigned long idx; if (_BitScanReverse(&idx, v)) return 31 - (int)idx; return 32; }
-static __inline int openvibe_qjs_ctz32(unsigned int v) { unsigned long idx; if (_BitScanForward(&idx, v)) return (int)idx; return 32; }
-static __inline int openvibe_qjs_clz64(uint64_t v) { uint32_t hi = (uint32_t)(v >> 32); uint32_t lo = (uint32_t)v; if (hi) return openvibe_qjs_clz32(hi); return 32 + openvibe_qjs_clz32(lo); }
-static __inline int openvibe_qjs_ctz64(uint64_t v) { uint32_t hi = (uint32_t)(v >> 32); uint32_t lo = (uint32_t)v; if (lo) return openvibe_qjs_ctz32(lo); return 32 + openvibe_qjs_ctz32(hi); }
-#ifndef __builtin_clz
-#define __builtin_clz(x) openvibe_qjs_clz32((unsigned int)(x))
-#endif
-#ifndef __builtin_ctz
-#define __builtin_ctz(x) openvibe_qjs_ctz32((unsigned int)(x))
-#endif
-#ifndef __builtin_clzll
-#define __builtin_clzll(x) openvibe_qjs_clz64((uint64_t)(x))
-#endif
-#ifndef __builtin_ctzll
-#define __builtin_ctzll(x) openvibe_qjs_ctz64((uint64_t)(x))
-#endif
-#ifndef alloca
-#define alloca _alloca
-#endif
-#ifndef strdup
-#define strdup _strdup
-#endif
-#endif
-#endif
-'@ | Set-Content -Encoding ascii $MsvcCompat
-
-# Disable QuickJS atomics/workers for the embedded game runtime.
-$QuickJsC = Join-Path $SdkQjs 'quickjs.c'
-if (Test-Path $QuickJsC) {
- $q = Get-Content -Raw $QuickJsC
- $q2 = [regex]::Replace($q, '(?m)^\s*#\s*define\s+CONFIG_ATOMICS\s*$', '/* OpenVibe Windows embedded build: CONFIG_ATOMICS disabled */')
- if ($q2 -ne $q) {
- Set-Content -Encoding ascii $QuickJsC $q2
- Say 'disabled CONFIG_ATOMICS in SDK quickjs.c copy'
- } else {
- Say 'CONFIG_ATOMICS define not found in quickjs.c copy; pthread shim remains available'
- }
-}
-
-Say "compat include=$CompatInclude"
-Say "compat sys/time.h=$TimeHeader"
-Say "compat pthread.h=$PThreadHeader"
-Say "clang forced include=$ClangCompat"
-Say "msvc forced include=$MsvcCompat"
-
-$sources = @('quickjs.c','libregexp.c','libunicode.c','cutils.c')
-if (Test-Path (Join-Path $SdkQjs 'dtoa.c')) { $sources += 'dtoa.c' }
-if (Test-Path (Join-Path $SdkQjs 'libbf.c')) { $sources += 'libbf.c' }
-
-Remove-Item (Join-Path $Out '*.obj') -Force -ErrorAction SilentlyContinue
-Remove-Item (Join-Path $Out 'libquickjs_openvibe.lib') -Force -ErrorAction SilentlyContinue
-
-if ($ClangCl) {
- $Compiler = $ClangCl
- $Common = @(
- '/nologo',
- '/TC',
- '/O2',
- '/MT',
- '/W3',
- '/D_CRT_SECURE_NO_WARNINGS',
- '/D_CRT_NONSTDC_NO_WARNINGS',
- '/DWIN32',
- '/D_WINDOWS',
- '/DNOMINMAX',
- '/DWIN32_LEAN_AND_MEAN',
- "/DCONFIG_VERSION=`\`"openvibe`\`"",
- "/FI$ClangCompat",
- "/I$CompatInclude",
- "/I$SdkQjs",
- '/clang:-std=gnu11',
- '/clang:-Wno-everything'
- )
-} else {
- $Compiler = $ClExe
- $Common = @(
- '/nologo',
- '/TC',
- '/O2',
- '/MT',
- '/W3',
- '/D_CRT_SECURE_NO_WARNINGS',
- '/D_CRT_NONSTDC_NO_WARNINGS',
- '/DWIN32',
- '/D_WINDOWS',
- '/DNOMINMAX',
- '/DWIN32_LEAN_AND_MEAN',
- "/DCONFIG_VERSION=`\`"openvibe`\`"",
- "/FI$MsvcCompat",
- "/I$CompatInclude",
- "/I$SdkQjs"
- )
-}
-
-$objects = @()
-foreach ($src in $sources) {
- $srcPath = Join-Path $SdkQjs $src
- $obj = Join-Path $Out ([IO.Path]::GetFileNameWithoutExtension($src) + '.obj')
- Say "$([IO.Path]::GetFileName($Compiler)) $src -> $obj"
- & $Compiler @Common /c "$srcPath" "/Fo$obj"
- if ($LASTEXITCODE -ne 0) { throw "$Compiler failed for $src" }
- if (!(Test-Path $obj)) { throw "$Compiler reported success but did not produce $obj" }
- $objects += $obj
-}
-
-$outLib = Join-Path $Out 'libquickjs_openvibe.lib'
-Say "lib -> $outLib"
-& lib.exe /nologo "/OUT:$outLib" $objects
-if ($LASTEXITCODE -ne 0) { throw "lib.exe failed" }
-if (!(Test-Path $outLib)) { throw "lib.exe did not produce $outLib" }
-Say "built $outLib"
-PS1
-
-cat > docs/WINDOWS_QUICKJS_CLANGCL.md <<'MD'
-# Windows QuickJS build uses clang-cl
-
-QuickJS uses GNU/C99 compiler features that plain `cl.exe` does not parse cleanly, including GNU attributes, GCC/Clang builtins, and C initializer patterns. The Windows workflow now prefers `clang-cl.exe` for QuickJS only. `clang-cl` emits MSVC-compatible COFF `.obj` files, and `lib.exe` packs them into `libquickjs_openvibe.lib` for the regular Source SDK MSVC link.
-
-The rest of the Source SDK build still uses the Visual Studio/MSBuild toolchain.
-MD
-
-chmod +x tools/trigger-windows-dll-build-clean.sh 2>/dev/null || true
-
-git add tools/build-quickjs-lib-windows.ps1 docs/WINDOWS_QUICKJS_CLANGCL.md "$0" 2>/dev/null || git add tools/build-quickjs-lib-windows.ps1 docs/WINDOWS_QUICKJS_CLANGCL.md
-
-say "git diff summary"
-git diff --cached --stat || true
-
-if git diff --cached --quiet; then
- say "no changes to commit"
-else
- git commit -m "Build QuickJS with clang-cl on Windows"
- git push
-fi
-
-say "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 windows-source-sdk-dlls.yml --ref "$BRANCH"
- sleep 10
- RUN_ID="$(gh run list --workflow windows-source-sdk-dlls.yml --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"
- gh run watch "$RUN_ID" --exit-status || true
- tools/windows-workflow-debug-and-install.sh "$RUN_ID" || true
-fi
-
-say "done"
deleted file mode 100755
deleted file mode 100755
@@ -1,104 +0,0 @@
@@ -1,104 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-QJS="$ROOT/sdk/openvibe/third_party/quickjs"
-
-cd "$ROOT"
-
-echo "[openvibe] forcing clean QuickJS re-vendor"
-rm -rf "$QJS"
-tools/vendor-quickjs.sh
-
-echo "[openvibe] replacing smoke-quickjs.sh with robust version"
-
-cat > tools/smoke-quickjs.sh <<'SMOKE'
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-QJS="$ROOT/sdk/openvibe/third_party/quickjs"
-BUILD="$ROOT/.tmp/quickjs-smoke"
-
-rm -rf "$BUILD"
-mkdir -p "$BUILD"
-
-cat > "$BUILD/smoke.c" <<'C'
-#include <stdio.h>
-#include "quickjs.h"
-
-int main(void) {
- JSRuntime *rt = JS_NewRuntime();
- JSContext *ctx = JS_NewContext(rt);
-
- JSValue value = JS_Eval(ctx, "1 + 2 + 3", 9, "<smoke>", JS_EVAL_TYPE_GLOBAL);
- int32_t out = 0;
- JS_ToInt32(ctx, &out, value);
-
- JS_FreeValue(ctx, value);
- JS_FreeContext(ctx);
- JS_FreeRuntime(rt);
-
- printf("quickjs result=%d\n", out);
- return out == 6 ? 0 : 1;
-}
-C
-
-sources=(
- "$QJS/quickjs.c"
- "$QJS/libregexp.c"
- "$QJS/libunicode.c"
- "$QJS/cutils.c"
-)
-
-[[ -f "$QJS/dtoa.c" ]] && sources+=("$QJS/dtoa.c")
-[[ -f "$QJS/libbf.c" ]] && sources+=("$QJS/libbf.c")
-
-cc \
- -std=gnu11 \
- -D_GNU_SOURCE \
- -DCONFIG_VERSION=\"openvibe\" \
- -I"$QJS" \
- "$BUILD/smoke.c" \
- "${sources[@]}" \
- -lm \
- -ldl \
- -lpthread \
- -o "$BUILD/smoke-quickjs"
-
-"$BUILD/smoke-quickjs"
-SMOKE
-
-chmod +x tools/smoke-quickjs.sh
-
-echo "[openvibe] patching apply-openvibe-sdk.sh to not require libbf.c blindly"
-
-python3 <<'PY'
-from pathlib import Path
-
-p = Path("tools/apply-openvibe-sdk.sh")
-s = p.read_text()
-
-# Remove hardcoded libbf VPC line insertion if present.
-s = s.replace(
- '\\n\\t\\t\\t\\$File\\t"..\\\\shared\\\\openvibe\\\\third_party\\\\quickjs\\\\libbf.c"',
- ''
-)
-
-# Also remove the cleanup regex for libbf so repeated runs don't matter.
-s = s.replace(
- ' s/^.*openvibe\\\\\\\\third_party\\\\\\\\quickjs\\\\\\\\libbf\\\\.c.*\\n//mg;\\n',
- ''
-)
-
-p.write_text(s)
-PY
-
-echo "[openvibe] quickjs folder now contains:"
-find "$QJS" -maxdepth 1 -type f -printf " - %f\n" | sort
-
-echo "[openvibe] running smoke test"
-tools/smoke-quickjs.sh
-
-echo
-echo "[openvibe] QuickJS vendor/smoke fixed."
deleted file mode 100755
deleted file mode 100755
@@ -1,215 +0,0 @@
@@ -1,215 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"
-RUN_BUILD="${RUN_BUILD:-1}"
-STAMP="$(date +%Y%m%d-%H%M%S)"
-
-cd "$ROOT"
-
-backup_file() {
- local file="$1"
- [[ -f "$file" ]] && cp "$file" "$file.bak.$STAMP"
-}
-
-say() {
- echo "[openvibe] $*"
-}
-
-say "fixing QuickJS static-library VPC link issue"
-
-mkdir -p tools
-
-# Clean accidentally tracked local/generated junk if it exists.
-say "cleaning accidental tracked junk if present"
-git rm -f .tmp/quickjs-smoke/smoke-quickjs 2>/dev/null || true
-git rm -f tools/apply-openvibe-sdk.sh.bak.* 2>/dev/null || true
-
-touch .gitignore
-if ! grep -qxF ".tmp/" .gitignore; then
- cat >> .gitignore <<'GITIGNORE_ADD'
-
-# OpenVibe local/generated build junk
-.tmp/
-*.bak.*
-tools/*.bak.*
-engine/source-sdk-2013/src/game/shared/openvibe/third_party/quickjs/build/
-GITIGNORE_ADD
-fi
-
-say "writing tools/build-quickjs-lib.sh"
-
-cat > tools/build-quickjs-lib.sh <<'BUILDQJS'
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"
-
-SRC_QJS="$ROOT/sdk/openvibe/third_party/quickjs"
-SDK_QJS="$SDK/src/game/shared/openvibe/third_party/quickjs"
-OUT="$SDK_QJS/build"
-
-if [[ ! -f "$SRC_QJS/quickjs.c" ]]; then
- echo "Missing $SRC_QJS/quickjs.c" >&2
- echo "Run: tools/vendor-quickjs.sh" >&2
- exit 1
-fi
-
-mkdir -p "$SDK_QJS"
-
-# Exclude build/ so rsync does not delete our .o/.a output dir.
-rsync -a --delete --exclude='build/' "$SRC_QJS/" "$SDK_QJS/"
-
-mkdir -p "$OUT"
-
-sources=(
- quickjs.c
- libregexp.c
- libunicode.c
- cutils.c
-)
-
-[[ -f "$SDK_QJS/dtoa.c" ]] && sources+=(dtoa.c)
-[[ -f "$SDK_QJS/libbf.c" ]] && sources+=(libbf.c)
-
-rm -f "$OUT"/*.o "$OUT"/libquickjs_openvibe.a
-
-for src in "${sources[@]}"; do
- obj="$OUT/${src%.c}.o"
- echo "[openvibe-qjs] cc $src -> $obj"
-
- cc \
- -std=gnu11 \
- -O2 \
- -fPIC \
- -D_GNU_SOURCE \
- -DCONFIG_VERSION=\"openvibe\" \
- -I"$SDK_QJS" \
- -c "$SDK_QJS/$src" \
- -o "$obj"
-done
-
-ar rcs "$OUT/libquickjs_openvibe.a" "$OUT"/*.o
-
-echo "[openvibe-qjs] built $OUT/libquickjs_openvibe.a"
-ls -lh "$OUT/libquickjs_openvibe.a"
-BUILDQJS
-
-chmod +x tools/build-quickjs-lib.sh
-
-say "patching tools/apply-openvibe-sdk.sh"
-
-if [[ ! -f tools/apply-openvibe-sdk.sh ]]; then
- echo "Missing tools/apply-openvibe-sdk.sh" >&2
- exit 1
-fi
-
-backup_file tools/apply-openvibe-sdk.sh
-
-python3 <<'PY'
-from pathlib import Path
-import re
-
-p = Path("tools/apply-openvibe-sdk.sh")
-s = p.read_text()
-
-# Remove older injected QuickJS final blocks so this script stays idempotent.
-patterns = [
- r'\n# OPENVIBE_REMOVE_DIRECT_QUICKJS_C\n.*?(?=\n# OPENVIBE|\necho "\[openvibe-sdk\] Source SDK OpenVibe patch applied"|\Z)',
- r'\n# OPENVIBE_LINK_QUICKJS_STATIC_LIB\n.*?(?=\n# OPENVIBE|\necho "\[openvibe-sdk\] Source SDK OpenVibe patch applied"|\Z)',
- r'\n# OPENVIBE_FIX_QUICKJS_LINK_FINAL_V2\n.*?(?=\n# OPENVIBE|\necho "\[openvibe-sdk\] Source SDK OpenVibe patch applied"|\Z)',
-]
-for pat in patterns:
- s = re.sub(pat, "\n", s, flags=re.S)
-
-# Do not allow a literal libquickjs_openvibe.a in any VPC $Lib line.
-# VPC appends .a itself, so ".a" here becomes ".a.a".
-s = s.replace("libquickjs_openvibe.a", "libquickjs_openvibe")
-
-# Ensure apply script calls the QuickJS static library builder.
-if '"$ROOT/tools/build-quickjs-lib.sh"' not in s:
- needle = 'copy_tree "$ROOT/sdk/openvibe/third_party/quickjs" \\\n "$SDK/src/game/shared/openvibe/third_party/quickjs"'
- if needle in s:
- s = s.replace(needle, needle + '\n\n"$ROOT/tools/build-quickjs-lib.sh"')
- else:
- s += '\n\n"$ROOT/tools/build-quickjs-lib.sh"\n'
-
-final_block = r'''
-# OPENVIBE_FIX_QUICKJS_LINK_FINAL_V2
-# QuickJS is C, so do not compile its .c files through Source SDK/VPC C++.
-# We build libquickjs_openvibe.a with cc and link it as a library.
-if [[ -f "$SERVER_VPC" ]]; then
- perl -0pi -e '
- s/^.*quickjs\\quickjs\.c.*\n//mg;
- s/^.*quickjs\\libregexp\.c.*\n//mg;
- s/^.*quickjs\\libunicode\.c.*\n//mg;
- s/^.*quickjs\\cutils\.c.*\n//mg;
- s/^.*quickjs\\dtoa\.c.*\n//mg;
- s/^.*quickjs\\libbf\.c.*\n//mg;
- ' "$SERVER_VPC"
-
- perl -0pi -e 's/^.*libquickjs_openvibe(?:\.a)?".*\n//mg' "$SERVER_VPC"
-
- perl -0pi -e 's/(\$File\s+"hl2mp\\openvibe_js_server\.cpp"\n)/$1\t\t\t\$Lib\t"..\\shared\\openvibe\\third_party\\quickjs\\build\\libquickjs_openvibe"\n/s' "$SERVER_VPC"
-
- echo "[openvibe-sdk] linked QuickJS static library"
-fi
-'''
-
-final_echo = 'echo "[openvibe-sdk] Source SDK OpenVibe patch applied"'
-if final_echo in s:
- s = s.replace(final_echo, final_block + "\n" + final_echo)
-else:
- s += "\n" + final_block + "\n"
-
-p.write_text(s)
-PY
-
-chmod +x tools/apply-openvibe-sdk.sh
-
-say "applying SDK patch"
-tools/apply-openvibe-sdk.sh
-
-SERVER_VPC="$SDK/src/game/server/server_hl2mp.vpc"
-
-say "force-fixing already-applied SDK server_hl2mp.vpc"
-
-if [[ -f "$SERVER_VPC" ]]; then
- backup_file "$SERVER_VPC"
-
- perl -0pi -e '
- s/^.*quickjs\\quickjs\.c.*\n//mg;
- s/^.*quickjs\\libregexp\.c.*\n//mg;
- s/^.*quickjs\\libunicode\.c.*\n//mg;
- s/^.*quickjs\\cutils\.c.*\n//mg;
- s/^.*quickjs\\dtoa\.c.*\n//mg;
- s/^.*quickjs\\libbf\.c.*\n//mg;
- ' "$SERVER_VPC"
-
- perl -0pi -e 's/^.*libquickjs_openvibe(?:\.a)?".*\n//mg' "$SERVER_VPC"
- perl -0pi -e 's/(\$File\s+"hl2mp\\openvibe_js_server\.cpp"\n)/$1\t\t\t\$Lib\t"..\\shared\\openvibe\\third_party\\quickjs\\build\\libquickjs_openvibe"\n/s' "$SERVER_VPC"
-fi
-
-say "building QuickJS static lib"
-tools/build-quickjs-lib.sh
-
-say "VPC quickjs check"
-if [[ -f "$SERVER_VPC" ]]; then
- grep -nE 'quickjs\\(quickjs|libregexp|libunicode|cutils|dtoa|libbf)\.c|libquickjs_openvibe' "$SERVER_VPC" || true
-else
- echo "Missing $SERVER_VPC" >&2
-fi
-
-if [[ "$RUN_BUILD" = "1" ]]; then
- say "running SDK build"
- tools/build-sdk-linux.sh 2>&1 | tee "$HOME/ov-build.log"
-
- say "build finished. Last lines:"
- tail -80 "$HOME/ov-build.log"
-else
- say "skipping build because RUN_BUILD=0"
- echo "Run manually:"
- echo " tools/build-sdk-linux.sh 2>&1 | tee ~/ov-build.log"
-fi
deleted file mode 100755
deleted file mode 100755
@@ -1,127 +0,0 @@
@@ -1,127 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-say() { echo "[openvibe] $*"; }
-warn() { echo "[openvibe warn] $*" >&2; }
-fail() { echo "[openvibe error] $*" >&2; exit 1; }
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-cd "$ROOT"
-BRANCH="${OPENVIBE_BRANCH:-$(git rev-parse --abbrev-ref HEAD)}"
-REPO="${OPENVIBE_REPO:-$(tools/openvibe-gh-repo.sh)}"
-WORKFLOW="${OPENVIBE_WORKFLOW:-windows-source-sdk-dlls.yml}"
-BUILD_PS1="tools/build-sdk-windows.ps1"
-
-say "fix Win32 build: build/copy bitmap.lib before client link"
-say "root=$ROOT"
-say "branch=$BRANCH"
-say "repo=$REPO"
-
-command -v python3 >/dev/null 2>&1 || fail "python3 is required"
-command -v gh >/dev/null 2>&1 || fail "GitHub CLI gh is required"
-[[ -f "$BUILD_PS1" ]] || fail "missing $BUILD_PS1"
-
-python3 - <<'PY'
-from pathlib import Path
-p = Path('tools/build-sdk-windows.ps1')
-s = p.read_text()
-orig = s
-
-# Add bitmap project to the dependency build list. Client links ..\..\lib\public\bitmap.lib.
-if '"bitmap*.vcxproj"' not in s:
- anchor = ' "mathlib*.vcxproj",\n'
- if anchor not in s:
- raise SystemExit('could not find dependency patterns anchor near mathlib*.vcxproj')
- s = s.replace(anchor, anchor + ' "bitmap*.vcxproj",\n', 1)
-
-# Ensure bitmap.lib is copied into the public lib dir after dependency builds.
-if 'Copy-LibIfNeeded "bitmap.lib"' not in s:
- anchor = ' Copy-LibIfNeeded "mathlib.lib" $publicLibDir\n'
- if anchor not in s:
- raise SystemExit('could not find Copy-LibIfNeeded anchor near mathlib.lib')
- s = s.replace(anchor, anchor + ' Copy-LibIfNeeded "bitmap.lib" $publicLibDir\n', 1)
-
-# Improve fallback search ordering for libs: prefer target arch output dirs over stale x64.
-old = ''' $found = Get-ChildItem -Path $Sdk -Recurse -File -Filter $libName -ErrorAction SilentlyContinue |
- Where-Object { $_.FullName -notmatch "artifacts|windows-build-debug|_deps" } |
- Sort-Object LastWriteTime -Descending |
- Select-Object -First 1
-'''
-new = ''' $found = Get-ChildItem -Path $Sdk -Recurse -File -Filter $libName -ErrorAction SilentlyContinue |
- Where-Object { $_.FullName -notmatch "artifacts|windows-build-debug|_deps" } |
- Sort-Object @{ Expression = {
- if ($script:OpenVibeTargetArch -eq "x86") {
- if ($_.FullName -match "\\x64\\|/x64/|win64|x64") { 1 } else { 0 }
- } else {
- if ($_.FullName -match "\\x64\\|/x64/|win64|x64") { 0 } else { 1 }
- }
- } }, LastWriteTime -Descending |
- Select-Object -First 1
-'''
-if old in s and new not in s:
- s = s.replace(old, new, 1)
-
-# Add an explicit diagnostic line so we can tell this patch ran.
-marker = '# OPENVIBE_WIN32_BITMAP_LIB_DEP_PATCH'
-if marker not in s:
- s = s.replace('function Build-SourceSdkDependencyProjects {\n', f'{marker}\nfunction Build-SourceSdkDependencyProjects {{\n', 1)
-
-if s != orig:
- p.write_text(s)
- print('[patched] tools/build-sdk-windows.ps1')
-else:
- print('[openvibe] tools/build-sdk-windows.ps1 already patched')
-PY
-
-mkdir -p docs
-cat > docs/WINDOWS_WIN32_BITMAP_LIB_FIX.md <<'DOC'
-# Windows Win32 bitmap.lib fix
-
-The Win32/Proton DLL build converts VPC's generated win64 projects to Win32 before MSBuild.
-After conversion, the HL2MP client links against `..\..\lib\public\bitmap.lib`.
-
-This patch adds the generated bitmap project to the dependency build list and copies
-`bitmap.lib` into the target public library directory before building `client.dll`.
-DOC
-
-say "git diff summary"
-git diff --stat
-
-git add tools/build-sdk-windows.ps1 docs/WINDOWS_WIN32_BITMAP_LIB_FIX.md tools/fix-win32-build-missing-bitmap-lib-and-install.sh 2>/dev/null || true
-if ! git diff --cached --quiet; then
- git commit -m "Build bitmap lib for Win32 Proton DLLs"
- git push origin "$BRANCH"
-else
- say "no commit needed"
-fi
-
-HEAD="$(git rev-parse HEAD)"
-say "head=$HEAD"
-say "triggering Win32 workflow"
-gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$BRANCH" -f diagnostics=1 || true
-
-RUN_ID=""
-for _ in $(seq 1 60); do
- RUN_ID="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --commit "$HEAD" --limit 10 --json databaseId,createdAt,status --jq 'sort_by(.createdAt) | last | .databaseId' 2>/dev/null || true)"
- if [[ -n "$RUN_ID" && "$RUN_ID" != "null" ]]; then
- break
- fi
- sleep 5
-done
-[[ -n "$RUN_ID" && "$RUN_ID" != "null" ]] || fail "could not find workflow run for commit $HEAD"
-
-say "watching run $RUN_ID"
-if ! gh run watch "$RUN_ID" --repo "$REPO" --exit-status; then
- warn "workflow failed; downloading diagnostics"
- tools/windows-workflow-debug-and-install.sh "$RUN_ID" || true
- fail "Win32 DLL workflow failed"
-fi
-
-say "workflow passed; installing PE32 OpenVibe DLLs"
-OPENVIBE_EXPECT_DLL_ARCH=x86 tools/install-latest-openvibe-windows-dlls.sh "$RUN_ID"
-
-say "done. Now fully close hl2.exe/Proton, relaunch, and test:"
-echo 'OPENVIBE_PROTON_LOG=1 OPENVIBE_CLIENT_MODE=proton tools/run-client-auto.sh 127.0.0.1 27015'
-echo 'ov_help'
-echo 'ov_join hub'
-echo 'ov_menu'
deleted file mode 100755
deleted file mode 100755
@@ -1,195 +0,0 @@
@@ -1,195 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-cd "$ROOT"
-
-say() { printf '[openvibe] %s\n' "$*"; }
-
-BRANCH="$(git rev-parse --abbrev-ref HEAD)"
-REPO=""
-if [[ -x tools/openvibe-gh-repo.sh ]]; then
- REPO="$(tools/openvibe-gh-repo.sh || true)"
-fi
-if [[ -z "$REPO" ]]; then
- REPO="$(git remote get-url origin | sed -E 's#^[email protected]:##; s#^https://github.com/##; s#\.git$##')"
-else
- REPO="$(printf '%s' "$REPO" | sed -E 's#^[email protected]:##; s#^https://github.com/##; s#\.git$##')"
-fi
-WORKFLOW="windows-source-sdk-dlls.yml"
-WORKFLOW_PATH=".github/workflows/$WORKFLOW"
-BOOT="tools/bootstrap-source-sdk-2013-windows.ps1"
-
-say "fix Windows bootstrap by using actions/checkout for Valve SDK"
-say "root=$ROOT"
-say "branch=$BRANCH"
-say "repo=$REPO"
-
-mkdir -p .github/workflows tools docs
-
-# Keep downloaded diagnostics and bootstrapped SDK out of git from now on.
-touch .gitignore
-for line in 'artifacts/' '_deps/' 'engine/source-sdk-2013/'; do
- grep -qxF "$line" .gitignore || echo "$line" >> .gitignore
-done
-
-cat > "$BOOT" <<'PS1'
-$ErrorActionPreference = 'Stop'
-
-$Root = if ($env:OPENVIBE_ROOT) { (Resolve-Path $env:OPENVIBE_ROOT).Path } else { (Resolve-Path (Join-Path $PSScriptRoot '..')).Path }
-$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root 'engine/source-sdk-2013' }
-$Src = Join-Path $Sdk 'src'
-$Deps = Join-Path $Root '_deps'
-$Checkout = Join-Path $Deps 'source-sdk-2013-upstream'
-$LogDir = Join-Path $Root 'artifacts/windows-build-debug'
-New-Item -ItemType Directory -Force -Path $LogDir | Out-Null
-
-function Say($m) { Write-Host "[openvibe-sdk-bootstrap] $m" }
-function WriteTree($path, $name) {
- try {
- if (Test-Path $path) {
- "=== $path ===" | Out-File (Join-Path $LogDir $name)
- Get-ChildItem -Force $path | Select-Object Mode,Length,Name,FullName | Format-Table -AutoSize | Out-File (Join-Path $LogDir $name) -Append
- } else {
- "[missing] $path" | Out-File (Join-Path $LogDir $name)
- }
- } catch {
- "[tree failed] $path $($_.Exception.Message)" | Out-File (Join-Path $LogDir $name)
- }
-}
-function HasHl2mp($root) {
- $src = Join-Path $root 'src'
- return ((Test-Path (Join-Path $src 'game/client/hl2mp')) -and (Test-Path (Join-Path $src 'game/server/hl2mp')))
-}
-function CopyLayout($fromRoot, $label) {
- Say "using Valve SDK layout: $label -> $Sdk"
- if (Test-Path $Sdk) { Remove-Item -Recurse -Force $Sdk }
- New-Item -ItemType Directory -Force -Path $Sdk | Out-Null
- Get-ChildItem -Force $fromRoot | Where-Object { $_.Name -ne '.git' } | ForEach-Object {
- Copy-Item $_.FullName -Destination $Sdk -Recurse -Force
- }
- if (-not (HasHl2mp $Sdk)) {
- WriteTree $Sdk 'sdk-root-after-copy.txt'
- throw "Copied SDK layout is missing expected src/game/client/hl2mp and src/game/server/hl2mp folders"
- }
- Say "SDK bootstrapped successfully"
-}
-
-Say "root=$Root"
-Say "sdk=$Sdk"
-Say "deps=$Deps"
-Say "checkout=$Checkout"
-WriteTree $Root 'repo-root-before-bootstrap.txt'
-WriteTree $Deps 'deps-before-bootstrap.txt'
-
-if (HasHl2mp $Sdk) {
- Say "existing SDK tree looks usable"
- exit 0
-}
-
-# Preferred path: GitHub Actions checks out ValveSoftware/source-sdk-2013 into _deps/source-sdk-2013-upstream.
-# This avoids flaky codeload/curl behavior and avoids git credential leakage from the OpenVibe checkout.
-$candidates = @(
- @{ Root = $Checkout; Label = 'upstream root/src' },
- @{ Root = (Join-Path $Checkout 'mp'); Label = 'upstream mp/src' },
- @{ Root = (Join-Path $Checkout 'sp'); Label = 'upstream sp/src' }
-)
-foreach ($c in $candidates) {
- if ((Test-Path $c.Root) -and (HasHl2mp $c.Root)) {
- CopyLayout $c.Root $c.Label
- exit 0
- }
-}
-
-# Emergency fallback only: try a clean public clone with credentials explicitly disabled.
-# The workflow should normally never reach this if the actions/checkout step worked.
-Say "actions checkout layout was not found; trying emergency public git clone fallback"
-$Fallback = Join-Path $Deps 'source-sdk-2013-git-fallback'
-if (Test-Path $Fallback) { Remove-Item -Recurse -Force $Fallback }
-New-Item -ItemType Directory -Force -Path $Deps | Out-Null
-$cloneLog = Join-Path $LogDir 'bootstrap-git-clone-fallback.log'
-& git -c http.https://github.com/.extraheader= clone --depth 1 https://github.com/ValveSoftware/source-sdk-2013.git $Fallback 2>&1 | Tee-Object -FilePath $cloneLog
-if ($LASTEXITCODE -ne 0) {
- WriteTree $Deps 'deps-after-bootstrap-failure.txt'
- throw "Could not clone ValveSoftware/source-sdk-2013. Check $cloneLog"
-}
-
-foreach ($root in @($Fallback, (Join-Path $Fallback 'mp'), (Join-Path $Fallback 'sp'))) {
- if ((Test-Path $root) -and (HasHl2mp $root)) {
- CopyLayout $root "git fallback $root"
- exit 0
- }
-}
-
-WriteTree $Fallback 'fallback-root-after-clone.txt'
-throw "ValveSoftware/source-sdk-2013 was fetched, but no usable HL2MP src layout was found."
-PS1
-
-python3 - <<'PY'
-from pathlib import Path
-p = Path('.github/workflows/windows-source-sdk-dlls.yml')
-s = p.read_text()
-insert = '''\n - name: Checkout Valve Source SDK 2013\n uses: actions/checkout@v4\n with:\n repository: ValveSoftware/source-sdk-2013\n path: _deps/source-sdk-2013-upstream\n fetch-depth: 1\n persist-credentials: false\n\n'''
-if 'Checkout Valve Source SDK 2013' not in s:
- marker = ' - name: Bootstrap Source SDK 2013\n'
- if marker not in s:
- raise SystemExit('Could not find Bootstrap Source SDK 2013 step in workflow')
- s = s.replace(marker, insert + marker, 1)
-# Make the tree/debug step aware of the second checkout path if it exists later in the job.
-old = "if (Test-Path engine/source-sdk-2013/src) { Get-ChildItem -Force engine/source-sdk-2013/src | Select-Object Mode,Length,Name } else { Write-Host \"[miss] engine/source-sdk-2013/src\" }"
-new = old + "\n if (Test-Path _deps/source-sdk-2013-upstream) { Get-ChildItem -Force _deps/source-sdk-2013-upstream | Select-Object Mode,Length,Name } else { Write-Host \"[miss] _deps/source-sdk-2013-upstream before checkout step\" }"
-if old in s and '_deps/source-sdk-2013-upstream before checkout step' not in s:
- s = s.replace(old, new, 1)
-p.write_text(s)
-PY
-
-cat > docs/WINDOWS_WORKFLOW_SDK_BOOTSTRAP.md <<'MD'
-# Windows Source SDK bootstrap
-
-The Windows DLL workflow uses a two-checkout model:
-
-1. Checkout this OpenVibe repository.
-2. Checkout `ValveSoftware/source-sdk-2013` into `_deps/source-sdk-2013-upstream`.
-3. Normalize the Valve SDK layout into `engine/source-sdk-2013`.
-4. Apply OpenVibe SDK patches.
-5. Build `client.dll` and `server.dll` with MSBuild.
-
-The bootstrap script accepts these upstream layouts:
-
-- `_deps/source-sdk-2013-upstream/src/...`
-- `_deps/source-sdk-2013-upstream/mp/src/...`
-- `_deps/source-sdk-2013-upstream/sp/src/...`
-
-Diagnostics are uploaded under the `openvibe-windows-build-debug` artifact.
-MD
-
-say "git diff summary"
-git diff --stat -- .gitignore "$WORKFLOW_PATH" "$BOOT" docs/WINDOWS_WORKFLOW_SDK_BOOTSTRAP.md || true
-
-git add .gitignore "$WORKFLOW_PATH" "$BOOT" docs/WINDOWS_WORKFLOW_SDK_BOOTSTRAP.md "$0" 2>/dev/null || \
- git add .gitignore "$WORKFLOW_PATH" "$BOOT" docs/WINDOWS_WORKFLOW_SDK_BOOTSTRAP.md tools/fix-windows-bootstrap-actions-checkout-and-rerun.sh
-
-git commit -m "Use actions checkout for Windows Source SDK bootstrap" || say "nothing to commit"
-git push
-
-say "triggering workflow"
-gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$BRANCH"
-sleep 8
-RUN_ID="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"
-say "watching run $RUN_ID"
-if gh run watch "$RUN_ID" --repo "$REPO" --exit-status; then
- say "workflow passed"
-else
- say "workflow failed; downloading diagnostics anyway"
-fi
-
-if [[ -x tools/windows-workflow-debug-and-install.sh ]]; then
- tools/windows-workflow-debug-and-install.sh || true
-fi
-
-say "verify DLL content"
-if [[ -x tools/verify-openvibe-dll-content.sh ]]; then
- tools/verify-openvibe-dll-content.sh || true
-fi
-
-say "done"
deleted file mode 100755
deleted file mode 100755
@@ -1,248 +0,0 @@
@@ -1,248 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-cd "$ROOT"
-
-BRANCH="$(git rev-parse --abbrev-ref HEAD)"
-REPO="${OPENVIBE_REPO:-$(git remote get-url origin | sed -E 's#.*github.com[:/]([^/]+/[^/.]+)(\.git)?#\1#')}"
-REPO="${REPO%.git}"
-WORKFLOW="windows-source-sdk-dlls.yml"
-
-echo "[openvibe] fix Windows bootstrap: use Valve master zip/download fallback"
-echo "[openvibe] root=$ROOT"
-echo "[openvibe] branch=$BRANCH"
-echo "[openvibe] repo=$REPO"
-
-mkdir -p tools docs .github/workflows
-
-cat > tools/bootstrap-source-sdk-2013-windows.ps1 <<'PS'
-$ErrorActionPreference = 'Stop'
-
-$Root = if ($env:OPENVIBE_ROOT) { (Resolve-Path $env:OPENVIBE_ROOT).Path } else { (Resolve-Path (Join-Path $PSScriptRoot '..')).Path }
-$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root 'engine/source-sdk-2013' }
-$Src = Join-Path $Sdk 'src'
-$Deps = Join-Path $Root '_deps'
-$LogDir = Join-Path $Root 'artifacts/windows-build-debug'
-New-Item -ItemType Directory -Force -Path $Deps, $LogDir | Out-Null
-
-function Say($m) { Write-Host "[openvibe-sdk-bootstrap] $m" }
-function Dump-Dir($path, $outName) {
- try {
- if (Test-Path $path) {
- Get-ChildItem -Force $path | Select-Object Mode,Length,Name | Format-Table -AutoSize | Out-File (Join-Path $LogDir $outName)
- } else {
- "[missing] $path" | Out-File (Join-Path $LogDir $outName)
- }
- } catch {
- "dump failed: $($_.Exception.Message)" | Out-File (Join-Path $LogDir $outName)
- }
-}
-function Assert-UsableSdk($prefix) {
- if ((Test-Path (Join-Path $Src 'game/client/hl2mp')) -and (Test-Path (Join-Path $Src 'game/server/hl2mp'))) {
- Say "$prefix SDK tree looks usable"
- return $true
- }
- return $false
-}
-function Copy-SdkRoot($sourceRoot) {
- Say "copying SDK root $sourceRoot -> $Sdk"
- if (Test-Path $Sdk) { Remove-Item -Recurse -Force $Sdk }
- New-Item -ItemType Directory -Force -Path $Sdk | Out-Null
- Copy-Item -Path (Join-Path $sourceRoot '*') -Destination $Sdk -Recurse -Force
- Dump-Dir $Sdk 'sdk-root-after-copy.txt'
- if (!(Assert-UsableSdk 'copied')) {
- throw "Copied SDK is missing expected HL2MP folders under $Src"
- }
-}
-
-Say "root=$Root"
-Say "sdk=$Sdk"
-Say "deps=$Deps"
-
-if (Assert-UsableSdk 'existing') { exit 0 }
-
-# ValveSoftware/source-sdk-2013 currently uses the default/master layout with src/ at the repository root.
-# Do not assume an mp branch or mp/src folder. Downloading the zip avoids git/extraheader/auth weirdness on Actions runners.
-$zip = Join-Path $Deps 'source-sdk-2013-master.zip'
-$extractParent = Join-Path $Deps 'source-sdk-2013-zip'
-$zipRoot = Join-Path $extractParent 'source-sdk-2013-master'
-$zipUrl = 'https://codeload.github.com/ValveSoftware/source-sdk-2013/zip/refs/heads/master'
-
-$zipOk = $false
-try {
- Say "downloading Valve SDK master zip from $zipUrl"
- if (Test-Path $zip) { Remove-Item -Force $zip }
- if (Test-Path $extractParent) { Remove-Item -Recurse -Force $extractParent }
- New-Item -ItemType Directory -Force -Path $extractParent | Out-Null
-
- if (Get-Command curl.exe -ErrorAction SilentlyContinue) {
- & curl.exe -L --retry 4 --retry-delay 3 --connect-timeout 30 -o $zip $zipUrl 2>&1 | Tee-Object -FilePath (Join-Path $LogDir 'bootstrap-curl-master-zip.log')
- if ($LASTEXITCODE -ne 0) { throw "curl.exe failed with exit code $LASTEXITCODE" }
- } else {
- Invoke-WebRequest -Uri $zipUrl -OutFile $zip -UseBasicParsing 2>&1 | Tee-Object -FilePath (Join-Path $LogDir 'bootstrap-iwr-master-zip.log')
- }
-
- $zipInfo = Get-Item $zip
- Say "zip bytes=$($zipInfo.Length)"
- if ($zipInfo.Length -lt 1000000) { throw "downloaded zip is suspiciously small" }
-
- Expand-Archive -Path $zip -DestinationPath $extractParent -Force
- Dump-Dir $extractParent 'zip-extract-parent.txt'
- Dump-Dir $zipRoot 'zip-root.txt'
-
- if (Test-Path (Join-Path $zipRoot 'src/game/client/hl2mp')) {
- Copy-SdkRoot $zipRoot
- $zipOk = $true
- } else {
- throw "zip did not contain expected src/game/client/hl2mp at $zipRoot"
- }
-} catch {
- Say "zip bootstrap failed: $($_.Exception.Message)"
- $_ | Out-String | Out-File (Join-Path $LogDir 'bootstrap-zip-exception.txt')
-}
-
-if ($zipOk) { Say 'SDK bootstrapped successfully from zip'; exit 0 }
-
-# Last-resort git fallback. Clear possible GitHub Actions extraheaders and force public HTTPS.
-$gitRepo = Join-Path $Deps 'source-sdk-2013-git'
-try {
- Say "trying git clone fallback"
- git config --global --unset-all http.https://github.com/.extraheader 2>$null
- git config --global --unset-all http.https://github.com/ValveSoftware/source-sdk-2013.extraheader 2>$null
- if (Test-Path $gitRepo) { Remove-Item -Recurse -Force $gitRepo }
- & git -c http.https://github.com/.extraheader= clone --depth 1 --single-branch https://github.com/ValveSoftware/source-sdk-2013.git $gitRepo 2>&1 | Tee-Object -FilePath (Join-Path $LogDir 'bootstrap-git-clone-master.log')
- if ($LASTEXITCODE -ne 0) { throw "git clone failed with exit code $LASTEXITCODE" }
- Dump-Dir $gitRepo 'git-root.txt'
- if (Test-Path (Join-Path $gitRepo 'src/game/client/hl2mp')) {
- Copy-SdkRoot $gitRepo
- Say 'SDK bootstrapped successfully from git'
- exit 0
- }
- throw "git clone did not contain src/game/client/hl2mp"
-} catch {
- Say "git bootstrap failed: $($_.Exception.Message)"
- $_ | Out-String | Out-File (Join-Path $LogDir 'bootstrap-git-exception.txt')
-}
-
-Dump-Dir $Deps 'deps-after-bootstrap-failure.txt'
-throw "Could not bootstrap ValveSoftware/source-sdk-2013. Check openvibe-windows-build-debug artifact, especially bootstrap-curl-master-zip.log, bootstrap-zip-exception.txt, and bootstrap-git-clone-master.log."
-PS
-
-cat > docs/WINDOWS_WORKFLOW_SDK_BOOTSTRAP.md <<'MD'
-# Windows Source SDK bootstrap
-
-The Windows GitHub Actions runner is fresh and does not have Alex's local `engine/source-sdk-2013` checkout.
-
-The workflow bootstraps Valve's public Source SDK 2013 repository into:
-
-```text
-engine/source-sdk-2013
-```
-
-The expected SDK layout is:
-
-```text
-engine/source-sdk-2013/src/game/client/hl2mp
-engine/source-sdk-2013/src/game/server/hl2mp
-```
-
-The bootstrap intentionally downloads the default/master zip first:
-
-```text
-https://codeload.github.com/ValveSoftware/source-sdk-2013/zip/refs/heads/master
-```
-
-This avoids brittle branch assumptions and avoids GitHub Actions `git` auth/header issues. A git clone fallback is still present for debugging.
-MD
-
-# Make sure the workflow has the bootstrap step before build, without rewriting the whole file if it already does.
-python3 - <<'PY'
-from pathlib import Path
-p = Path('.github/workflows/windows-source-sdk-dlls.yml')
-s = p.read_text()
-if 'Bootstrap Source SDK 2013 MP' not in s and 'Bootstrap Source SDK 2013' not in s:
- marker = ' - name: Build Windows Source SDK DLLs\n'
- insert = ''' - name: Bootstrap Source SDK 2013\n run: |\n $ErrorActionPreference = 'Stop'\n powershell -NoProfile -ExecutionPolicy Bypass -File tools/bootstrap-source-sdk-2013-windows.ps1 *>&1 | Tee-Object -FilePath artifacts/windows-build-debug/bootstrap-source-sdk-2013.log\n\n'''
- if marker not in s:
- raise SystemExit('could not find Build Windows Source SDK DLLs step marker')
- s = s.replace(marker, insert + marker, 1)
-# Normalize the old step label if present.
-s = s.replace('Bootstrap Source SDK 2013 MP', 'Bootstrap Source SDK 2013')
-p.write_text(s)
-PY
-
-# Improve debug helper to print the new bootstrap files if the run fails.
-python3 - <<'PY'
-from pathlib import Path
-p = Path('tools/windows-workflow-debug-and-install.sh')
-if not p.exists():
- raise SystemExit(0)
-s = p.read_text()
-needle = 'grep -RInE "(error|failed|fatal|C[0-9]{4}|LNK[0-9]{4}|MSB[0-9]{4}|No patched OpenVibe DLLs)" "$OUT" 2>/dev/null | head -n 80 || true'
-replacement = '''grep -RInE "(error|failed|fatal|C[0-9]{4}|LNK[0-9]{4}|MSB[0-9]{4}|No patched OpenVibe DLLs|Could not bootstrap|suspiciously small)" "$OUT" 2>/dev/null | head -n 120 || true
-
-for f in \
- "$OUT"/openvibe-windows-build-debug/bootstrap-source-sdk-2013.log \
- "$OUT"/openvibe-windows-build-debug/bootstrap-curl-master-zip.log \
- "$OUT"/openvibe-windows-build-debug/bootstrap-zip-exception.txt \
- "$OUT"/openvibe-windows-build-debug/bootstrap-git-clone-master.log \
- "$OUT"/openvibe-windows-build-debug/bootstrap-git-exception.txt \
- "$OUT"/openvibe-windows-build-debug/build-sdk-windows.log; do
- if [[ -f "$f" ]]; then
- echo
- echo "[openvibe] tail: ${f#$ROOT/}"
- tail -n 80 "$f" || true
- fi
-done'''
-if needle in s:
- s = s.replace(needle, replacement)
-elif 'bootstrap-curl-master-zip.log' not in s:
- s += '\n\n# Extra bootstrap tails for current Windows workflow debugging.\n'
-p.write_text(s)
-PY
-
-echo "[openvibe] git diff summary"
-git diff --stat
-
-if ! git diff --quiet; then
- git add tools/bootstrap-source-sdk-2013-windows.ps1 docs/WINDOWS_WORKFLOW_SDK_BOOTSTRAP.md .github/workflows/windows-source-sdk-dlls.yml tools/windows-workflow-debug-and-install.sh
- git commit -m "Fix Windows SDK bootstrap download path"
- git push origin "$BRANCH"
-else
- echo "[openvibe] no changes to commit"
-fi
-
-if ! command -v gh >/dev/null 2>&1; then
- echo "[openvibe] gh missing; install/authenticate GitHub CLI, then run tools/windows-workflow-debug-and-install.sh after triggering workflow."
- exit 0
-fi
-
-echo "[openvibe] triggering workflow"
-gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$BRANCH" || true
-sleep 8
-RUN_ID="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"
-echo "[openvibe] watching run $RUN_ID"
-if gh run watch "$RUN_ID" --repo "$REPO" --exit-status; then
- echo "[openvibe] workflow passed"
-else
- echo "[openvibe warn] workflow failed; downloading diagnostics"
-fi
-
-tools/windows-workflow-debug-and-install.sh || true
-
-echo
-if [[ -x tools/verify-openvibe-dll-content.sh ]]; then
- tools/verify-openvibe-dll-content.sh || true
-fi
-
-echo
-cat <<'EOF'
-[openvibe] next if patched DLLs are installed:
- OPENVIBE_PROTON_LOG=1 OPENVIBE_CLIENT_MODE=proton tools/run-client-auto.sh 127.0.0.1 27015
-
-In game console:
- ov_join hub
- ov_menu
- ov_menu_servers
-EOF
deleted file mode 100755
deleted file mode 100755
@@ -1,206 +0,0 @@
@@ -1,206 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-cd "$ROOT"
-
-BRANCH="$(git rev-parse --abbrev-ref HEAD)"
-REPO="${OPENVIBE_GITHUB_REPO:-$(gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null || true)}"
-REPO="${REPO:-OpenVibers/OpenVibe.Games}"
-REPO="${REPO%.git}"
-WORKFLOW="windows-source-sdk-dlls.yml"
-
-say(){ printf '[openvibe] %s\n' "$*"; }
-
-say "fix Windows bootstrap to clone the real Source SDK 2013 MP branch"
-say "root=$ROOT"
-say "branch=$BRANCH"
-say "repo=$REPO"
-
-if ! command -v gh >/dev/null 2>&1; then
- echo "Missing gh. Run: sudo apt install gh && gh auth login" >&2
- exit 1
-fi
-
-if ! gh auth status >/dev/null 2>&1; then
- echo "gh is not authenticated. Run: gh auth login" >&2
- exit 1
-fi
-
-mkdir -p tools docs .github/workflows
-
-cat > tools/bootstrap-source-sdk-2013-windows.ps1 <<'PS1'
-$ErrorActionPreference = 'Stop'
-
-$Root = if ($env:OPENVIBE_ROOT) { (Resolve-Path $env:OPENVIBE_ROOT).Path } else { (Resolve-Path (Join-Path $PSScriptRoot '..')).Path }
-$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root 'engine/source-sdk-2013' }
-$Src = Join-Path $Sdk 'src'
-$Deps = Join-Path $Root '_deps'
-$LogDir = Join-Path $Root 'artifacts/windows-build-debug'
-New-Item -ItemType Directory -Force -Path $LogDir | Out-Null
-
-function Say($m) { Write-Host "[openvibe-sdk-bootstrap] $m" }
-function Has-HL2MP($base) {
- return ((Test-Path (Join-Path $base 'src/game/client/hl2mp')) -and (Test-Path (Join-Path $base 'src/game/server/hl2mp')))
-}
-function Run-Git($args, $logName) {
- $log = Join-Path $LogDir $logName
- Say "git $args"
- & git @args 2>&1 | Tee-Object -FilePath $log
- if ($LASTEXITCODE -ne 0) { throw "git $args failed with exit code $LASTEXITCODE. See $log" }
-}
-
-Say "root=$Root"
-Say "sdk=$Sdk"
-Say "logdir=$LogDir"
-
-if (Has-HL2MP $Sdk) {
- Say "existing SDK tree already contains HL2MP client/server"
- exit 0
-}
-
-New-Item -ItemType Directory -Force -Path $Deps | Out-Null
-
-# Valve's source-sdk-2013 repository is normally organized by branches: mp and sp.
-# Some forks/mirrors use root folders like mp/src. Support both layouts.
-$RepoUrl = if ($env:OPENVIBE_SOURCE_SDK_REPO) { $env:OPENVIBE_SOURCE_SDK_REPO } else { 'https://github.com/ValveSoftware/source-sdk-2013.git' }
-$ValveRepo = Join-Path $Deps 'source-sdk-2013-mp'
-
-if (Test-Path $ValveRepo) { Remove-Item -Recurse -Force $ValveRepo }
-
-$cloned = $false
-try {
- Say "cloning Valve Source SDK 2013 branch mp"
- Run-Git @('clone','--depth','1','--branch','mp',$RepoUrl,$ValveRepo) 'bootstrap-git-clone-mp.log'
- $cloned = $true
-} catch {
- Say "mp branch clone failed: $($_.Exception.Message)"
- if (Test-Path $ValveRepo) { Remove-Item -Recurse -Force $ValveRepo }
- Say "falling back to default branch clone"
- Run-Git @('clone','--depth','1',$RepoUrl,$ValveRepo) 'bootstrap-git-clone-default.log'
- $cloned = $true
-}
-
-if (-not $cloned) { throw "could not clone Source SDK repo" }
-
-"=== Valve repo root ===" | Out-File (Join-Path $LogDir 'valve-repo-root.txt')
-Get-ChildItem -Force $ValveRepo | Select-Object Mode,Length,Name | Format-Table -AutoSize | Out-File (Join-Path $LogDir 'valve-repo-root.txt') -Append
-"=== candidate src folders ===" | Out-File (Join-Path $LogDir 'valve-candidate-src.txt')
-Get-ChildItem -Path $ValveRepo -Recurse -Directory -Filter src -ErrorAction SilentlyContinue |
- Select-Object FullName | Format-List | Out-File (Join-Path $LogDir 'valve-candidate-src.txt') -Append
-
-$candidates = @(
- $ValveRepo,
- (Join-Path $ValveRepo 'mp'),
- (Join-Path $ValveRepo 'srcsdk/mp'),
- (Join-Path $ValveRepo 'source-sdk-2013/mp')
-)
-
-$SourceRoot = $null
-foreach ($candidate in $candidates) {
- if (Has-HL2MP $candidate) {
- $SourceRoot = $candidate
- break
- }
-}
-
-if (-not $SourceRoot) {
- $matches = Get-ChildItem -Path $ValveRepo -Recurse -Directory -Filter hl2mp -ErrorAction SilentlyContinue |
- Where-Object { $_.FullName -match '[\\/]src[\\/]game[\\/](client|server)[\\/]hl2mp$' }
- $matches | Select-Object FullName | Format-List | Out-File (Join-Path $LogDir 'hl2mp-folder-search.txt')
- throw "Could not find Source SDK 2013 MP HL2MP layout after clone. Check valve-repo-root.txt, valve-candidate-src.txt, and hl2mp-folder-search.txt."
-}
-
-Say "source root=$SourceRoot"
-Say "copying Source SDK MP layout into $Sdk"
-if (Test-Path $Sdk) { Remove-Item -Recurse -Force $Sdk }
-New-Item -ItemType Directory -Force -Path $Sdk | Out-Null
-Copy-Item -Path (Join-Path $SourceRoot '*') -Destination $Sdk -Recurse -Force
-
-if (!(Has-HL2MP $Sdk)) {
- Get-ChildItem -Force $Sdk | Select-Object Mode,Length,Name | Format-Table -AutoSize | Out-File (Join-Path $LogDir 'sdk-root-after-copy.txt')
- throw "Bootstrapped SDK is missing expected HL2MP folders after copy"
-}
-
-"=== SDK root after copy ===" | Out-File (Join-Path $LogDir 'sdk-root-after-copy.txt')
-Get-ChildItem -Force $Sdk | Select-Object Mode,Length,Name | Format-Table -AutoSize | Out-File (Join-Path $LogDir 'sdk-root-after-copy.txt') -Append
-"=== SDK src root after copy ===" | Out-File (Join-Path $LogDir 'sdk-src-root-after-copy.txt')
-Get-ChildItem -Force (Join-Path $Sdk 'src') | Select-Object Mode,Length,Name | Format-Table -AutoSize | Out-File (Join-Path $LogDir 'sdk-src-root-after-copy.txt') -Append
-
-Say "SDK bootstrapped successfully"
-PS1
-
-cat > docs/WINDOWS_WORKFLOW_SDK_BOOTSTRAP.md <<'MD'
-# Windows Source SDK bootstrap
-
-The GitHub Actions Windows runner does not have Alex's local `engine/source-sdk-2013` checkout. The workflow bootstraps it by cloning ValveSoftware/source-sdk-2013.
-
-Important detail: the Source SDK 2013 multiplayer code is normally on the `mp` branch, not necessarily in a `mp/` directory on the default branch. The bootstrap script now supports both layouts:
-
-- `source-sdk-2013` checked out directly on the `mp` branch, with `src/...` at repo root
-- older/mirrored layout with `mp/src/...`
-
-The workflow should not upload or install stock/stale DLLs. A DLL artifact is considered useful only when `client.dll` contains OpenVibe command strings such as `ov_join`, `ov_menu`, or `OpenVibe`.
-MD
-
-# Make workflow rerun on bootstrap changes and make bootstrap log more obvious.
-python3 - <<'PY'
-from pathlib import Path
-p = Path('.github/workflows/windows-source-sdk-dlls.yml')
-s = p.read_text()
-if 'tools/bootstrap-source-sdk-2013-windows.ps1' not in s:
- s = s.replace(' - "tools/build-sdk-windows.ps1"\n', ' - "tools/build-sdk-windows.ps1"\n - "tools/bootstrap-source-sdk-2013-windows.ps1"\n')
-# Ensure bootstrap step captures a specific log and prints it on failure before exiting.
-old = ''' - name: Bootstrap Source SDK 2013 MP\n run: |\n powershell -NoProfile -ExecutionPolicy Bypass -File tools/bootstrap-source-sdk-2013-windows.ps1 *>&1 | Tee-Object -FilePath artifacts/windows-build-debug/bootstrap-source-sdk-2013.log\n'''
-new = ''' - name: Bootstrap Source SDK 2013 MP\n run: |\n $ErrorActionPreference = 'Continue'\n New-Item -ItemType Directory -Force -Path artifacts/windows-build-debug | Out-Null\n powershell -NoProfile -ExecutionPolicy Bypass -File tools/bootstrap-source-sdk-2013-windows.ps1 *>&1 | Tee-Object -FilePath artifacts/windows-build-debug/bootstrap-source-sdk-2013.log\n $code = $LASTEXITCODE\n if ($code -ne 0) {\n Write-Host \"[openvibe] bootstrap failed; tail follows\"\n Get-Content artifacts/windows-build-debug/bootstrap-source-sdk-2013.log -Tail 120\n exit $code\n }\n'''
-if old in s:
- s = s.replace(old, new)
-p.write_text(s)
-PY
-
-# Improve debug downloader so it actually prints the useful logs next time.
-if [[ -f tools/windows-workflow-debug-and-install.sh ]]; then
-python3 - <<'PY'
-from pathlib import Path
-p = Path('tools/windows-workflow-debug-and-install.sh')
-s = p.read_text()
-needle = 'say "artifact files:"\nfind "$OUT" -type f | sort || true\n'
-insert = '''say "artifact files:"\nfind "$OUT" -type f | sort || true\n\nfor f in \\\n "$OUT"/openvibe-windows-build-debug/bootstrap-source-sdk-2013.log \\\n "$OUT"/openvibe-windows-build-debug/build-sdk-windows.log \\\n "$OUT"/openvibe-windows-build-debug/bootstrap-git-clone-mp.log \\\n "$OUT"/openvibe-windows-build-debug/valve-repo-root.txt \\\n "$OUT"/openvibe-windows-build-debug/valve-candidate-src.txt \\\n "$OUT"/openvibe-windows-build-debug/hl2mp-folder-search.txt; do\n if [[ -f "$f" ]]; then\n echo\n say "tail: ${f#$OUT/}"\n tail -n 120 "$f" || true\n fi\ndone\n'''
-if needle in s and 'bootstrap-git-clone-mp.log' not in s:
- s = s.replace(needle, insert)
-p.write_text(s)
-PY
-fi
-
-say "git diff summary"
-git diff --stat
-
-git add .github/workflows/windows-source-sdk-dlls.yml tools/bootstrap-source-sdk-2013-windows.ps1 docs/WINDOWS_WORKFLOW_SDK_BOOTSTRAP.md tools/windows-workflow-debug-and-install.sh
-
-if git diff --cached --quiet; then
- say "nothing to commit"
-else
- git commit -m "Fix Windows Source SDK MP branch bootstrap"
- git push origin "$BRANCH"
-fi
-
-say "triggering workflow"
-gh workflow run "$WORKFLOW" --ref "$BRANCH"
-sleep 6
-RUN_ID="$(gh run list --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId -q '.[0].databaseId')"
-say "watching run $RUN_ID"
-set +e
-gh run watch "$RUN_ID" --exit-status
-STATUS=$?
-set -e
-
-say "downloading diagnostics/artifacts"
-tools/windows-workflow-debug-and-install.sh || true
-
-if [[ $STATUS -eq 0 ]]; then
- say "workflow succeeded"
-else
- say "workflow failed again; paste the tail printed above if it does not clearly show the next compile error"
-fi
-
-exit $STATUS
deleted file mode 100755
deleted file mode 100755
@@ -1,183 +0,0 @@
@@ -1,183 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$(pwd)}"
-if [[ ! -d "$ROOT/.git" ]]; then
- ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-fi
-cd "$ROOT"
-
-say(){ printf '[openvibe] %s\n' "$*"; }
-
-say "fix Windows build: build Source SDK deps + make python available"
-say "root=$ROOT"
-say "branch=$(git branch --show-current)"
-
-python3 - <<'PY'
-from pathlib import Path
-import re
-
-root = Path.cwd()
-workflow = root / '.github/workflows/windows-source-sdk-dlls.yml'
-build = root / 'tools/build-sdk-windows.ps1'
-
-# ---------------------------------------------------------------------------
-# 1) Workflow: GitHub Windows runner has Python installed in some contexts, but
-# Source SDK custom build steps call literal "python" from inside vcvars/msbuild.
-# setup-python makes that deterministic.
-# ---------------------------------------------------------------------------
-y = workflow.read_text()
-if 'actions/setup-python@v5' not in y:
- needle = ''' - name: Setup MSBuild\n uses: microsoft/setup-msbuild@v2\n'''
- repl = needle + '''\n - name: Setup Python\n uses: actions/setup-python@v5\n with:\n python-version: "3.x"\n'''
- if needle not in y:
- raise SystemExit('Could not find Setup MSBuild block in workflow')
- y = y.replace(needle, repl)
-workflow.write_text(y)
-
-# ---------------------------------------------------------------------------
-# 2) Build script: install a python command/shim onto PATH after vcvars64.
-# ---------------------------------------------------------------------------
-s = build.read_text()
-helper = r'''
-function Ensure-PythonOnPath {
- $pyCmd = Get-Command python.exe -ErrorAction SilentlyContinue
- if (-not $pyCmd) { $pyCmd = Get-Command python -ErrorAction SilentlyContinue }
- if ($pyCmd) {
- Say "python=$($pyCmd.Source)"
- try { & $pyCmd.Source --version 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "python-version.txt") | Out-Null } catch {}
- return
- }
-
- $pyLauncher = Get-Command py.exe -ErrorAction SilentlyContinue
- if ($pyLauncher) {
- $shimDir = Join-Path $Root "_tools/python-shim"
- New-Item -ItemType Directory -Force -Path $shimDir | Out-Null
- $shim = Join-Path $shimDir "python.bat"
- "@echo off`r`npy -3 %*`r`n" | Set-Content -Encoding ascii $shim
- $env:PATH = "$shimDir;$env:PATH"
- Say "python shim=$shim"
- & python --version 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "python-version.txt") | Out-Null
- return
- }
-
- throw "python was not found on PATH and py.exe was not found. Add actions/setup-python before the build step."
-}
-'''
-if 'function Ensure-PythonOnPath' not in s:
- marker = 'function Require($p, $msg) {\n if (!(Test-Path $p)) { throw "$msg`nMissing: $p" }\n}\n'
- if marker not in s:
- raise SystemExit('Could not find Require function marker')
- s = s.replace(marker, marker + helper + '\n')
-
-call_marker = 'where.exe lib | Out-File (Join-Path $LogDir "where-lib.txt")\n'
-if 'Ensure-PythonOnPath' in s and 'Ensure-PythonOnPath\n\n# Apply OpenVibe' not in s:
- if call_marker not in s:
- raise SystemExit('Could not find where-lib marker')
- s = s.replace(call_marker, call_marker + 'Ensure-PythonOnPath\n\n')
-
-# ---------------------------------------------------------------------------
-# 3) Build dependency libs before client/server projects.
-# Direct .vcxproj builds do not necessarily build VPC's implicit lib deps.
-# Current failure was missing src/lib/public/x64/mathlib.lib.
-# ---------------------------------------------------------------------------
-deps_block = r'''
-function Find-ProjectByPattern([string]$pattern) {
- return @(Get-ChildItem -Path $Src -Recurse -File -Filter $pattern -ErrorAction SilentlyContinue |
- Sort-Object @{ Expression = { if ($_.FullName -match "\\(mathlib|tier1|raytrace|vgui2|fgdlib)\\") { 0 } else { 1 } } }, FullName)
-}
-
-function Copy-LibIfNeeded([string]$libName, [string]$destDir) {
- New-Item -ItemType Directory -Force -Path $destDir | Out-Null
- $dest = Join-Path $destDir $libName
- if (Test-Path $dest) { return }
- $found = Get-ChildItem -Path $Sdk -Recurse -File -Filter $libName -ErrorAction SilentlyContinue |
- Where-Object { $_.FullName -notmatch "artifacts|windows-build-debug|_deps" } |
- Sort-Object LastWriteTime -Descending |
- Select-Object -First 1
- if ($found) {
- Say "copying fallback lib $($found.FullName) -> $dest"
- Copy-Item $found.FullName $dest -Force
- }
-}
-
-function Build-SourceSdkDependencyProjects {
- Say "building Source SDK dependency libraries before HL2MP client/server"
-
- $patterns = @(
- "tier1*_win64.vcxproj",
- "mathlib*_win64.vcxproj",
- "raytrace*_win64.vcxproj",
- "vgui_controls*_win64.vcxproj",
- "matsys_controls*_win64.vcxproj",
- "fgdlib*_win64.vcxproj"
- )
-
- $projects = @()
- foreach ($pat in $patterns) {
- $projects += Find-ProjectByPattern $pat
- }
- $projects = @($projects | Sort-Object FullName -Unique)
-
- "=== dependency projects ===" | Out-File (Join-Path $LogDir "dependency-projects.txt")
- $projects | Select-Object FullName,Length,LastWriteTime | Format-List | Out-File (Join-Path $LogDir "dependency-projects.txt") -Append
-
- foreach ($dep in $projects) {
- $label = "dep-$($dep.BaseName -replace '[^A-Za-z0-9]+','_')"
- [void](Invoke-MSBuildProject $dep $label)
- }
-
- $publicX64 = Join-Path $Src "lib/public/x64"
- Copy-LibIfNeeded "mathlib.lib" $publicX64
- Copy-LibIfNeeded "tier1.lib" $publicX64
- Copy-LibIfNeeded "raytrace.lib" $publicX64
- Copy-LibIfNeeded "vgui_controls.lib" $publicX64
- Copy-LibIfNeeded "matsys_controls.lib" $publicX64
-
- "=== public x64 libs after deps ===" | Out-File (Join-Path $LogDir "public-x64-libs-after-deps.txt")
- if (Test-Path $publicX64) {
- Get-ChildItem -Path $publicX64 -File -ErrorAction SilentlyContinue |
- Select-Object FullName,Length,LastWriteTime |
- Format-List | Out-File (Join-Path $LogDir "public-x64-libs-after-deps.txt") -Append
- }
-}
-'''
-if 'function Build-SourceSdkDependencyProjects' not in s:
- marker = 'function Invoke-MSBuildProject([System.IO.FileInfo]$proj, [string]$label) {'
- idx = s.find(marker)
- if idx == -1:
- raise SystemExit('Could not find Invoke-MSBuildProject function marker')
- # place deps block after Invoke-MSBuildProject function by locating the next Generate projects comment
- gen_marker = '# Generate projects if no relevant vcxproj files exist yet.'
- gen_idx = s.find(gen_marker)
- if gen_idx == -1:
- raise SystemExit('Could not find Generate projects marker')
- s = s[:gen_idx] + deps_block + '\n' + s[gen_idx:]
-
-build_call_marker = '$beforeBuild = Get-Date\n\nif (-not (Invoke-MSBuildProject $clientProject "client")) {'
-if 'Build-SourceSdkDependencyProjects\n\n$beforeBuild = Get-Date' not in s:
- if build_call_marker not in s:
- raise SystemExit('Could not find beforeBuild/client build marker')
- s = s.replace(build_call_marker, 'Build-SourceSdkDependencyProjects\n\n$beforeBuild = Get-Date\n\nif (-not (Invoke-MSBuildProject $clientProject "client")) {')
-
-build.write_text(s)
-PY
-
-say "git diff summary"
-git diff --stat
-
-git add .github/workflows/windows-source-sdk-dlls.yml tools/build-sdk-windows.ps1
-if git diff --cached --quiet; then
- say "nothing to commit"
-else
- git commit -m "Build Windows Source SDK dependencies before HL2MP DLLs"
-fi
-
-git push
-
-say "triggering clean Windows DLL build"
-if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then
- tools/trigger-windows-dll-build-clean.sh
-else
- tools/gh-windows-build-and-install.sh
-fi
deleted file mode 100755
deleted file mode 100755
@@ -1,168 +0,0 @@
@@ -1,168 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
-cd "$ROOT"
-BRANCH="$(git rev-parse --abbrev-ref HEAD)"
-WORKFLOW="windows-source-sdk-dlls.yml"
-
-if [[ -x tools/openvibe-gh-repo.sh ]]; then
- # shellcheck disable=SC1091
- REPO="$(tools/openvibe-gh-repo.sh)"
-else
- REPO="$(git remote get-url origin | sed -E 's#^[email protected]:##; s#^https://github.com/##; s#\.git$##')"
-fi
-REPO="${REPO%.git}"
-
-echo "[openvibe] fix Windows MSBuild Python shim + rerun"
-echo "[openvibe] root=$ROOT"
-echo "[openvibe] branch=$BRANCH"
-echo "[openvibe] repo=$REPO"
-
-# Keep setup-python in the workflow. This is still useful, but vcvars/msbuild can lose PATH,
-# so the PowerShell build script will also create its own python.bat shim.
-python3 - <<'PY'
-from pathlib import Path
-p = Path('.github/workflows/windows-source-sdk-dlls.yml')
-if p.exists():
- s = p.read_text()
- if 'actions/setup-python@v5' not in s:
- marker = ' - name: Setup MSBuild\n'
- block = """ - name: Setup Python\n uses: actions/setup-python@v5\n with:\n python-version: '3.x'\n\n"""
- if marker in s:
- s = s.replace(marker, block + marker, 1)
- else:
- s += "\n" + block
- p.write_text(s)
-PY
-
-python3 - <<'PY'
-from pathlib import Path
-p = Path('tools/build-sdk-windows.ps1')
-s = p.read_text()
-marker = 'OPENVIBE_PYTHON_SHIM_FOR_MSBUILD'
-func = r'''
-# OPENVIBE_PYTHON_SHIM_FOR_MSBUILD
-function Ensure-PythonCommandForMSBuild {
- $shimDir = Join-Path $Root "artifacts/python-shim"
- New-Item -ItemType Directory -Force -Path $shimDir | Out-Null
- $log = Join-Path $LogDir "python-version.txt"
- "=== Ensure-PythonCommandForMSBuild ===" | Out-File $log
- "initial PATH=$env:PATH" | Out-File $log -Append
- "pythonLocation=$env:pythonLocation" | Out-File $log -Append
- "Python_ROOT_DIR=$env:Python_ROOT_DIR" | Out-File $log -Append
-
- $candidates = New-Object System.Collections.Generic.List[string]
- foreach ($base in @($env:pythonLocation, $env:Python_ROOT_DIR, $env:Python3_ROOT_DIR)) {
- if ($base) {
- [void]$candidates.Add((Join-Path $base "python.exe"))
- [void]$candidates.Add((Join-Path $base "python3.exe"))
- }
- }
-
- foreach ($name in @("python.exe", "python3.exe", "py.exe")) {
- $cmd = Get-Command $name -ErrorAction SilentlyContinue
- if ($cmd -and $cmd.Source) { [void]$candidates.Add($cmd.Source) }
- }
-
- foreach ($rootCandidate in @("C:\hostedtoolcache\windows\Python", "C:\hostedtoolcache\windows\PyPy")) {
- if (Test-Path $rootCandidate) {
- Get-ChildItem -Path $rootCandidate -Recurse -File -Filter "python.exe" -ErrorAction SilentlyContinue |
- Sort-Object LastWriteTime -Descending |
- ForEach-Object { [void]$candidates.Add($_.FullName) }
- }
- }
-
- $py = $null
- foreach ($cand in ($candidates | Select-Object -Unique)) {
- if ($cand -and (Test-Path $cand)) {
- $py = (Resolve-Path $cand).Path
- break
- }
- }
-
- if (-not $py) {
- "candidate list:" | Out-File $log -Append
- $candidates | Out-File $log -Append
- throw "Python executable not found after setup-python/vcvars. Check python-version.txt in the debug artifact."
- }
-
- $pyDir = Split-Path -Parent $py
- $bat = Join-Path $shimDir "python.bat"
- $cmdFile = Join-Path $shimDir "python.cmd"
- $batContent = @"
-@echo off
-"$py" %*
-exit /b %ERRORLEVEL%
-"@
- Set-Content -Encoding ascii -Path $bat -Value $batContent
- Set-Content -Encoding ascii -Path $cmdFile -Value $batContent
-
- # Put the shim first so custom build steps that literally run `python` find it.
- $env:PATH = "$shimDir;$pyDir;$env:PATH"
- "selected python=$py" | Out-File $log -Append
- "shimDir=$shimDir" | Out-File $log -Append
- "updated PATH=$env:PATH" | Out-File $log -Append
- & $py --version 2>&1 | Tee-Object -FilePath $log -Append
- & cmd.exe /d /s /c "where python" 2>&1 | Tee-Object -FilePath $log -Append
-}
-'''
-
-if marker not in s:
- # Insert after Require function, before Find-VcVars64.
- needle = 'function Find-VcVars64 {'
- if needle not in s:
- raise SystemExit('Could not find Find-VcVars64 insertion point in tools/build-sdk-windows.ps1')
- s = s.replace(needle, func + '\n' + needle, 1)
-
-# Ensure it is called after tool where dumps in the x64 dev shell, before apply-openvibe-sdk/build.
-call = 'Ensure-PythonCommandForMSBuild'
-if call not in s.split('# Apply OpenVibe source files', 1)[0]:
- needle = 'where.exe lib | Out-File (Join-Path $LogDir "where-lib.txt")\n'
- if needle in s:
- s = s.replace(needle, needle + call + '\n', 1)
- else:
- needle2 = 'Say "cl.exe already available"\n'
- if needle2 not in s:
- raise SystemExit('Could not find tool setup insertion point in tools/build-sdk-windows.ps1')
- s = s.replace(needle2, needle2 + call + '\n', 1)
-
-p.write_text(s)
-PY
-
-# Commit patch.
-echo "[openvibe] git diff summary"
-git diff --stat .github/workflows/windows-source-sdk-dlls.yml tools/build-sdk-windows.ps1 || true
-
-git add .github/workflows/windows-source-sdk-dlls.yml tools/build-sdk-windows.ps1
-if git diff --cached --quiet; then
- echo "[openvibe] no code changes to commit"
-else
- git commit -m "Make python available to Windows MSBuild custom steps"
-fi
-
-echo "[openvibe] pushing $BRANCH"
-git push origin "$BRANCH"
-
-# Trigger and watch/download using the repo helper if available.
-if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then
- echo "[openvibe] triggering clean Windows DLL build"
- tools/trigger-windows-dll-build-clean.sh
-else
- echo "[openvibe] triggering workflow"
- gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$BRANCH"
- sleep 8
- 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 --repo "$REPO" "$RUN_ID" --exit-status || true
- if [[ -x tools/windows-workflow-debug-and-install.sh ]]; then
- tools/windows-workflow-debug-and-install.sh || true
- fi
-fi
-
-if [[ -x tools/verify-openvibe-dll-content.sh ]]; then
- echo "[openvibe] verify DLL content"
- tools/verify-openvibe-dll-content.sh || true
-fi
-
-echo "[openvibe] done"
deleted file mode 100755
deleted file mode 100755
@@ -1,521 +0,0 @@
@@ -1,521 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-cd "$ROOT"
-BRANCH="$(git rev-parse --abbrev-ref HEAD)"
-REPO="${OPENVIBE_GITHUB_REPO:-OpenVibers/OpenVibe.Games}"
-REPO="${REPO%.git}"
-WORKFLOW="windows-source-sdk-dlls.yml"
-
-echo "[openvibe] fix Windows SDK bootstrap + real DLL artifact validation"
-echo "[openvibe] root=$ROOT"
-echo "[openvibe] branch=$BRANCH"
-echo "[openvibe] repo=$REPO"
-
-mkdir -p tools .github/workflows docs
-
-cat > tools/bootstrap-source-sdk-2013-windows.ps1 <<'PS1'
-$ErrorActionPreference = 'Stop'
-
-$Root = if ($env:OPENVIBE_ROOT) { (Resolve-Path $env:OPENVIBE_ROOT).Path } else { (Resolve-Path (Join-Path $PSScriptRoot '..')).Path }
-$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root 'engine/source-sdk-2013' }
-$Src = Join-Path $Sdk 'src'
-$Deps = Join-Path $Root '_deps'
-$ValveRepo = Join-Path $Deps 'source-sdk-2013'
-$LogDir = Join-Path $Root 'artifacts/windows-build-debug'
-New-Item -ItemType Directory -Force -Path $LogDir | Out-Null
-
-function Say($m) { Write-Host "[openvibe-sdk-bootstrap] $m" }
-
-Say "root=$Root"
-Say "sdk=$Sdk"
-
-if ((Test-Path (Join-Path $Src 'game/client/hl2mp')) -and (Test-Path (Join-Path $Src 'game/server/hl2mp'))) {
- Say "existing SDK tree looks usable"
- exit 0
-}
-
-New-Item -ItemType Directory -Force -Path $Deps | Out-Null
-
-if (!(Test-Path (Join-Path $ValveRepo '.git'))) {
- Say "cloning ValveSoftware/source-sdk-2013"
- if (Test-Path $ValveRepo) { Remove-Item -Recurse -Force $ValveRepo }
- git clone --depth 1 https://github.com/ValveSoftware/source-sdk-2013.git $ValveRepo 2>&1 | Tee-Object -FilePath (Join-Path $LogDir 'bootstrap-git-clone.log')
-} else {
- Say "updating cached Valve source-sdk-2013"
- Push-Location $ValveRepo
- git fetch --depth 1 origin 2>&1 | Tee-Object -FilePath (Join-Path $LogDir 'bootstrap-git-fetch.log')
- git reset --hard origin/master 2>&1 | Tee-Object -FilePath (Join-Path $LogDir 'bootstrap-git-reset.log')
- Pop-Location
-}
-
-$Mp = Join-Path $ValveRepo 'mp'
-if (!(Test-Path (Join-Path $Mp 'src'))) {
- Get-ChildItem -Force $ValveRepo | Select-Object Mode,Length,Name | Format-Table -AutoSize | Out-File (Join-Path $LogDir 'valve-repo-root.txt')
- throw "Valve source-sdk-2013 clone does not contain mp/src at $Mp/src"
-}
-
-Say "copying mp branch layout into $Sdk"
-if (Test-Path $Sdk) { Remove-Item -Recurse -Force $Sdk }
-New-Item -ItemType Directory -Force -Path $Sdk | Out-Null
-Copy-Item -Path (Join-Path $Mp '*') -Destination $Sdk -Recurse -Force
-
-if (!(Test-Path (Join-Path $Src 'game/client/hl2mp')) -or !(Test-Path (Join-Path $Src 'game/server/hl2mp'))) {
- Get-ChildItem -Force $Sdk | Select-Object Mode,Length,Name | Format-Table -AutoSize | Out-File (Join-Path $LogDir 'sdk-root-after-copy.txt')
- throw "Bootstrapped SDK is missing expected HL2MP folders"
-}
-
-Say "SDK bootstrapped successfully"
-PS1
-
-cat > tools/build-sdk-windows.ps1 <<'PS1'
-param(
- [switch]$InDevShell
-)
-
-$ErrorActionPreference = "Stop"
-
-$Root = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
-$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root "engine/source-sdk-2013" }
-$Src = Join-Path $Sdk "src"
-$Mod = Join-Path $Root "game/openvibe.games"
-$OutBin = Join-Path $Mod "bin"
-$LogDir = Join-Path $Root "artifacts/windows-build-debug"
-New-Item -ItemType Directory -Force -Path $LogDir | Out-Null
-
-function Say($m) { Write-Host "[openvibe-win] $m" }
-function Require($p, $msg) { if (!(Test-Path $p)) { throw "$msg`nMissing: $p" } }
-
-function Enter-MsvcDevShellIfNeeded {
- if (Get-Command cl.exe -ErrorAction SilentlyContinue) {
- Say "cl.exe already available"
- return
- }
- if ($InDevShell) { throw "cl.exe still not found after entering MSVC dev shell" }
-
- $vswhere = Join-Path ${env:ProgramFiles(x86)} "Microsoft Visual Studio/Installer/vswhere.exe"
- if (!(Test-Path $vswhere)) { throw "vswhere.exe not found; Visual Studio Build Tools are not installed" }
-
- $install = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
- if (!$install) { throw "Could not find Visual Studio installation with VC x86/x64 tools" }
-
- $vcvars = Join-Path $install "VC/Auxiliary/Build/vcvars32.bat"
- if (!(Test-Path $vcvars)) { throw "vcvars32.bat not found at $vcvars" }
-
- Say "relaunching through MSVC x86 dev shell: $vcvars"
- $self = $PSCommandPath
- $cmd = "call `"$vcvars`" && powershell -NoProfile -ExecutionPolicy Bypass -File `"$self`" -InDevShell"
- & cmd.exe /d /s /c $cmd
- exit $LASTEXITCODE
-}
-
-Say "root=$Root"
-Say "sdk=$Sdk"
-Say "src=$Src"
-
-$env:OPENVIBE_ROOT = $Root
-$env:OPENVIBE_SDK = $Sdk
-
-if (!(Test-Path $Src)) {
- Say "SDK src missing; bootstrapping Source SDK 2013 MP"
- & powershell -NoProfile -ExecutionPolicy Bypass -File (Join-Path $Root "tools/bootstrap-source-sdk-2013-windows.ps1")
- if ($LASTEXITCODE -ne 0) { throw "bootstrap-source-sdk-2013-windows.ps1 failed with exit code $LASTEXITCODE" }
-}
-
-Require $Src "Source SDK checkout is missing from this runner."
-Require (Join-Path $Src "game/client/hl2mp") "Source SDK HL2MP client tree missing."
-Require (Join-Path $Src "game/server/hl2mp") "Source SDK HL2MP server tree missing."
-
-Enter-MsvcDevShellIfNeeded
-
-# Do not let old/stock repo DLLs get mistaken for a successful new build.
-New-Item -ItemType Directory -Force -Path $OutBin | Out-Null
-Remove-Item (Join-Path $OutBin "client.dll") -Force -ErrorAction SilentlyContinue
-Remove-Item (Join-Path $OutBin "server.dll") -Force -ErrorAction SilentlyContinue
-Remove-Item -Recurse -Force (Join-Path $Sdk "game/mod_hl2mp/bin") -ErrorAction SilentlyContinue
-
-# Apply OpenVibe source files using Git Bash. Skip the Linux QuickJS build inside apply-openvibe-sdk.sh.
-$bash = (Get-Command bash -ErrorAction SilentlyContinue)
-if ($bash) {
- Say "applying OpenVibe SDK patch through bash"
- $env:OPENVIBE_SKIP_QJS_BUILD = "1"
- & bash "$Root/tools/apply-openvibe-sdk.sh" 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "apply-openvibe-sdk.log")
- if ($LASTEXITCODE -ne 0) { throw "apply-openvibe-sdk.sh failed with exit code $LASTEXITCODE" }
-} else {
- throw "Git Bash is required on the Windows runner to apply the OpenVibe SDK patch."
-}
-
-Say "building QuickJS Windows static library"
-& powershell -NoProfile -ExecutionPolicy Bypass -File (Join-Path $Root "tools/build-quickjs-lib-windows.ps1") 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "quickjs-windows.log")
-if ($LASTEXITCODE -ne 0) { throw "build-quickjs-lib-windows.ps1 failed with exit code $LASTEXITCODE" }
-
-Set-Location $Src
-
-# Generate Visual Studio project files if needed.
-$solutions = @(Get-ChildItem -Path $Src -Recurse -Filter "*.sln" -ErrorAction SilentlyContinue)
-if ($solutions.Count -eq 0) {
- Say "no .sln files found; trying Source SDK project generators"
- $generators = @(
- (Join-Path $Src "creategameprojects.bat"),
- (Join-Path $Src "createallprojects.bat"),
- (Join-Path $Src "createprojects.bat")
- )
- $ran = $false
- foreach ($gen in $generators) {
- if (Test-Path $gen) {
- Say "running $gen"
- & cmd /d /s /c "`"$gen`"" 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "project-generation.log")
- $ran = $true
- break
- }
- }
-
- if (-not $ran) {
- $vpc = Join-Path $Src "devtools/bin/vpc.exe"
- if (Test-Path $vpc) {
- Say "running vpc.exe fallback"
- & $vpc /hl2mp +game /mksln OpenVibe_HL2MP.sln 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "vpc.log")
- $ran = $true
- }
- }
-
- $solutions = @(Get-ChildItem -Path $Src -Recurse -Filter "*.sln" -ErrorAction SilentlyContinue)
- if ($solutions.Count -eq 0) {
- Get-ChildItem -Path $Src -Force | Select-Object Mode,Length,Name | Format-Table -AutoSize | Out-File (Join-Path $LogDir "src-root-after-generator.txt")
- throw "No Visual Studio solution was generated. Check openvibe-windows-build-debug artifact."
- }
-}
-
-Say "solutions found:"
-$solutions | ForEach-Object { Say " $($_.FullName)" }
-
-$solution = $solutions |
- Where-Object { $_.Name -match "hl2mp|game|sdk|everything|OpenVibe" } |
- Select-Object -First 1
-if (-not $solution) { $solution = $solutions | Select-Object -First 1 }
-Say "selected solution=$($solution.FullName)"
-
-$configs = @("Release", "Release_HL2MP", "Release HL2MP")
-$platforms = @("Win32", "x86")
-$targets = @("client_hl2mp", "server_hl2mp", "client", "server", "Build")
-
-$builtAny = $false
-foreach ($cfg in $configs) {
- foreach ($plat in $platforms) {
- foreach ($target in $targets) {
- Say "msbuild cfg=$cfg platform=$plat target=$target"
- $log = Join-Path $LogDir "msbuild-$($cfg -replace '[^A-Za-z0-9]','_')-$plat-$target.log"
- & msbuild $solution.FullName /m /p:Configuration=$cfg /p:Platform=$plat /p:PlatformToolset=v143 /t:$target /v:minimal /nologo 2>&1 | Tee-Object -FilePath $log
- if ($LASTEXITCODE -eq 0) { $builtAny = $true; break }
- }
- if ($builtAny) { break }
- }
- if ($builtAny) { break }
-}
-
-if (-not $builtAny) { throw "MSBuild could not build any known Source SDK HL2MP target. Check uploaded msbuild logs." }
-
-New-Item -ItemType Directory -Force -Path $OutBin | Out-Null
-
-$client = Get-ChildItem -Path $Sdk -Recurse -Filter client.dll -ErrorAction SilentlyContinue |
- Where-Object { $_.FullName -match "hl2mp|mod_hl2mp|client" } |
- Sort-Object LastWriteTime -Descending |
- Select-Object -First 1
-$server = Get-ChildItem -Path $Sdk -Recurse -Filter server.dll -ErrorAction SilentlyContinue |
- Where-Object { $_.FullName -match "hl2mp|mod_hl2mp|server" } |
- Sort-Object LastWriteTime -Descending |
- Select-Object -First 1
-
-if (-not $client) { throw "client.dll was not produced" }
-if (-not $server) { throw "server.dll was not produced" }
-
-Say "copy client=$($client.FullName)"
-Say "copy server=$($server.FullName)"
-Copy-Item $client.FullName (Join-Path $OutBin "client.dll") -Force
-Copy-Item $server.FullName (Join-Path $OutBin "server.dll") -Force
-
-$clientText = & strings.exe (Join-Path $OutBin "client.dll") 2>$null | Select-String -Pattern "ov_join|ov_menu|OpenVibe" -Quiet
-if (-not $clientText) { throw "Built client.dll does not contain OpenVibe strings; refusing to publish stale/stock DLL" }
-
-Say "done"
-PS1
-
-python3 - <<'PY'
-from pathlib import Path
-p = Path('tools/apply-openvibe-sdk.sh')
-s = p.read_text()
-old = '''copy_tree() {\n local src="$1"\n local dst="$2"\n mkdir -p "$dst"\n rsync -a --delete "$src/" "$dst/"\n echo "[openvibe-sdk] copied tree ${dst#$SDK/}"\n}\n'''
-new = '''copy_tree() {\n local src="$1"\n local dst="$2"\n mkdir -p "$dst"\n if command -v rsync >/dev/null 2>&1; then\n rsync -a --delete "$src/" "$dst/"\n else\n rm -rf "$dst"\n mkdir -p "$dst"\n cp -R "$src"/. "$dst"/\n fi\n echo "[openvibe-sdk] copied tree ${dst#$SDK/}"\n}\n'''
-if old not in s:
- raise SystemExit('Could not patch copy_tree() in tools/apply-openvibe-sdk.sh')
-p.write_text(s.replace(old, new))
-PY
-
-cat > .github/workflows/windows-source-sdk-dlls.yml <<'YAML'
-name: Build Windows Source DLLs
-
-on:
- workflow_dispatch:
- inputs:
- diagnostics:
- description: "Upload verbose build diagnostics"
- required: false
- default: "1"
- push:
- branches:
- - codex/openvibe-next-steps
- paths:
- - ".github/workflows/windows-source-sdk-dlls.yml"
- - "tools/bootstrap-source-sdk-2013-windows.ps1"
- - "tools/build-sdk-windows.ps1"
- - "tools/build-quickjs-lib-windows.ps1"
- - "tools/apply-openvibe-sdk.sh"
- - "sdk/openvibe/**"
- - "game/openvibe.games/js/**"
-
-permissions:
- contents: read
- actions: read
-
-jobs:
- windows-dlls:
- name: Windows client.dll/server.dll
- runs-on: windows-2022
- defaults:
- run:
- shell: pwsh
-
- steps:
- - name: Checkout OpenVibe
- uses: actions/checkout@v4
- with:
- fetch-depth: 1
-
- - name: Configure git line endings
- run: |
- git config --global core.autocrlf false
- git config --global core.eol lf
- git status --short
-
- - name: Setup MSBuild
- uses: microsoft/setup-msbuild@v2
-
- - name: Show toolchain and tree
- run: |
- $ErrorActionPreference = 'Continue'
- New-Item -ItemType Directory -Force -Path artifacts/windows-build-debug | Out-Null
- Write-Host "pwd=$(Get-Location)"
- git rev-parse --short HEAD
- where.exe msbuild
- where.exe cl
- where.exe link
- where.exe bash
- where.exe git
- Get-ChildItem -Force | Select-Object Mode,Length,Name | Tee-Object -FilePath artifacts/windows-build-debug/repo-root-before-build.txt
- if (Test-Path engine) { Get-ChildItem -Force engine | Select-Object Mode,Length,Name | Tee-Object -FilePath artifacts/windows-build-debug/engine-before-build.txt } else { Write-Host "[miss] engine directory" }
-
- - name: Bootstrap Source SDK 2013 MP
- run: |
- $ErrorActionPreference = 'Stop'
- powershell -NoProfile -ExecutionPolicy Bypass -File tools/bootstrap-source-sdk-2013-windows.ps1 *>&1 | Tee-Object -FilePath artifacts/windows-build-debug/bootstrap-source-sdk-2013.log
-
- - name: Build Windows Source SDK DLLs
- id: build
- run: |
- $ErrorActionPreference = 'Stop'
- New-Item -ItemType Directory -Force -Path artifacts/windows-build-debug | Out-Null
- powershell -NoProfile -ExecutionPolicy Bypass -File tools/build-sdk-windows.ps1 *>&1 | Tee-Object -FilePath artifacts/windows-build-debug/build-sdk-windows.log
-
- - name: Stage build outputs and diagnostics
- if: always()
- run: |
- $ErrorActionPreference = 'Continue'
- New-Item -ItemType Directory -Force -Path artifacts/windows-dlls | Out-Null
- New-Item -ItemType Directory -Force -Path artifacts/windows-build-debug | Out-Null
-
- "=== repo ===" | Out-File artifacts/windows-build-debug/tree.txt
- Get-ChildItem -Force | Format-Table -AutoSize | Out-File artifacts/windows-build-debug/tree.txt -Append
- "=== dll search ===" | Out-File artifacts/windows-build-debug/dll-search.txt
- Get-ChildItem -Recurse -Force -Include client.dll,server.dll -ErrorAction SilentlyContinue |
- Select-Object FullName,Length,LastWriteTime |
- Format-List | Out-File artifacts/windows-build-debug/dll-search.txt -Append
- "=== project/log search ===" | Out-File artifacts/windows-build-debug/project-search.txt
- Get-ChildItem -Recurse -Force -Include *.sln,*.vcxproj,*.vcproj,*.log -ErrorAction SilentlyContinue |
- Select-Object FullName,Length,LastWriteTime |
- Format-List | Out-File artifacts/windows-build-debug/project-search.txt -Append
-
- $client = "game/openvibe.games/bin/client.dll"
- $server = "game/openvibe.games/bin/server.dll"
- $clientOk = $false
- if (Test-Path $client) {
- $text = strings.exe $client 2>$null | Select-String -Pattern "ov_join|ov_menu|OpenVibe" -Quiet
- if ($text) { $clientOk = $true }
- }
- if ($clientOk -and (Test-Path $server)) {
- Copy-Item $client artifacts/windows-dlls/client.dll -Force
- Copy-Item $server artifacts/windows-dlls/server.dll -Force
- Write-Host "[ok] staged patched OpenVibe Windows DLLs"
- } else {
- Write-Host "[miss] not staging DLLs because patched OpenVibe client.dll was not produced"
- "No patched OpenVibe DLLs staged. Check build logs." | Out-File artifacts/windows-build-debug/no-patched-dlls.txt
- }
-
- - name: Upload Windows DLL artifact
- if: always()
- uses: actions/upload-artifact@v4
- with:
- name: openvibe-windows-dlls
- path: artifacts/windows-dlls/**
- if-no-files-found: warn
-
- - name: Upload Windows build diagnostics
- if: always()
- uses: actions/upload-artifact@v4
- with:
- name: openvibe-windows-build-debug
- path: artifacts/windows-build-debug/**
- if-no-files-found: warn
-YAML
-
-cat > tools/windows-workflow-debug-and-install.sh <<'BASH2'
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-cd "$ROOT"
-REPO="${OPENVIBE_GITHUB_REPO:-OpenVibers/OpenVibe.Games}"
-WORKFLOW="${OPENVIBE_WINDOWS_WORKFLOW:-windows-source-sdk-dlls.yml}"
-RUN_ID="${1:-}"
-
-if ! command -v gh >/dev/null 2>&1; then
- echo "[openvibe] missing gh. Install with: sudo apt install gh" >&2
- exit 1
-fi
-
-if [[ -z "$RUN_ID" ]]; then
- RUN_ID="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$(git rev-parse --abbrev-ref HEAD)" --limit 1 --json databaseId --jq '.[0].databaseId')"
-fi
-
-OUT="$ROOT/artifacts/windows-workflow-debug/run-$RUN_ID-artifacts"
-rm -rf "$OUT"
-mkdir -p "$OUT"
-
-echo "[openvibe] run=$RUN_ID"
-echo "[openvibe] downloading logs/artifacts into $OUT"
-
-gh run view "$RUN_ID" --repo "$REPO" --log > "$OUT/full-run.log" || true
-gh run download "$RUN_ID" --repo "$REPO" --dir "$OUT" || true
-
-find "$OUT" -maxdepth 3 -type f | sort > "$OUT/file-list.txt"
-
-echo
-echo "[openvibe] likely errors:"
-grep -RniE "error MSB|fatal error|undefined reference|unresolved external|LNK[0-9]+|C[0-9]{4}:|throw |Missing:|not found|could not|failed|No Visual Studio solution|No patched" "$OUT" | tail -n 120 || true
-
-echo
-echo "[openvibe] artifact files:"
-sed -n '1,160p' "$OUT/file-list.txt"
-
-DLLDIR="$OUT/openvibe-windows-dlls"
-if [[ -f "$DLLDIR/client.dll" && -f "$DLLDIR/server.dll" ]]; then
- if strings "$DLLDIR/client.dll" | grep -Eq 'ov_join|ov_menu|OpenVibe'; then
- mkdir -p game/openvibe.games/bin
- cp -f "$DLLDIR/client.dll" game/openvibe.games/bin/client.dll
- cp -f "$DLLDIR/server.dll" game/openvibe.games/bin/server.dll
- echo "[openvibe] installed patched Windows DLLs"
- tools/verify-openvibe-dll-content.sh || true
- else
- echo "[openvibe] refused to install DLL artifact because client.dll lacks OpenVibe strings" >&2
- exit 2
- fi
-else
- echo "[openvibe] no DLL artifact available yet" >&2
- exit 3
-fi
-BASH2
-chmod +x tools/windows-workflow-debug-and-install.sh
-
-cat > docs/WINDOWS_WORKFLOW_SDK_BOOTSTRAP.md <<'MD'
-# Windows DLL workflow bootstrap
-
-The Windows GitHub Actions runner does not have the local `engine/source-sdk-2013` checkout that exists on the Linux workstation.
-
-The workflow now bootstraps Valve's Source SDK 2013 multiplayer tree into:
-
-```text
-engine/source-sdk-2013
-```
-
-Then it applies the OpenVibe SDK patch, skips the Linux QuickJS static-library build, builds QuickJS with MSVC, generates Visual Studio projects, builds `client.dll/server.dll`, and only uploads DLLs when `client.dll` contains OpenVibe command strings such as `ov_join`, `ov_menu`, or `OpenVibe`.
-
-Use:
-
-```bash
-tools/gh-windows-build-and-install.sh
-```
-
-or debug the latest run with:
-
-```bash
-tools/windows-workflow-debug-and-install.sh
-```
-MD
-
-# Keep gh helper stricter if present: do not install stale DLLs.
-if [[ -f tools/gh-windows-build-and-install.sh ]]; then
-python3 - <<'PY'
-from pathlib import Path
-p = Path('tools/gh-windows-build-and-install.sh')
-s = p.read_text()
-if 'strings "$ARTDIR/openvibe-windows-dlls/client.dll"' not in s and 'refused to install DLL artifact' not in s:
- marker = 'if [[ -f "$ARTDIR/openvibe-windows-dlls/client.dll" && -f "$ARTDIR/openvibe-windows-dlls/server.dll" ]]; then'
- if marker in s:
- repl = marker + '''
- if ! strings "$ARTDIR/openvibe-windows-dlls/client.dll" | grep -Eq 'ov_join|ov_menu|OpenVibe'; then
- echo "[openvibe] refused to install DLL artifact because client.dll lacks OpenVibe strings" >&2
- tools/windows-workflow-debug-and-install.sh "$RUN_ID" || true
- exit 2
- fi'''
- s = s.replace(marker, repl)
- p.write_text(s)
-PY
-fi
-
-# Commit/push and run workflow if gh is available.
-echo
-echo "[openvibe] git diff summary"
-git diff --stat
-
-git add \
- .github/workflows/windows-source-sdk-dlls.yml \
- tools/bootstrap-source-sdk-2013-windows.ps1 \
- tools/build-sdk-windows.ps1 \
- tools/apply-openvibe-sdk.sh \
- tools/windows-workflow-debug-and-install.sh \
- tools/gh-windows-build-and-install.sh \
- docs/WINDOWS_WORKFLOW_SDK_BOOTSTRAP.md 2>/dev/null || true
-
-if ! git diff --cached --quiet; then
- git commit -m "Bootstrap Source SDK for Windows DLL workflow"
- git push origin "$BRANCH"
-else
- echo "[openvibe] no changes to commit"
-fi
-
-if command -v gh >/dev/null 2>&1; then
- echo "[openvibe] triggering workflow"
- gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$BRANCH" -f diagnostics=1
- sleep 6
- 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" || true
- tools/windows-workflow-debug-and-install.sh "$RUN_ID" || true
-else
- echo "[openvibe] gh missing; run tools/gh-windows-build-and-install.sh after installing/authenticating gh"
-fi
-
-echo
-echo "[openvibe] next if DLLs installed:"
-echo " OPENVIBE_PROTON_LOG=1 OPENVIBE_CLIENT_MODE=proton tools/run-client-auto.sh 127.0.0.1 27015"
-echo " # in game console: ov_join hub ; ov_menu ; ov_menu_servers"
deleted file mode 100755
deleted file mode 100755
@@ -1,146 +0,0 @@
@@ -1,146 +0,0 @@
-#!/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="$(git remote get-url origin | sed -E 's#^[email protected]:##; s#^https://github.com/##; s#\.git$##')"
-
-echo "[openvibe] fix Windows server vcxproj python custom build commands + rerun"
-echo "[openvibe] root=$ROOT"
-echo "[openvibe] branch=$BRANCH"
-echo "[openvibe] repo=$REPO"
-
-python3 - <<'PY'
-from pathlib import Path
-p = Path('tools/build-sdk-windows.ps1')
-s = p.read_text()
-orig = s
-
-# 1) Ensure the robust MSBuild python shim is actually called. The previous patch
-# defined Ensure-PythonCommandForMSBuild but only called Ensure-PythonOnPath.
-needle = 'Ensure-PythonOnPath\n'
-if needle in s and 'Ensure-PythonCommandForMSBuild\n' not in s.split(needle,1)[1][:120]:
- s = s.replace(needle, 'Ensure-PythonOnPath\nEnsure-PythonCommandForMSBuild\n', 1)
-
-# 2) Make Ensure-PythonCommandForMSBuild publish the selected shim path for later project-file patching.
-needle2 = '$env:PATH = "$shimDir;$pyDir;$env:PATH"\n'
-if needle2 in s and '$script:OpenVibePythonCommand' not in s[s.find(needle2):s.find(needle2)+260]:
- s = s.replace(needle2, needle2 + ' $script:OpenVibePythonCommand = $bat\n', 1)
-
-# 3) Add a generated vcxproj patcher. The Source SDK VPC emits custom build commands
-# that literally start with `python ...`. On hosted Windows, MSBuild sometimes does
-# not resolve batch shims through PATH for these custom steps. Rewriting generated
-# project files to call the absolute shim is deterministic.
-marker = 'function Patch-GeneratedPythonCustomBuildCommands'
-func = r'''
-function Patch-GeneratedPythonCustomBuildCommands {
- if (-not $script:OpenVibePythonCommand -or !(Test-Path $script:OpenVibePythonCommand)) {
- Ensure-PythonCommandForMSBuild
- }
-
- $py = $script:OpenVibePythonCommand
- Say "patching generated vcxproj python custom build commands to $py"
- $patchLog = Join-Path $LogDir "python-vcxproj-patches.txt"
- "python command=$py" | Out-File $patchLog
-
- $projects = @(Get-ChildItem -Path $Src -Recurse -File -Filter "*.vcxproj" -ErrorAction SilentlyContinue)
- foreach ($proj in $projects) {
- $text = Get-Content -Raw $proj.FullName
- $before = $text
-
- # XML command bodies can contain python at line start, after XML tags, or after &&.
- # Replace only executable tokens, preserving surrounding whitespace/tags.
- $text = [regex]::Replace($text, '(?im)(^|[>`r`n])([ `t]*)python(\.exe)?([ `t]+)', {
- param($m)
- return $m.Groups[1].Value + $m.Groups[2].Value + '"' + $py + '"' + $m.Groups[4].Value
- })
- $text = [regex]::Replace($text, '(?i)(&&[ `t]*)python(\.exe)?([ `t]+)', {
- param($m)
- return $m.Groups[1].Value + '"' + $py + '"' + $m.Groups[3].Value
- })
- $text = [regex]::Replace($text, '(?i)(&&[ `t]*)python(\.exe)?([ `t]+)', {
- param($m)
- return $m.Groups[1].Value + '"' + $py + '"' + $m.Groups[3].Value
- })
-
- if ($text -ne $before) {
- Set-Content -Encoding utf8 -Path $proj.FullName -Value $text
- "patched $($proj.FullName)" | Out-File $patchLog -Append
- }
- }
-}
-'''
-if marker not in s:
- insert_after = 'function Invoke-MSBuildProject([System.IO.FileInfo]$proj, [string]$label) {'
- idx = s.find(insert_after)
- if idx == -1:
- raise SystemExit('Could not find Invoke-MSBuildProject insertion point')
- s = s[:idx] + func + '\n\n' + s[idx:]
-
-# 4) Call patcher after project generation/project discovery but before building dependencies/client/server.
-call = 'Patch-GeneratedPythonCustomBuildCommands\n\nBuild-SourceSdkDependencyProjects'
-if call not in s:
- s = s.replace('Build-SourceSdkDependencyProjects\n\n$beforeBuild = Get-Date', call + '\n\n$beforeBuild = Get-Date', 1)
-
-if s == orig:
- print('[openvibe] build-sdk-windows.ps1 already had python project patch')
-else:
- p.write_text(s)
- print('[openvibe] patched tools/build-sdk-windows.ps1')
-PY
-
-cat > docs/WINDOWS_SERVER_PYTHON_CUSTOM_BUILD.md <<'EOF_DOC'
-# Windows server.vcxproj Python custom build fix
-
-Valve Source SDK's generated server project uses custom build commands that run
-`python` while converting `.nut` script resources into generated headers. On
-GitHub-hosted Windows runners, `actions/setup-python` may succeed while the VPC
-emitted MSBuild custom step still cannot resolve the literal `python` command.
-
-The Windows build script now:
-
-1. creates a deterministic `python.bat`/`python.cmd` shim;
-2. prepends it to PATH;
-3. rewrites generated `.vcxproj` custom build command bodies so `python ...`
- uses the absolute shim path.
-
-This keeps the generated Source SDK tree disposable while making CI builds
-reliable.
-EOF_DOC
-
-echo "[openvibe] git diff summary"
-git diff --stat
-
-git add tools/build-sdk-windows.ps1 docs/WINDOWS_SERVER_PYTHON_CUSTOM_BUILD.md
-if git diff --cached --quiet; then
- echo "[openvibe] no changes to commit"
-else
- git commit -m "Patch Windows server Python custom build commands"
-fi
-
-echo "[openvibe] pushing $BRANCH"
-git push origin "$BRANCH"
-
-if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then
- echo "[openvibe] triggering clean Windows DLL build"
- tools/trigger-windows-dll-build-clean.sh || true
-else
- echo "[openvibe] triggering workflow via gh"
- gh workflow run windows-source-sdk-dlls.yml --repo "$REPO" --ref "$BRANCH"
- sleep 5
- RUN_ID="$(gh run list --repo "$REPO" --workflow windows-source-sdk-dlls.yml --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"
- echo "[openvibe] watching run $RUN_ID"
- gh run watch "$RUN_ID" --repo "$REPO" --exit-status || true
-fi
-
-if [[ -x tools/windows-workflow-debug-and-install.sh ]]; then
- echo "[openvibe] downloading diagnostics/artifacts"
- tools/windows-workflow-debug-and-install.sh || true
-fi
-
-if [[ -x tools/verify-openvibe-dll-content.sh ]]; then
- tools/verify-openvibe-dll-content.sh || true
-fi
-
-echo "[openvibe] done"
deleted file mode 100755
deleted file mode 100755
@@ -1,188 +0,0 @@
@@ -1,188 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-log(){ printf '[openvibe] %s\n' "$*"; }
-warn(){ printf '[openvibe warn] %s\n' "$*" >&2; }
-
-ROOT="${OPENVIBE_ROOT:-$(pwd)}"
-cd "$ROOT"
-BRANCH="$(git branch --show-current)"
-REPO="$(git config --get remote.origin.url | sed -E 's#^[email protected]:##; s#^https://github.com/##; s#\.git$##')"
-: "${REPO:=OpenVibers/OpenVibe.Games}"
-
-log "fix Windows server build: QuickJS C++ header + vscript nut headers + real msbuild failures"
-log "root=$ROOT"
-log "branch=$BRANCH"
-log "repo=$REPO"
-
-python3 - <<'PY'
-from pathlib import Path
-p = Path('tools/build-sdk-windows.ps1')
-text = p.read_text()
-
-# 1) Make Invoke-MSBuildProject stop leaking Tee-Object output into the function return value.
-old = '& msbuild $proj.FullName /m /p:Configuration="$($p.Configuration)" /p:Platform="$($p.Platform)" /t:Build /v:minimal /nologo 2>&1 | Tee-Object -FilePath $log'
-new = '& msbuild $proj.FullName /m /p:Configuration="$($p.Configuration)" /p:Platform="$($p.Platform)" /t:Build /v:minimal /nologo 2>&1 | Tee-Object -FilePath $log | Out-Host'
-if old in text:
- text = text.replace(old, new)
-
-# 2) Ensure the stronger python shim is actually called and exposes paths for other helpers.
-if 'Ensure-PythonOnPath\nEnsure-PythonCommandForMSBuild' not in text and 'Ensure-PythonOnPath\r\nEnsure-PythonCommandForMSBuild' not in text:
- text = text.replace('Ensure-PythonOnPath\n', 'Ensure-PythonOnPath\nEnsure-PythonCommandForMSBuild\n')
-
-# Add script-scoped python vars inside Ensure-PythonCommandForMSBuild if not present.
-if '$script:OpenVibePythonExe = $py' not in text:
- text = text.replace(' $pyDir = Split-Path -Parent $py\n', ' $pyDir = Split-Path -Parent $py\n $script:OpenVibePythonExe = $py\n')
-if '$script:OpenVibePythonShim = $bat' not in text:
- text = text.replace(' Set-Content -Encoding ascii -Path $cmdFile -Value $batContent\n', ' Set-Content -Encoding ascii -Path $cmdFile -Value $batContent\n $script:OpenVibePythonShim = $bat\n')
-
-helper = r'''
-
-# OPENVIBE_FIX_QJS_CPP_AND_NUT_HEADERS
-function Patch-QuickJsHeaderForMsvcCpp {
- $header = Join-Path $Sdk "src/game/shared/openvibe/third_party/quickjs/quickjs.h"
- if (!(Test-Path $header)) {
- Say "QuickJS header not present yet, skipping C++ compatibility patch: $header"
- return
- }
-
- $log = Join-Path $LogDir "quickjs-cpp-header-patch.txt"
- "header=$header" | Out-File $log
- $q = Get-Content -Raw $header
- $orig = $q
-
- $mkvalOld = '#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .uint64 = (uint32_t)(val) }, tag }'
- $mkvalNew = @'
-#if defined(__cplusplus)
-static inline JSValue JS_MKVAL_CPP(int tag, uint32_t val) { JSValue v; v.u.uint64 = (uint32_t)val; v.tag = tag; return v; }
-#define JS_MKVAL(tag, val) JS_MKVAL_CPP((tag), (uint32_t)(val))
-#else
-#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .uint64 = (uint32_t)(val) }, tag }
-#endif
-'@
- $q = $q.Replace($mkvalOld, $mkvalNew)
-
- $mkptrOld = '#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag }'
- $mkptrNew = @'
-#if defined(__cplusplus)
-static inline JSValue JS_MKPTR_CPP(int tag, void *p) { JSValue v; v.u.ptr = p; v.tag = tag; return v; }
-#define JS_MKPTR(tag, p) JS_MKPTR_CPP((tag), (void *)(p))
-#else
-#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag }
-#endif
-'@
- $q = $q.Replace($mkptrOld, $mkptrNew)
-
- $nanOld = '#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 }'
- $nanNew = @'
-#if defined(__cplusplus)
-static inline JSValue JS_NAN_CPP(void) { JSValue v; v.u.float64 = JS_FLOAT64_NAN; v.tag = JS_TAG_FLOAT64; return v; }
-#define JS_NAN JS_NAN_CPP()
-#else
-#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 }
-#endif
-'@
- $q = $q.Replace($nanOld, $nanNew)
-
- $q = $q.Replace(' JSCFunctionType ft = { .generic_magic = func };', ' JSCFunctionType ft; memset(&ft, 0, sizeof(ft)); ft.generic_magic = func;')
-
- if ($q -ne $orig) {
- Set-Content -Encoding ascii -Path $header -Value $q
- "patched=1" | Out-File $log -Append
- Say "patched QuickJS header for MSVC C++ compound literal/designated initializer compatibility"
- } else {
- "patched=0" | Out-File $log -Append
- Say "QuickJS header C++ patch made no changes"
- }
-}
-
-function Ensure-ServerNutHeaders {
- $serverDir = Join-Path $Src "game/server"
- $textToArray = Join-Path $Src "devtools/bin/texttoarray.py"
- $log = Join-Path $LogDir "server-nut-headers.txt"
- "serverDir=$serverDir" | Out-File $log
- "textToArray=$textToArray" | Out-File $log -Append
- "pythonExe=$script:OpenVibePythonExe" | Out-File $log -Append
- "pythonShim=$script:OpenVibePythonShim" | Out-File $log -Append
-
- if (!(Test-Path $textToArray)) {
- throw "Missing Source SDK texttoarray.py at $textToArray"
- }
- if (-not $script:OpenVibePythonExe -or !(Test-Path $script:OpenVibePythonExe)) {
- throw "Python exe was not captured for nut header generation. Check python-version.txt."
- }
-
- foreach ($name in @("spawn_helper", "vscript_server")) {
- $input = Join-Path $serverDir "$name.nut"
- $out = Join-Path $serverDir "${name}_nut.h"
- if (!(Test-Path $input)) {
- "missing input $input" | Out-File $log -Append
- continue
- }
- Say "generating $out from $input"
- & $script:OpenVibePythonExe $textToArray $input "g_Script_$name" | Set-Content -Encoding ascii -Path $out
- if ($LASTEXITCODE -ne 0) { throw "texttoarray.py failed for $input" }
- if (!(Test-Path $out)) { throw "Expected generated nut header was not created: $out" }
- "generated $out" | Out-File $log -Append
- }
-}
-'''
-
-if 'function Patch-QuickJsHeaderForMsvcCpp' not in text:
- marker = 'function Find-VcVars64 {'
- text = text.replace(marker, helper + '\n' + marker)
-
-# Call QuickJS C++ header patch right after QuickJS lib build and before Set-Location.
-if 'Patch-QuickJsHeaderForMsvcCpp\n\nSet-Location $Src' not in text:
- text = text.replace('\nSet-Location $Src\n', '\nPatch-QuickJsHeaderForMsvcCpp\n\nSet-Location $Src\n')
-
-# Generate vscript headers after project generation/patches and before dependency/client/server builds.
-if 'Ensure-ServerNutHeaders\nBuild-SourceSdkDependencyProjects' not in text:
- text = text.replace('Build-SourceSdkDependencyProjects\n', 'Ensure-ServerNutHeaders\nBuild-SourceSdkDependencyProjects\n')
-
-p.write_text(text)
-PY
-
-cat > docs/WINDOWS_SERVER_QJS_CPP_NUT_HEADERS.md <<'EOF'
-# Windows server build: QuickJS C++ and vscript nut headers
-
-The Windows HL2MP server build needs two extra fixes on hosted Actions runners:
-
-- The Source SDK custom build step for `vscript_server.nut` can report success without producing `vscript_server_nut.h`, so the build script pre-generates `spawn_helper_nut.h` and `vscript_server_nut.h` with `devtools/bin/texttoarray.py`.
-- QuickJS is compiled as C with `clang-cl`, but its header is also included from C++ server files. The script patches the SDK copy of `quickjs.h` so MSVC C++ can compile QuickJS value macros without C compound literals/designated initializers.
-
-The patch is applied only to the generated SDK copy under `engine/source-sdk-2013`, not to the vendored source files.
-EOF
-
-log "git diff summary"
-git diff --stat
-
-git add tools/build-sdk-windows.ps1 docs/WINDOWS_SERVER_QJS_CPP_NUT_HEADERS.md
-if git diff --cached --quiet; then
- log "nothing new to commit"
-else
- git commit -m "Fix Windows server QuickJS C++ and nut headers"
-fi
-
-log "pushing $BRANCH"
-git push origin "$BRANCH"
-
-if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then
- log "triggering clean Windows DLL build"
- tools/trigger-windows-dll-build-clean.sh || true
-elif [[ -x tools/gh-windows-build-and-install.sh ]]; then
- log "triggering Windows DLL build/install helper"
- tools/gh-windows-build-and-install.sh || true
-else
- log "triggering workflow directly"
- gh workflow run windows-source-sdk-dlls.yml --repo "$REPO" --ref "$BRANCH"
- sleep 5
- run_id="$(gh run list --repo "$REPO" --workflow windows-source-sdk-dlls.yml --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"
- log "watching run $run_id"
- gh run watch "$run_id" --repo "$REPO" --exit-status || true
- if [[ -x tools/windows-workflow-debug-and-install.sh ]]; then
- tools/windows-workflow-debug-and-install.sh "$run_id" || true
- fi
-fi
-
-log "done"
deleted file mode 100755
deleted file mode 100755
@@ -1,160 +0,0 @@
@@ -1,160 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-log(){ printf '[openvibe] %s\n' "$*"; }
-warn(){ printf '[openvibe warn] %s\n' "$*" >&2; }
-
-ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
-cd "$ROOT"
-BRANCH="$(git rev-parse --abbrev-ref HEAD)"
-if [[ -x tools/openvibe-gh-repo.sh ]]; then
- REPO="$(tools/openvibe-gh-repo.sh)"
-else
- remote="$(git config --get remote.origin.url || true)"
- REPO="${remote#[email protected]:}"; REPO="${REPO#ssh://[email protected]/}"; REPO="${REPO#https://github.com/}"; REPO="${REPO%.git}"; REPO="${REPO%/}"
-fi
-WORKFLOW="windows-source-sdk-dlls.yml"
-
-log "fix Windows server stub PCH order + rerun"
-log "root=$ROOT"
-log "branch=$BRANCH"
-log "repo=$REPO"
-
-PY="$(command -v python3 || command -v python || true)"
-if [[ -z "$PY" ]]; then
- echo "python3/python required for this patch script" >&2
- exit 1
-fi
-
-"$PY" - <<'PY'
-from pathlib import Path
-import re
-
-root = Path.cwd()
-files = [
- Path("sdk/openvibe/server/hl2mp/openvibe_js_server.cpp"),
- Path("sdk/openvibe/shared/ov_js_runtime.cpp"),
- Path("sdk/openvibe/shared/ov_js_bindings.cpp"),
- Path("sdk/openvibe/shared/ov_js_player.cpp"),
-]
-marker = "// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB"
-cond = "#if defined(_WIN32) && defined(GAME_DLL) && !defined(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS)"
-
-for rel in files:
- path = root / rel
- if not path.exists():
- raise SystemExit(f"missing expected file: {rel}")
- text = path.read_text(encoding="utf-8")
- original = text
- if marker not in text:
- print(f"[skip] no stub marker: {rel}")
- continue
-
- # MSVC /Yu cbase.h skips preprocessor/code before the PCH include. The earlier
- # stub patch put #if before #include "cbase.h", so MSVC skipped the #if and then
- # saw an unexpected #else. Make cbase.h the first real line.
- bom = "\ufeff" if text.startswith("\ufeff") else ""
- if bom:
- text = text[1:]
-
- start_pat = re.compile(
- r"\A\s*// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB\s*\r?\n"
- r"\s*#if defined\(_WIN32\) && defined\(GAME_DLL\) && !defined\(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS\)\s*\r?\n"
- r"\s*(?:#include \"cbase\.h\"\s*\r?\n)?",
- re.MULTILINE,
- )
- text2, n = start_pat.subn(f'#include "cbase.h"\n{marker}\n{cond}\n', text, count=1)
- if n == 0:
- # Idempotent path: already starts with cbase.h, but still normalize the next lines.
- text2 = text
- text2 = re.sub(
- r"\A\s*#include \"cbase\.h\"\s*\r?\n\s*// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB\s*\r?\n\s*#if defined\(_WIN32\) && defined\(GAME_DLL\) && !defined\(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS\)\s*\r?\n",
- f'#include "cbase.h"\n{marker}\n{cond}\n',
- text2,
- count=1,
- )
-
- # Remove the duplicate cbase.h include from the real/non-stub side. It is already
- # first in the translation unit and must not be repeated immediately after #else.
- text2 = re.sub(r"(#else\s*\r?\n)\s*#include \"cbase\.h\"\s*\r?\n", r"\1", text2, count=1)
-
- # Ensure there is a newline before #endif at EOF.
- text2 = text2.rstrip() + "\n"
-
- # Cheap sanity checks for the stub wrapper.
- if not text2.startswith('#include "cbase.h"\n'):
- raise SystemExit(f"{rel}: cbase.h is not first after patch")
- if text2.count(cond) != 1:
- raise SystemExit(f"{rel}: expected exactly one stub #if, found {text2.count(cond)}")
- if text2.count("#else") < 1:
- raise SystemExit(f"{rel}: expected at least one #else")
- if text2.count("#endif") < 1:
- raise SystemExit(f"{rel}: expected at least one #endif")
-
- if text2 != original:
- path.write_text(bom + text2, encoding="utf-8", newline="\n")
- print(f"[patched] {rel}")
- else:
- print(f"[ok] already normalized: {rel}")
-PY
-
-cat > docs/WINDOWS_SERVER_STUB_PCH_ORDER.md <<'EOF_DOC'
-# Windows server stub PCH order
-
-Source SDK server projects compile with MSVC precompiled headers using `/Yu cbase.h`.
-MSVC ignores/skips text before the configured PCH include, so the Windows server
-QuickJS stub wrapper cannot put `#if ...` before `#include "cbase.h"`.
-
-The stubbed OpenVibe server `.cpp` files must start with:
-
-```cpp
-#include "cbase.h"
-// OPENVIBE_WINDOWS_SERVER_QUICKJS_STUB
-#if defined(_WIN32) && defined(GAME_DLL) && !defined(OPENVIBE_WINDOWS_SERVER_REAL_QUICKJS)
-```
-
-The non-stub branch must not re-include `cbase.h` immediately after `#else`.
-EOF_DOC
-
-log "git diff summary"
-git diff --stat
-
-if [[ -n "$(git status --porcelain)" ]]; then
- git add \
- sdk/openvibe/server/hl2mp/openvibe_js_server.cpp \
- sdk/openvibe/shared/ov_js_runtime.cpp \
- sdk/openvibe/shared/ov_js_bindings.cpp \
- sdk/openvibe/shared/ov_js_player.cpp \
- docs/WINDOWS_SERVER_STUB_PCH_ORDER.md
- git commit -m "Fix Windows server stub PCH include order"
-else
- log "no source changes to commit"
-fi
-
-log "pushing $BRANCH"
-git push origin "$BRANCH"
-
-if [[ -x tools/trigger-windows-dll-build-clean.sh ]]; then
- log "triggering clean Windows DLL build"
- tools/trigger-windows-dll-build-clean.sh || true
-elif command -v gh >/dev/null 2>&1; then
- log "triggering workflow"
- gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$BRANCH" || true
- sleep 8
- RUN_ID="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId' || true)"
- if [[ -n "${RUN_ID:-}" && "$RUN_ID" != "null" ]]; then
- log "watching run $RUN_ID"
- gh run watch "$RUN_ID" --repo "$REPO" --exit-status || true
- if [[ -x tools/windows-workflow-debug-and-install.sh ]]; then
- tools/windows-workflow-debug-and-install.sh "$RUN_ID" || true
- fi
- fi
-else
- warn "gh not found; pushed patch but did not trigger workflow"
-fi
-
-if [[ -x tools/verify-openvibe-dll-content.sh ]]; then
- tools/verify-openvibe-dll-content.sh || true
-fi
-
-log "done"
deleted file mode 100755
deleted file mode 100755
@@ -1,330 +0,0 @@
@@ -1,330 +0,0 @@
-#!/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"
deleted file mode 100755
deleted file mode 100755
@@ -1,335 +0,0 @@
@@ -1,335 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-cd "$ROOT"
-
-BRANCH="$(git branch --show-current)"
-echo "[openvibe] fix Windows build: VPC generated extensionless solution / direct vcxproj build"
-echo "[openvibe] root=$ROOT"
-echo "[openvibe] branch=$BRANCH"
-
-mkdir -p tools docs
-
-cat > tools/build-sdk-windows.ps1 <<'PS1'
-param(
- [switch]$InDevShell
-)
-
-$ErrorActionPreference = "Stop"
-
-$Root = if ($env:OPENVIBE_ROOT) { (Resolve-Path $env:OPENVIBE_ROOT).Path } else { (Resolve-Path (Join-Path $PSScriptRoot "..")).Path }
-$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root "engine/source-sdk-2013" }
-$Src = Join-Path $Sdk "src"
-$Mod = Join-Path $Root "game/openvibe.games"
-$OutBin = Join-Path $Mod "bin"
-$LogDir = Join-Path $Root "artifacts/windows-build-debug"
-New-Item -ItemType Directory -Force -Path $LogDir | Out-Null
-
-function Say($m) { Write-Host "[openvibe-win] $m" }
-function Require($p, $msg) {
- if (!(Test-Path $p)) { throw "$msg`nMissing: $p" }
-}
-
-function Find-VcVars64 {
- $candidates = @(
- "${env:ProgramFiles}\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat",
- "${env:ProgramFiles}\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvars64.bat",
- "${env:ProgramFiles}\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat",
- "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat",
- "${env:ProgramFiles}\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
- )
- foreach ($c in $candidates) {
- if ($c -and (Test-Path $c)) { return $c }
- }
- return $null
-}
-
-Say "root=$Root"
-Say "sdk=$Sdk"
-Say "src=$Src"
-
-Require $Src "Source SDK checkout is missing from this runner. Bootstrap must create engine/source-sdk-2013 first."
-
-# VPC is generating *_win64_*.vcxproj on the hosted runner, so use an x64 VS shell.
-# clang-cl/lib/msbuild all remain MSVC ABI-compatible.
-if (-not $InDevShell -or !(Get-Command cl.exe -ErrorAction SilentlyContinue)) {
- $vcvars = Find-VcVars64
- if (-not $vcvars) { throw "Could not find Visual Studio vcvars64.bat" }
- Say "relaunching through MSVC x64 dev shell: $vcvars"
- $self = $PSCommandPath
- $cmd = "`"$vcvars`" && powershell -NoProfile -ExecutionPolicy Bypass -File `"$self`" -InDevShell"
- & cmd.exe /d /s /c $cmd
- exit $LASTEXITCODE
-}
-
-Say "cl.exe already available"
-where.exe cl | Out-File (Join-Path $LogDir "where-cl.txt")
-where.exe msbuild | Out-File (Join-Path $LogDir "where-msbuild.txt")
-where.exe lib | Out-File (Join-Path $LogDir "where-lib.txt")
-
-# Apply OpenVibe source files using Git Bash when available. Skip Linux QuickJS build on Windows.
-$bash = (Get-Command bash -ErrorAction SilentlyContinue)
-if ($bash) {
- Say "applying OpenVibe SDK patch through bash"
- $env:OPENVIBE_ROOT = $Root
- $env:OPENVIBE_SDK = $Sdk
- $env:OPENVIBE_SKIP_QJS_BUILD = "1"
- & bash "$Root/tools/apply-openvibe-sdk.sh" 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "apply-openvibe-sdk.log")
- if ($LASTEXITCODE -ne 0) { throw "apply-openvibe-sdk.sh failed with exit code $LASTEXITCODE" }
-} else {
- throw "Git Bash is required on the Windows runner to apply the OpenVibe SDK patch."
-}
-
-# Build QuickJS as MSVC ABI objects/lib. quickjs itself uses clang-cl because the upstream C is GNU/C99-ish.
-if (Test-Path (Join-Path $Root "tools/build-quickjs-lib-windows.ps1")) {
- Say "building QuickJS Windows static library"
- & powershell -NoProfile -ExecutionPolicy Bypass -File (Join-Path $Root "tools/build-quickjs-lib-windows.ps1") 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "quickjs-windows.log")
- if ($LASTEXITCODE -ne 0) { throw "build-quickjs-lib-windows.ps1 failed with exit code $LASTEXITCODE" }
-} else {
- Say "no build-quickjs-lib-windows.ps1 found; continuing"
-}
-
-Set-Location $Src
-
-function Test-SlnContent([string]$path) {
- if (!(Test-Path $path)) { return $false }
- try {
- $first = Get-Content -Path $path -TotalCount 3 -ErrorAction Stop
- return (($first -join "`n") -match "Microsoft Visual Studio Solution File")
- } catch {
- return $false
- }
-}
-
-function Normalize-Solutions {
- $out = New-Object System.Collections.Generic.List[System.IO.FileInfo]
-
- # Normal .sln files.
- Get-ChildItem -Path $Src -Recurse -File -Filter "*.sln" -ErrorAction SilentlyContinue | ForEach-Object {
- if (Test-SlnContent $_.FullName) { [void]$out.Add($_) }
- }
-
- # VPC sometimes logs "Writing solution file ...\everything." and produces an extensionless-ish file.
- Get-ChildItem -Path $Src -Recurse -File -ErrorAction SilentlyContinue |
- Where-Object { $_.Name -match '^(everything|games|OpenVibe_HL2MP)(\.?)$' } |
- ForEach-Object {
- if (Test-SlnContent $_.FullName) {
- $fixed = Join-Path $_.DirectoryName ($_.BaseName.TrimEnd('.') + ".sln")
- if ($fixed -ne $_.FullName) {
- Copy-Item $_.FullName $fixed -Force
- Say "normalized extensionless VPC solution $($_.FullName) -> $fixed"
- [void]$out.Add((Get-Item $fixed))
- } else {
- [void]$out.Add($_)
- }
- }
- }
-
- return @($out | Sort-Object FullName -Unique)
-}
-
-function Run-ProjectGenerator {
- Say "generating Source SDK Visual Studio projects"
- $generators = @(
- (Join-Path $Src "createallprojects.bat"),
- (Join-Path $Src "creategameprojects.bat"),
- (Join-Path $Src "createprojects.bat")
- )
-
- $ran = $false
- foreach ($gen in $generators) {
- if (Test-Path $gen) {
- Say "running $gen"
- & cmd /c "`"$gen`"" 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "project-generation.log")
- $ran = $true
- break
- }
- }
-
- if (-not $ran) {
- $vpc = Join-Path $Src "devtools/bin/vpc.exe"
- if (Test-Path $vpc) {
- Say "running vpc.exe fallback"
- & $vpc /hl2mp /define:SOURCESDK +game /mksln OpenVibe_HL2MP.sln 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "vpc.log")
- $ran = $true
- }
- }
-
- if (-not $ran) {
- throw "No Source SDK project generator found."
- }
-}
-
-function Get-ProjectConfigs([string]$projPath) {
- $text = Get-Content -Raw $projPath
- $matches = [regex]::Matches($text, '<ProjectConfiguration\s+Include="([^|"]+)\|([^"]+)"')
- $pairs = @()
- foreach ($m in $matches) {
- $pairs += [pscustomobject]@{ Configuration = $m.Groups[1].Value; Platform = $m.Groups[2].Value }
- }
- return @($pairs | Sort-Object Configuration,Platform -Unique)
-}
-
-function Sort-PreferredConfigs($pairs, [string]$projName) {
- $preferred = @()
- foreach ($cfg in @("Release", "Release_HL2MP", "Release HL2MP", "Debug")) {
- foreach ($plat in @("x64", "Win64", "Win32", "x86")) {
- $hit = $pairs | Where-Object { $_.Configuration -eq $cfg -and $_.Platform -eq $plat }
- if ($hit) { $preferred += $hit }
- }
- }
- # Add anything else not covered.
- foreach ($p in $pairs) {
- if (-not ($preferred | Where-Object { $_.Configuration -eq $p.Configuration -and $_.Platform -eq $p.Platform })) {
- $preferred += $p
- }
- }
- return @($preferred)
-}
-
-function Invoke-MSBuildProject([System.IO.FileInfo]$proj, [string]$label) {
- Say "building $label project=$($proj.FullName)"
- $pairs = Get-ProjectConfigs $proj.FullName
- if ($pairs.Count -eq 0) {
- $pairs = @([pscustomobject]@{ Configuration = "Release"; Platform = "x64" })
- }
- $pairs = Sort-PreferredConfigs $pairs $proj.Name
-
- foreach ($p in $pairs) {
- $cfgSafe = ($p.Configuration -replace '[^A-Za-z0-9]+','_')
- $platSafe = ($p.Platform -replace '[^A-Za-z0-9]+','_')
- $log = Join-Path $LogDir "msbuild-$label-$cfgSafe-$platSafe.log"
- Say "msbuild $label cfg=$($p.Configuration) platform=$($p.Platform)"
- & msbuild $proj.FullName /m /p:Configuration="$($p.Configuration)" /p:Platform="$($p.Platform)" /t:Build /v:minimal /nologo 2>&1 | Tee-Object -FilePath $log
- if ($LASTEXITCODE -eq 0) {
- Say "built $label with cfg=$($p.Configuration) platform=$($p.Platform)"
- return $true
- }
- }
-
- return $false
-}
-
-# Generate projects if no relevant vcxproj files exist yet.
-$clientProjects = @(Get-ChildItem -Path $Src -Recurse -File -Filter "*client*hl2mp*.vcxproj" -ErrorAction SilentlyContinue)
-$serverProjects = @(Get-ChildItem -Path $Src -Recurse -File -Filter "*server*hl2mp*.vcxproj" -ErrorAction SilentlyContinue)
-$solutions = Normalize-Solutions
-
-if ($clientProjects.Count -eq 0 -or $serverProjects.Count -eq 0) {
- Run-ProjectGenerator
- $solutions = Normalize-Solutions
- $clientProjects = @(Get-ChildItem -Path $Src -Recurse -File -Filter "*client*hl2mp*.vcxproj" -ErrorAction SilentlyContinue)
- $serverProjects = @(Get-ChildItem -Path $Src -Recurse -File -Filter "*server*hl2mp*.vcxproj" -ErrorAction SilentlyContinue)
-}
-
-"=== normalized solutions ===" | Out-File (Join-Path $LogDir "normalized-solutions.txt")
-$solutions | Select-Object FullName,Length,LastWriteTime | Format-List | Out-File (Join-Path $LogDir "normalized-solutions.txt") -Append
-"=== hl2mp projects ===" | Out-File (Join-Path $LogDir "hl2mp-projects.txt")
-@($clientProjects + $serverProjects) | Select-Object FullName,Length,LastWriteTime | Format-List | Out-File (Join-Path $LogDir "hl2mp-projects.txt") -Append
-
-if ($clientProjects.Count -eq 0) {
- Get-ChildItem -Path $Src -Recurse -File -Include "*.vcxproj","*.sln","everything*" -ErrorAction SilentlyContinue |
- Select-Object FullName,Length,LastWriteTime |
- Format-List | Out-File (Join-Path $LogDir "project-search-after-generator.txt")
- throw "No HL2MP client vcxproj was generated. Check openvibe-windows-build-debug artifact."
-}
-if ($serverProjects.Count -eq 0) {
- Get-ChildItem -Path $Src -Recurse -File -Include "*.vcxproj","*.sln","everything*" -ErrorAction SilentlyContinue |
- Select-Object FullName,Length,LastWriteTime |
- Format-List | Out-File (Join-Path $LogDir "project-search-after-generator.txt")
- throw "No HL2MP server vcxproj was generated. Check openvibe-windows-build-debug artifact."
-}
-
-# Prefer the actual HL2MP game projects and prefer win64 because current Valve VPC emitted *_win64_hl2mp.vcxproj on the runner.
-$clientProject = $clientProjects |
- Sort-Object @{ Expression = { if ($_.Name -match 'win64') { 0 } else { 1 } } },
- @{ Expression = { if ($_.Name -match '^client') { 0 } else { 1 } } },
- FullName |
- Select-Object -First 1
-$serverProject = $serverProjects |
- Sort-Object @{ Expression = { if ($_.Name -match 'win64') { 0 } else { 1 } } },
- @{ Expression = { if ($_.Name -match '^server') { 0 } else { 1 } } },
- FullName |
- Select-Object -First 1
-
-$beforeBuild = Get-Date
-
-if (-not (Invoke-MSBuildProject $clientProject "client")) {
- throw "MSBuild failed all configurations for $($clientProject.FullName). Check msbuild-client-*.log."
-}
-if (-not (Invoke-MSBuildProject $serverProject "server")) {
- throw "MSBuild failed all configurations for $($serverProject.FullName). Check msbuild-server-*.log."
-}
-
-New-Item -ItemType Directory -Force -Path $OutBin | Out-Null
-
-# Prefer recently built DLLs. Then prefer HL2MP paths.
-$allDlls = @(Get-ChildItem -Path $Sdk -Recurse -Filter "*.dll" -ErrorAction SilentlyContinue |
- Where-Object { $_.LastWriteTime -ge $beforeBuild.AddMinutes(-2) } |
- Sort-Object LastWriteTime -Descending)
-
-"=== dlls after build ===" | Out-File (Join-Path $LogDir "dlls-after-build.txt")
-$allDlls | Select-Object FullName,Length,LastWriteTime | Format-List | Out-File (Join-Path $LogDir "dlls-after-build.txt") -Append
-
-$client = $allDlls |
- Where-Object { $_.Name -ieq "client.dll" -and $_.FullName -match "hl2mp|client" } |
- Select-Object -First 1
-$server = $allDlls |
- Where-Object { $_.Name -ieq "server.dll" -and $_.FullName -match "hl2mp|server" } |
- Select-Object -First 1
-
-# Fallback: search all SDK DLLs if timestamps were weird.
-if (-not $client) {
- $client = Get-ChildItem -Path $Sdk -Recurse -Filter client.dll -ErrorAction SilentlyContinue |
- Where-Object { $_.FullName -match "hl2mp|client" } |
- Sort-Object LastWriteTime -Descending |
- Select-Object -First 1
-}
-if (-not $server) {
- $server = Get-ChildItem -Path $Sdk -Recurse -Filter server.dll -ErrorAction SilentlyContinue |
- Where-Object { $_.FullName -match "hl2mp|server" } |
- Sort-Object LastWriteTime -Descending |
- Select-Object -First 1
-}
-
-if (-not $client) { throw "client.dll was not produced" }
-if (-not $server) { throw "server.dll was not produced" }
-
-Say "copy client=$($client.FullName)"
-Say "copy server=$($server.FullName)"
-Copy-Item $client.FullName (Join-Path $OutBin "client.dll") -Force
-Copy-Item $server.FullName (Join-Path $OutBin "server.dll") -Force
-
-Say "done"
-PS1
-
-cat > docs/WINDOWS_VPC_DIRECT_PROJECT_BUILD.md <<'EOF'
-# Windows VPC direct project build
-
-Valve's `createallprojects.bat` runs VPC and is expected to emit `everything.sln`, but on the hosted runner VPC logged an extensionless `everything` solution while still generating the actual HL2MP `.vcxproj` files.
-
-The Windows build now treats the solution as optional and builds the generated HL2MP client/server projects directly:
-
-- `client*_hl2mp*.vcxproj`
-- `server*_hl2mp*.vcxproj`
-
-It also records generated projects and DLL locations in `artifacts/windows-build-debug`.
-EOF
-
-echo "[openvibe] git diff summary"
-git diff --stat -- tools/build-sdk-windows.ps1 docs/WINDOWS_VPC_DIRECT_PROJECT_BUILD.md || true
-
-git add tools/build-sdk-windows.ps1 docs/WINDOWS_VPC_DIRECT_PROJECT_BUILD.md "$0"
-if ! git diff --cached --quiet; then
- git commit -m "Build Windows Source SDK HL2MP projects directly"
-fi
-
-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
- tools/gh-windows-build-and-install.sh
-fi
deleted file mode 100755
deleted file mode 100755
@@ -1,392 +0,0 @@
@@ -1,392 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-REPO="${OPENVIBE_REPO:-OpenVibers/OpenVibe.Games}"
-REPO="${REPO%.git}"
-WORKFLOW="${OPENVIBE_WINDOWS_WORKFLOW:-windows-source-sdk-dlls.yml}"
-BRANCH="$(git -C "$ROOT" rev-parse --abbrev-ref HEAD)"
-LOG_DIR="$ROOT/artifacts/windows-workflow-debug"
-
-cd "$ROOT"
-
-echo "[openvibe] fix Windows workflow diagnostics + rerun"
-echo "[openvibe] root=$ROOT"
-echo "[openvibe] repo=$REPO"
-echo "[openvibe] workflow=$WORKFLOW"
-echo "[openvibe] branch=$BRANCH"
-
-if ! command -v gh >/dev/null 2>&1; then
- echo "[openvibe error] gh is required. Install/auth first:" >&2
- echo " sudo apt update && sudo apt install gh" >&2
- echo " gh auth login" >&2
- exit 1
-fi
-
-if ! gh auth status >/dev/null 2>&1; then
- echo "[openvibe error] gh is installed but not authenticated. Run:" >&2
- echo " gh auth login" >&2
- exit 1
-fi
-
-mkdir -p "$LOG_DIR"
-
-latest_run="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId // empty')"
-if [[ -n "$latest_run" ]]; then
- echo "[openvibe] latest workflow run=$latest_run"
- echo "[openvibe] saving failed/full logs into $LOG_DIR"
- gh run view "$latest_run" --repo "$REPO" --log-failed > "$LOG_DIR/run-${latest_run}-failed.log" 2>&1 || true
- gh run view "$latest_run" --repo "$REPO" --log > "$LOG_DIR/run-${latest_run}-full.log" 2>&1 || true
-
- echo "[openvibe] likely error snippets:"
- grep -Eina "error|fatal|exception|not found|cannot find|MSB[0-9]+|LNK[0-9]+|C[0-9]{4}|No such|missing|failed" \
- "$LOG_DIR/run-${latest_run}-failed.log" "$LOG_DIR/run-${latest_run}-full.log" \
- | tail -n 80 || true
-else
- echo "[openvibe warn] no existing workflow run found yet"
-fi
-
-echo
-
-echo "[openvibe] rewriting Windows workflow to always upload logs/artifacts"
-mkdir -p .github/workflows
-cat > .github/workflows/windows-source-sdk-dlls.yml <<'YAML'
-name: Build Windows Source DLLs
-
-on:
- workflow_dispatch:
- inputs:
- diagnostics:
- description: "Upload verbose build diagnostics"
- required: false
- default: "1"
- push:
- branches:
- - codex/openvibe-next-steps
- paths:
- - ".github/workflows/windows-source-sdk-dlls.yml"
- - "tools/build-sdk-windows.ps1"
- - "tools/build-quickjs-lib-windows.ps1"
- - "tools/apply-openvibe-sdk.sh"
- - "sdk/openvibe/**"
- - "game/openvibe.games/js/**"
-
-permissions:
- contents: read
- actions: read
-
-jobs:
- windows-dlls:
- name: Windows client.dll/server.dll
- runs-on: windows-2022
- defaults:
- run:
- shell: pwsh
-
- steps:
- - name: Checkout OpenVibe
- uses: actions/checkout@v4
- with:
- fetch-depth: 1
-
- - name: Configure git line endings
- run: |
- git config --global core.autocrlf false
- git config --global core.eol lf
- git status --short
-
- - name: Setup MSBuild
- uses: microsoft/setup-msbuild@v2
-
- - name: Show toolchain and tree
- run: |
- $ErrorActionPreference = 'Continue'
- Write-Host "pwd=$(Get-Location)"
- git rev-parse --short HEAD
- where.exe msbuild
- where.exe cl
- where.exe link
- where.exe bash
- Get-ChildItem -Force | Select-Object Mode,Length,Name
- if (Test-Path engine) { Get-ChildItem -Force engine | Select-Object Mode,Length,Name } else { Write-Host "[miss] engine directory" }
- if (Test-Path engine/source-sdk-2013/src) { Get-ChildItem -Force engine/source-sdk-2013/src | Select-Object Mode,Length,Name } else { Write-Host "[miss] engine/source-sdk-2013/src" }
-
- - name: Build Windows Source SDK DLLs
- id: build
- run: |
- $ErrorActionPreference = 'Stop'
- New-Item -ItemType Directory -Force -Path artifacts/windows-build-debug | Out-Null
- powershell -NoProfile -ExecutionPolicy Bypass -File tools/build-sdk-windows.ps1 *>&1 | Tee-Object -FilePath artifacts/windows-build-debug/build-sdk-windows.log
-
- - name: Stage build outputs and diagnostics
- if: always()
- run: |
- $ErrorActionPreference = 'Continue'
- New-Item -ItemType Directory -Force -Path artifacts/windows-dlls | Out-Null
- New-Item -ItemType Directory -Force -Path artifacts/windows-build-debug | Out-Null
-
- "=== repo ===" | Out-File artifacts/windows-build-debug/tree.txt
- Get-ChildItem -Force | Format-Table -AutoSize | Out-File artifacts/windows-build-debug/tree.txt -Append
- "=== dll search ===" | Out-File artifacts/windows-build-debug/dll-search.txt
- Get-ChildItem -Recurse -Force -Include client.dll,server.dll -ErrorAction SilentlyContinue |
- Select-Object FullName,Length,LastWriteTime |
- Format-List | Out-File artifacts/windows-build-debug/dll-search.txt -Append
- "=== vc/sln search ===" | Out-File artifacts/windows-build-debug/project-search.txt
- Get-ChildItem -Recurse -Force -Include *.sln,*.vcxproj,*.vcproj,*.log -ErrorAction SilentlyContinue |
- Select-Object FullName,Length,LastWriteTime |
- Format-List | Out-File artifacts/windows-build-debug/project-search.txt -Append
-
- $clientCandidates = @(
- "game/openvibe.games/bin/client.dll",
- "engine/source-sdk-2013/game/mod_hl2mp/bin/client.dll",
- "engine/source-sdk-2013/game/mod_hl2mp/bin/win64/client.dll",
- "engine/source-sdk-2013/game/mod_hl2mp/bin/Release/client.dll",
- "engine/source-sdk-2013/src/game/client/Release_hl2mp/client.dll",
- "engine/source-sdk-2013/src/game/client/Release/client.dll"
- )
- $serverCandidates = @(
- "game/openvibe.games/bin/server.dll",
- "engine/source-sdk-2013/game/mod_hl2mp/bin/server.dll",
- "engine/source-sdk-2013/game/mod_hl2mp/bin/win64/server.dll",
- "engine/source-sdk-2013/game/mod_hl2mp/bin/Release/server.dll",
- "engine/source-sdk-2013/src/game/server/Release_hl2mp/server.dll",
- "engine/source-sdk-2013/src/game/server/Release/server.dll"
- )
-
- $client = $clientCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
- $server = $serverCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
- if ($client) { Copy-Item $client artifacts/windows-dlls/client.dll -Force }
- if ($server) { Copy-Item $server artifacts/windows-dlls/server.dll -Force }
-
- if (Test-Path artifacts/windows-dlls/client.dll) {
- $clientText = (Select-String -Path artifacts/windows-dlls/client.dll -Pattern "ov_join|ov_menu|OpenVibe" -Encoding Byte -SimpleMatch -Quiet)
- Write-Host "client.dll staged"
- } else {
- Write-Host "[miss] no client.dll staged"
- }
- if (Test-Path artifacts/windows-dlls/server.dll) { Write-Host "server.dll staged" } else { Write-Host "[miss] no server.dll staged" }
-
- - name: Upload Windows DLL artifact
- if: always()
- uses: actions/upload-artifact@v4
- with:
- name: openvibe-windows-dlls
- path: artifacts/windows-dlls/**
- if-no-files-found: warn
-
- - name: Upload Windows build diagnostics
- if: always()
- uses: actions/upload-artifact@v4
- with:
- name: openvibe-windows-build-debug
- path: artifacts/windows-build-debug/**
- if-no-files-found: warn
-YAML
-
-cat > tools/build-sdk-windows.ps1 <<'PS1'
-$ErrorActionPreference = "Stop"
-
-$Root = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
-$Sdk = Join-Path $Root "engine/source-sdk-2013"
-$Src = Join-Path $Sdk "src"
-$Mod = Join-Path $Root "game/openvibe.games"
-$OutBin = Join-Path $Mod "bin"
-$Qjs = Join-Path $Src "game/shared/openvibe/third_party/quickjs"
-$LogDir = Join-Path $Root "artifacts/windows-build-debug"
-New-Item -ItemType Directory -Force -Path $LogDir | Out-Null
-
-function Say($m) { Write-Host "[openvibe-win] $m" }
-function Require($p, $msg) {
- if (!(Test-Path $p)) { throw "$msg`nMissing: $p" }
-}
-
-Say "root=$Root"
-Say "sdk=$Sdk"
-Say "src=$Src"
-Require $Src "Source SDK checkout is missing from this runner. The workflow must have engine/source-sdk-2013 available in the repository/submodule before Windows DLLs can build."
-
-# Apply OpenVibe source files using Git Bash when available. This reuses the Linux patcher on Windows runners.
-$bash = (Get-Command bash -ErrorAction SilentlyContinue)
-if ($bash) {
- Say "applying OpenVibe SDK patch through bash"
- & bash "$Root/tools/apply-openvibe-sdk.sh"
- if ($LASTEXITCODE -ne 0) { throw "apply-openvibe-sdk.sh failed with exit code $LASTEXITCODE" }
-} else {
- throw "Git Bash is required on the Windows runner to apply the OpenVibe SDK patch."
-}
-
-# Build QuickJS as MSVC objects/lib. The Linux .a cannot be linked into Windows DLLs.
-if (Test-Path (Join-Path $Root "tools/build-quickjs-lib-windows.ps1")) {
- Say "building QuickJS Windows static library"
- & powershell -NoProfile -ExecutionPolicy Bypass -File (Join-Path $Root "tools/build-quickjs-lib-windows.ps1")
- if ($LASTEXITCODE -ne 0) { throw "build-quickjs-lib-windows.ps1 failed with exit code $LASTEXITCODE" }
-} else {
- Say "no build-quickjs-lib-windows.ps1 found; continuing"
-}
-
-Set-Location $Src
-
-# Generate Visual Studio project files if needed.
-$solutions = @(Get-ChildItem -Path $Src -Recurse -Filter "*.sln" -ErrorAction SilentlyContinue)
-if ($solutions.Count -eq 0) {
- Say "no .sln files found; trying Source SDK project generators"
- $generators = @(
- (Join-Path $Src "creategameprojects.bat"),
- (Join-Path $Src "createallprojects.bat"),
- (Join-Path $Src "createprojects.bat")
- )
- $ran = $false
- foreach ($gen in $generators) {
- if (Test-Path $gen) {
- Say "running $gen"
- & cmd /c "`"$gen`"" | Tee-Object -FilePath (Join-Path $LogDir "project-generation.log")
- $ran = $true
- break
- }
- }
-
- if (-not $ran) {
- $vpc = Join-Path $Src "devtools/bin/vpc.exe"
- if (Test-Path $vpc) {
- Say "running vpc.exe fallback"
- & $vpc /hl2mp +game /mksln OpenVibe_HL2MP.sln | Tee-Object -FilePath (Join-Path $LogDir "vpc.log")
- $ran = $true
- }
- }
-
- $solutions = @(Get-ChildItem -Path $Src -Recurse -Filter "*.sln" -ErrorAction SilentlyContinue)
- if ($solutions.Count -eq 0) {
- Get-ChildItem -Path $Src -Force | Select-Object Mode,Length,Name | Format-Table -AutoSize | Out-File (Join-Path $LogDir "src-root-after-generator.txt")
- throw "No Visual Studio solution was generated. Check openvibe-windows-build-debug artifact."
- }
-}
-
-Say "solutions found:"
-$solutions | ForEach-Object { Say " $($_.FullName)" }
-
-# Pick the most likely HL2MP/game solution.
-$solution = $solutions |
- Where-Object { $_.Name -match "hl2mp|game|sdk|everything|OpenVibe" } |
- Select-Object -First 1
-if (-not $solution) { $solution = $solutions | Select-Object -First 1 }
-Say "selected solution=$($solution.FullName)"
-
-# Try common project targets/configurations. Source SDK projects vary by branch/version.
-$configs = @("Release", "Release_HL2MP", "Release HL2MP")
-$platforms = @("Win32", "x86")
-$targets = @("client_hl2mp", "server_hl2mp", "client", "server", "Build")
-
-$builtAny = $false
-foreach ($cfg in $configs) {
- foreach ($plat in $platforms) {
- foreach ($target in $targets) {
- Say "msbuild cfg=$cfg platform=$plat target=$target"
- & msbuild $solution.FullName /m /p:Configuration=$cfg /p:Platform=$plat /t:$target /v:minimal /nologo 2>&1 | Tee-Object -FilePath (Join-Path $LogDir "msbuild-$($cfg -replace '[^A-Za-z0-9]','_')-$plat-$target.log")
- if ($LASTEXITCODE -eq 0) {
- $builtAny = $true
- break
- }
- }
- if ($builtAny) { break }
- }
- if ($builtAny) { break }
-}
-
-if (-not $builtAny) {
- throw "MSBuild could not build any known Source SDK HL2MP target. Check uploaded msbuild logs."
-}
-
-New-Item -ItemType Directory -Force -Path $OutBin | Out-Null
-
-$client = Get-ChildItem -Path $Sdk -Recurse -Filter client.dll -ErrorAction SilentlyContinue |
- Where-Object { $_.FullName -match "hl2mp|mod_hl2mp|client" } |
- Sort-Object LastWriteTime -Descending |
- Select-Object -First 1
-$server = Get-ChildItem -Path $Sdk -Recurse -Filter server.dll -ErrorAction SilentlyContinue |
- Where-Object { $_.FullName -match "hl2mp|mod_hl2mp|server" } |
- Sort-Object LastWriteTime -Descending |
- Select-Object -First 1
-
-if (-not $client) { throw "client.dll was not produced" }
-if (-not $server) { throw "server.dll was not produced" }
-
-Say "copy client=$($client.FullName)"
-Say "copy server=$($server.FullName)"
-Copy-Item $client.FullName (Join-Path $OutBin "client.dll") -Force
-Copy-Item $server.FullName (Join-Path $OutBin "server.dll") -Force
-
-Say "done"
-PS1
-
-# Add a local helper that downloads full logs/artifacts from the last run even when failed.
-cat > tools/windows-workflow-debug-and-install.sh <<'SH'
-#!/usr/bin/env bash
-set -euo pipefail
-ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
-REPO="${OPENVIBE_REPO:-OpenVibers/OpenVibe.Games}"
-WORKFLOW="${OPENVIBE_WINDOWS_WORKFLOW:-windows-source-sdk-dlls.yml}"
-BRANCH="$(git -C "$ROOT" rev-parse --abbrev-ref HEAD)"
-OUT="$ROOT/artifacts/windows-workflow-debug"
-cd "$ROOT"
-mkdir -p "$OUT"
-run="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')"
-echo "[openvibe] latest run=$run"
-gh run view "$run" --repo "$REPO" --log > "$OUT/run-${run}.log" 2>&1 || true
-gh run view "$run" --repo "$REPO" --log-failed > "$OUT/run-${run}-failed.log" 2>&1 || true
-gh run download "$run" --repo "$REPO" --dir "$OUT/run-${run}-artifacts" || true
-printf '\n[openvibe] tail of failed log:\n'
-tail -n 160 "$OUT/run-${run}-failed.log" || true
-printf '\n[openvibe] artifacts saved under: %s\n' "$OUT/run-${run}-artifacts"
-if [[ -f "$OUT/run-${run}-artifacts/openvibe-windows-dlls/client.dll" && -f "$OUT/run-${run}-artifacts/openvibe-windows-dlls/server.dll" ]]; then
- mkdir -p game/openvibe.games/bin
- cp -f "$OUT/run-${run}-artifacts/openvibe-windows-dlls/client.dll" game/openvibe.games/bin/client.dll
- cp -f "$OUT/run-${run}-artifacts/openvibe-windows-dlls/server.dll" game/openvibe.games/bin/server.dll
- echo "[openvibe] installed DLL artifacts"
- tools/verify-openvibe-dll-content.sh || true
-else
- echo "[openvibe] no DLL artifact available yet; check debug artifact/logs"
-fi
-SH
-chmod +x tools/windows-workflow-debug-and-install.sh
-
-git add .github/workflows/windows-source-sdk-dlls.yml tools/build-sdk-windows.ps1 tools/windows-workflow-debug-and-install.sh
-
-if ! git diff --cached --quiet; then
- echo "[openvibe] committing workflow/build diagnostic patch"
- git commit -m "Improve Windows DLL workflow diagnostics"
- echo "[openvibe] pushing $BRANCH"
- git push origin "$BRANCH"
-else
- echo "[openvibe] no workflow/build changes to commit"
-fi
-
-echo "[openvibe] triggering updated workflow"
-gh workflow run "$WORKFLOW" --repo "$REPO" --ref "$BRANCH"
-
-echo "[openvibe] waiting for new workflow run to appear"
-sleep 5
-new_run="$(gh run list --repo "$REPO" --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId // empty')"
-if [[ -z "$new_run" ]]; then
- echo "[openvibe error] could not find new workflow run" >&2
- exit 1
-fi
-
-echo "[openvibe] watching run $new_run"
-set +e
-gh run watch "$new_run" --repo "$REPO" --exit-status
-status=$?
-set -e
-
-if [[ $status -eq 0 ]]; then
- echo "[openvibe] workflow passed; downloading artifacts"
-else
- echo "[openvibe warn] workflow still failed; downloading diagnostics anyway"
-fi
-
-tools/windows-workflow-debug-and-install.sh
-
-if [[ $status -ne 0 ]]; then
- echo
- echo "[openvibe] next: paste the tail printed above if you want me to patch the exact compile error."
- exit $status
-fi