openvibers/source.openvibe.games Commits
Finalize multiplatform client runtime support
Changes
nnew file mode 100644@@@ -0,0 +1,57 @@++# OpenVibe: Source multi-platform build matrix++++OpenVibe needs different client/server binaries for different Source runtimes.++++| Runtime | Executable | Module loaded by the engine | Output path |++| --- | --- | --- | --- |++| Linux native client | `hl2_linux` | Linux ELF client module | `game/openvibe.games/bin/linux64/client.so` |++| Linux dedicated server | `srcds_linux` | Linux ELF server module | `game/openvibe.games/bin/linux64/server.so` |++| Windows native client | `hl2.exe` | Windows PE client DLL | `game/openvibe.games/bin/client.dll` |++| Proton client | Windows `hl2.exe` under Proton | Windows PE client DLL | `game/openvibe.games/bin/client.dll` |++| Windows dedicated server | `srcds.exe` | Windows PE server DLL | `game/openvibe.games/bin/server.dll` |++++Important: Proton does **not** load Linux `client.so`. It runs Windows `hl2.exe`, so it needs `client.dll`.++If the in-game console says `Unknown command "ov_join"` or `Unknown command "ov_menu"`, the OpenVibe client module is not loaded for that runtime.++++## Linux build++++```bash++cd ~/src/openvibe-source++tools/build-sdk-linux.sh++tools/setup-openvibe-bin.sh++tools/check-openvibe-platform-binaries.sh++```++++## Windows DLL build++++Run on Windows in a Visual Studio Developer PowerShell with C++ Build Tools installed:++++```powershell++cd C:\path\to\openvibe-source++powershell -ExecutionPolicy Bypass -File tools\build-sdk-windows.ps1++```++++That script builds QuickJS as `libquickjs_openvibe.lib`, runs VPC/MSBuild, then copies DLLs into:++++```text++game/openvibe.games/bin/client.dll++game/openvibe.games/bin/server.dll++```++++Copy those DLLs back to the Linux dev checkout if you build on a separate Windows VM. Proton will then load them.++++## Runtime selection++++```bash++# Prefer native Linux if hl2_linux + client.so exist, otherwise use Proton if client.dll exists.++tools/run-client-auto.sh 127.0.0.1 27015++++# Force native Linux.++OPENVIBE_CLIENT_MODE=linux tools/run-client-auto.sh 127.0.0.1 27015++++# Force Proton; requires client.dll.++OPENVIBE_CLIENT_MODE=proton tools/run-client-auto.sh 127.0.0.1 27015++++# Launch Proton even without client.dll, but in-game client commands/menu will not exist.++OPENVIBE_CLIENT_MODE=proton-fallback tools/run-client-auto.sh 127.0.0.1 27015++```@@@ -247,37 +247,33 @@ async function ensureClientUiServer(root) {// ── Game launch ────────────────────────────────────────────────────────────────async function launchGame(serverIp, serverPort) {const root = path.resolve(__dirname, '..');-- const mod = path.join(root, 'game/openvibe.games');---- // Prefer the Proton-based launcher (Windows hl2.exe via GE-Proton)-- const protonScript = path.join(root, 'tools/run-client-proton.sh');-- const steamRoot = process.env.HOME + '/.steam/steam';-- const hl2Exe = '/mnt/data-f/SteamLibrary/steamapps/common/Source SDK Base 2013 Multiplayer/hl2.exe';---- const hasProton = fs.existsSync(path.join(steamRoot, 'compatibilitytools.d/GE-Proton10-34/proton'));-- const hasHl2 = fs.existsSync(hl2Exe);---- if (!hasProton || !hasHl2) {-- dialog.showErrorBox('Game Not Found',-- !hasHl2-- ? `hl2.exe not found at:\n${hl2Exe}`-- : 'GE-Proton10-34 not found.\nInstall it via ProtonUp-Qt or manually.');-- return false;-- }const uiReady = await ensureClientUiServer(root);if (!uiReady) {dialog.showErrorBox('OpenVibe Menu Server Failed',-- `Could not start http://127.0.0.1:${CLIENT_UI_PORT}/client for the in-game HTML menu.`);++ `Could not start http://127.0.0.1:${CLIENT_UI_PORT}/client for the launcher/in-game HTML menu.`);return false;}-- // Build arguments to pass through the shell scriptconst scriptArgs = serverIp && serverPort ? [serverIp, String(serverPort)] : [];++ const isWin = process.platform === 'win32';++ const launcherScript = isWin++ ? path.join(root, 'tools', 'run-client-windows.ps1')++ : path.join(root, 'tools', 'run-client-auto.sh');++++ if (!fs.existsSync(launcherScript)) {++ dialog.showErrorBox('OpenVibe Launcher Script Missing', `Could not find:\n${launcherScript}`);++ return false;++ }++++ const command = isWin ? 'powershell.exe' : 'bash';++ const args = isWin++ ? ['-ExecutionPolicy', 'Bypass', '-File', launcherScript, ...scriptArgs]++ : [launcherScript, ...scriptArgs];-- console.log('[launcher] spawning via Proton:', protonScript, scriptArgs.join(' '));++ console.log('[launcher] spawning client:', command, args.join(' '));-- gameProcess = spawn('bash', [protonScript, ...scriptArgs], {++ gameProcess = spawn(command, args, {cwd: root,detached: true,env: {@@@ -297,49 +293,17 @@ async function launchGame(serverIp, serverPort) {mainWindow?.focus();});-- emitLaunchPhase(-- 'spawned',-- 'Source has started. OpenVibe will keep this launcher visible until the game window is detected and stable.'-- );mainWindow?.webContents.send('game-started', gameProcess.pid);-- waitForGameWindowReady().then((state) => {-- if (!gameProcess) return;---- if (state.ready) {-- emitLaunchPhase(-- 'ready',-- HIDE_LAUNCHER_ON_GAME_READY-- ? 'Source window looks ready. Focusing game and hiding launcher.'-- : 'Source window looks ready. Launcher will stay open so you are not stuck behind a frozen loading screen.',-- state-- );-- focusGameWindow();---- if (HIDE_LAUNCHER_ON_GAME_READY) {-- if (mainWindow && !mainWindow.isDestroyed()) mainWindow.hide();-- } else {-- if (mainWindow && !mainWindow.isDestroyed()) {-- mainWindow.show();-- mainWindow.focus();-- }-- }-- } else {-- emitLaunchPhase(-- 'slow',-- 'Source is taking longer than expected. The launcher is staying visible; use Focus Game when the window appears.',-- state-- );-- if (mainWindow && !mainWindow.isDestroyed()) {-- mainWindow.show();-- mainWindow.focus();-- }-- }-- });++ // Keep Electron visible by default so users are not dumped behind a frozen Source loading window.++ // Advanced: set OPENVIBE_HIDE_LAUNCHER_ON_GAME_READY=1 to hide after a conservative delay.++ if (process.env.OPENVIBE_HIDE_LAUNCHER_ON_GAME_READY === '1') {++ setTimeout(() => {++ if (gameProcess && mainWindow && !mainWindow.isDestroyed()) mainWindow.hide();++ }, Number(process.env.OPENVIBE_GAME_READY_HIDE_DELAY_MS || 12000));++ }-- // Detach so the game keeps running if Electron is closedgameProcess.unref();--return true;}nnew file mode 100755@@@ -0,0 +1,750 @@++#!/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] adding multi-platform client/server binary support"++echo "[openvibe] root=$ROOT"++echo "[openvibe] sdk=$SDK"++++backup_file() {++ local f="$1"++ [[ -f "$f" ]] && cp "$f" "$f.bak.$STAMP"++}++++mkdir -p tools docs game/openvibe.games/bin game/openvibe.games/bin/linux64++++# -----------------------------------------------------------------------------++# 1) Make apply-openvibe-sdk.sh usable from Windows/Git Bash by allowing the++# QuickJS C build step to be skipped. Windows builds create a .lib via MSVC.++# -----------------------------------------------------------------------------++if [[ -f tools/apply-openvibe-sdk.sh ]]; then++ backup_file tools/apply-openvibe-sdk.sh++ python3 <<'PY'++from pathlib import Path++p = Path('tools/apply-openvibe-sdk.sh')++s = p.read_text()++old = '"$ROOT/tools/build-quickjs-lib.sh"'++new = '''if [[ "${OPENVIBE_SKIP_QJS_BUILD:-0}" != "1" ]]; then++ "$ROOT/tools/build-quickjs-lib.sh"++fi'''++if old in s and 'OPENVIBE_SKIP_QJS_BUILD' not in s:++ s = s.replace(old, new)++p.write_text(s)++PY++fi++++# -----------------------------------------------------------------------------++# 2) Diagnostic: exactly which binaries exist and which runtime can load them.++# -----------------------------------------------------------------------------++cat > tools/check-openvibe-platform-binaries.sh <<'CHECK'++#!/usr/bin/env bash++set -euo pipefail++++ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"++MOD="$ROOT/game/openvibe.games"++BIN="$MOD/bin"++LINUX_BIN="$BIN/linux64"++++find_hl2_linux() {++ local candidates=(++ "${OPENVIBE_HL2_LINUX:-}"++ "$HOME/.steam/steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"++ "$HOME/.local/share/Steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"++ "/mnt/data-f/SteamLibrary/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"++ "/mnt/6tb/ssd_offload/home/$USER/.steam/debian-installation/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"++ )++ for p in "${candidates[@]}"; do++ [[ -n "$p" && -x "$p" ]] && { echo "$p"; return 0; }++ done++ return 1++}++++find_hl2_exe() {++ local candidates=(++ "${OPENVIBE_HL2_EXE:-}"++ "/mnt/data-f/SteamLibrary/steamapps/common/Source SDK Base 2013 Multiplayer/hl2.exe"++ "$HOME/.steam/steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2.exe"++ "$HOME/.local/share/Steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2.exe"++ )++ for p in "${candidates[@]}"; do++ [[ -n "$p" && -f "$p" ]] && { echo "$p"; return 0; }++ done++ return 1++}++++ok=0++warn=0++++echo "[openvibe] platform binary matrix"++echo "mod: $MOD"++echo++++check_file() {++ local label="$1" path="$2"++ if [[ -f "$path" || -L "$path" ]]; then++ echo "[ok] $label: $path"++ else++ echo "[miss] $label: $path"++ warn=1++ fi++}++++check_file "Linux native client module" "$LINUX_BIN/client.so"++check_file "Linux native server module" "$LINUX_BIN/server.so"++check_file "Windows/Proton client module" "$BIN/client.dll"++check_file "Windows/Proton server module" "$BIN/server.dll"++++echo++if hl2_linux="$(find_hl2_linux 2>/dev/null)"; then++ echo "[ok] Native Linux client executable: $hl2_linux"++else++ echo "[miss] Native Linux client executable: set OPENVIBE_HL2_LINUX or install Source SDK Base 2013 MP Linux"++ warn=1++fi++++if hl2_exe="$(find_hl2_exe 2>/dev/null)"; then++ echo "[ok] Windows/Proton client executable: $hl2_exe"++else++ echo "[miss] Windows hl2.exe for Proton/Windows client"++ warn=1++fi++++echo++cat <<TEXT++Runtime rules:++ Linux native client loads: game/openvibe.games/bin/linux64/client.so++ Linux native server loads: game/openvibe.games/bin/linux64/server.so++ Windows native client loads: game/openvibe.games/bin/client.dll++ Proton client loads: game/openvibe.games/bin/client.dll++++If Proton/Windows says unknown command ov_join or ov_menu, client.dll is missing or failed to load.++Linux client.so cannot be loaded by Windows hl2.exe under Proton.++TEXT++++exit 0++CHECK++chmod +x tools/check-openvibe-platform-binaries.sh++++# -----------------------------------------------------------------------------++# 3) Setup script: keep Linux symlinks and also install/carry Windows DLLs when++# they have been produced by a Windows build.++# -----------------------------------------------------------------------------++backup_file tools/setup-openvibe-bin.sh++cat > tools/setup-openvibe-bin.sh <<'SETUP'++#!/usr/bin/env bash++set -euo pipefail++++ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"++TF2_SRCDS="${OPENVIBE_SRCDS:-$HOME/srcds/tf2}"++MOD_BIN="$ROOT/game/openvibe.games/bin"++MOD_LINUX_BIN="$MOD_BIN/linux64"++SDK_HL2MP_LINUX_BIN="$ROOT/engine/source-sdk-2013/game/mod_hl2mp/bin/linux64"++SDK_LIB_BIN="$ROOT/engine/source-sdk-2013/src/lib/public/linux64"++TF2_BIN="$TF2_SRCDS/bin/linux64"++SDK_HL2MP_WIN_BIN="$ROOT/engine/source-sdk-2013/game/mod_hl2mp/bin"++++mkdir -p "$MOD_LINUX_BIN" "$MOD_BIN"++++require_file() {++ if [[ ! -f "$1" ]]; then++ echo "Missing required file: $1" >&2++ exit 1++ fi++}++++link_if_exists() {++ local src="$1" dst="$2"++ if [[ -f "$src" ]]; then++ ln -sfn "$src" "$dst"++ echo "[openvibe] linked ${dst#$ROOT/} -> $src"++ fi++}++++copy_if_exists() {++ local src="$1" dst="$2"++ if [[ -f "$src" ]]; then++ install -D -m 0644 "$src" "$dst"++ echo "[openvibe] copied ${dst#$ROOT/}"++ return 0++ fi++ return 1++}++++# Linux native / Linux dedicated server modules.++require_file "$SDK_HL2MP_LINUX_BIN/client.so"++require_file "$SDK_HL2MP_LINUX_BIN/server.so"++require_file "$SDK_HL2MP_LINUX_BIN/game_shader_generic_example.so"++require_file "$SDK_LIB_BIN/libtier0.so"++require_file "$SDK_LIB_BIN/libvstdlib.so"++require_file "$SDK_LIB_BIN/libsteam_api.so"++++ln -sfn "$SDK_HL2MP_LINUX_BIN/client.so" "$MOD_LINUX_BIN/client.so"++ln -sfn client.so "$MOD_LINUX_BIN/client_srv.so"++ln -sfn "$SDK_HL2MP_LINUX_BIN/server.so" "$MOD_LINUX_BIN/server.so"++ln -sfn server.so "$MOD_LINUX_BIN/server_srv.so"++ln -sfn "$SDK_HL2MP_LINUX_BIN/game_shader_generic_example.so" "$MOD_LINUX_BIN/game_shader_generic_example_srv.so"++ln -sfn "$SDK_LIB_BIN/libtier0.so" "$MOD_LINUX_BIN/libtier0.so"++ln -sfn "$SDK_LIB_BIN/libvstdlib.so" "$MOD_LINUX_BIN/libvstdlib.so"++ln -sfn "$SDK_LIB_BIN/libsteam_api.so" "$MOD_LINUX_BIN/libsteam_api.so"++++if [[ -d "$TF2_BIN" ]]; then++ for module in soundemittersystem scenefilecache datacache materialsystem studiorender vphysics vscript replay shaderapiempty; do++ if [[ -f "$TF2_BIN/${module}_srv.so" ]]; then++ ln -sfn "$TF2_BIN/${module}_srv.so" "$MOD_LINUX_BIN/${module}.so"++ fi++ done++fi++++echo "[openvibe] linux bin/linux64 compatibility links ready"++++# Windows/Proton modules. These are optional on Linux until a Windows build has run.++win_client_candidates=(++ "$SDK_HL2MP_WIN_BIN/client.dll"++ "$SDK_HL2MP_WIN_BIN/win32/client.dll"++ "$ROOT/engine/source-sdk-2013/src/game/client/Release_hl2mp/client.dll"++ "$ROOT/engine/source-sdk-2013/src/game/client/Release/client.dll"++)++win_server_candidates=(++ "$SDK_HL2MP_WIN_BIN/server.dll"++ "$SDK_HL2MP_WIN_BIN/win32/server.dll"++ "$ROOT/engine/source-sdk-2013/src/game/server/Release_hl2mp/server.dll"++ "$ROOT/engine/source-sdk-2013/src/game/server/Release/server.dll"++)++++for src in "${win_client_candidates[@]}"; do++ if copy_if_exists "$src" "$MOD_BIN/client.dll"; then break; fi++done++for src in "${win_server_candidates[@]}"; do++ if copy_if_exists "$src" "$MOD_BIN/server.dll"; then break; fi++done++++if [[ -f "$MOD_BIN/client.dll" ]]; then++ echo "[openvibe] Windows/Proton client.dll present"++else++ echo "[openvibe] Windows/Proton client.dll not present yet; build on Windows to enable Proton in-game client DLL commands"++fi++++if [[ -f "$MOD_BIN/server.dll" ]]; then++ echo "[openvibe] Windows server.dll present"++else++ echo "[openvibe] Windows server.dll not present yet"++fi++SETUP++chmod +x tools/setup-openvibe-bin.sh++++# -----------------------------------------------------------------------------++# 4) Native Linux launcher.++# -----------------------------------------------------------------------------++cat > tools/run-client-linux.sh <<'LINUXRUN'++#!/usr/bin/env bash++set -euo pipefail++++ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"++GAME_DIR="$ROOT/game/openvibe.games"++CLIENT_SO="$GAME_DIR/bin/linux64/client.so"++++find_hl2_linux() {++ local candidates=(++ "${OPENVIBE_HL2_LINUX:-}"++ "$HOME/.steam/steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"++ "$HOME/.local/share/Steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"++ "/mnt/data-f/SteamLibrary/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"++ "/mnt/6tb/ssd_offload/home/$USER/.steam/debian-installation/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"++ )++ for p in "${candidates[@]}"; do++ [[ -n "$p" && -x "$p" ]] && { echo "$p"; return 0; }++ done++ return 1++}++++if [[ ! -f "$CLIENT_SO" && ! -L "$CLIENT_SO" ]]; then++ echo "ERROR: Linux client.so missing at $CLIENT_SO" >&2++ echo "Run: tools/build-sdk-linux.sh && tools/setup-openvibe-bin.sh" >&2++ exit 1++fi++++HL2_LINUX="$(find_hl2_linux)" || {++ echo "ERROR: could not find hl2_linux. Set OPENVIBE_HL2_LINUX=/path/to/hl2_linux" >&2++ exit 1++}++++CONNECT_ARGS=()++if [[ "${1:-}" != "" && "${2:-}" != "" ]]; then++ CONNECT_ARGS=(+connect "$1:$2")++fi++++exec "$HL2_LINUX" \++ -game "$GAME_DIR" \++ -console -dev -novid -sw -w 1280 -h 720 \++ -port 27115 -clientport 27105 \++ +exec openvibe_proton_client.cfg \++ "${CONNECT_ARGS[@]}"++LINUXRUN++chmod +x tools/run-client-linux.sh++++# -----------------------------------------------------------------------------++# 5) Auto launcher: native Linux when available, Proton when Windows DLL exists,++# Proton fallback if explicitly requested.++# -----------------------------------------------------------------------------++cat > tools/run-client-auto.sh <<'AUTORUN'++#!/usr/bin/env bash++set -euo pipefail++++ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"++MODE="${OPENVIBE_CLIENT_MODE:-auto}" # auto | linux | proton | proton-fallback++GAME_DIR="$ROOT/game/openvibe.games"++LINUX_SO="$GAME_DIR/bin/linux64/client.so"++WIN_DLL="$GAME_DIR/bin/client.dll"++++has_linux_client() {++ [[ -f "$LINUX_SO" || -L "$LINUX_SO" ]] && \++ { [[ -n "${OPENVIBE_HL2_LINUX:-}" && -x "$OPENVIBE_HL2_LINUX" ]] || \++ [[ -x "$HOME/.steam/steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux" ]] || \++ [[ -x "$HOME/.local/share/Steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux" ]] || \++ [[ -x "/mnt/data-f/SteamLibrary/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux" ]]; }++}++++has_windows_client_dll() {++ [[ -f "$WIN_DLL" ]]++}++++case "$MODE" in++ linux)++ exec "$ROOT/tools/run-client-linux.sh" "${@:-}"++ ;;++ proton)++ if ! has_windows_client_dll; then++ echo "ERROR: Proton mode requires $WIN_DLL for custom in-game client DLL commands/menu." >&2++ echo "Build Windows DLLs first, or use OPENVIBE_CLIENT_MODE=proton-fallback to launch without client DLL." >&2++ exit 1++ fi++ exec "$ROOT/tools/run-client-proton.sh" "${@:-}"++ ;;++ proton-fallback)++ echo "WARNING: Proton fallback may launch, but without game/openvibe.games/bin/client.dll the in-game ov_* client commands/menu will not exist." >&2++ exec "$ROOT/tools/run-client-proton.sh" "${@:-}"++ ;;++ auto)++ if has_linux_client; then++ echo "[openvibe] auto client mode: native linux"++ exec "$ROOT/tools/run-client-linux.sh" "${@:-}"++ fi++ if has_windows_client_dll; then++ echo "[openvibe] auto client mode: proton with Windows client.dll"++ exec "$ROOT/tools/run-client-proton.sh" "${@:-}"++ fi++ echo "[openvibe] auto client mode: proton fallback, but no Windows client.dll exists yet" >&2++ exec "$ROOT/tools/run-client-proton.sh" "${@:-}"++ ;;++ *)++ echo "ERROR: unknown OPENVIBE_CLIENT_MODE=$MODE. Use auto, linux, proton, or proton-fallback." >&2++ exit 1++ ;;++esac++AUTORUN++chmod +x tools/run-client-auto.sh++++# -----------------------------------------------------------------------------++# 6) Patch Proton launcher to be honest about DLL status and load fallback cfg.++# -----------------------------------------------------------------------------++if [[ -f tools/run-client-proton.sh ]]; then++ backup_file tools/run-client-proton.sh++ python3 <<'PY'++from pathlib import Path++p = Path('tools/run-client-proton.sh')++s = p.read_text()++if 'CLIENT_DLL="$GAME_DIR/bin/client.dll"' not in s:++ s = s.replace('GAME_DIR="/home/workstation/src/openvibe-source/game/openvibe.games"', 'GAME_DIR="/home/workstation/src/openvibe-source/game/openvibe.games"\nCLIENT_DLL="$GAME_DIR/bin/client.dll"')++if 'Proton Windows hl2.exe will not load Linux client.so' not in s:++ marker = 'echo "Launching OpenVibe: Source..."\n'++ s = s.replace(marker, marker + 'if [ ! -f "$CLIENT_DLL" ]; then\n echo "WARNING: $CLIENT_DLL is missing."\n echo "WARNING: Proton Windows hl2.exe will not load Linux client.so; in-game ov_* client commands/menu will be unavailable."\nfi\n')++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 \\\n +exec openvibe_proton_client.cfg \\\n')++p.write_text(s)++PY++fi++++# -----------------------------------------------------------------------------++# 7) Windows QuickJS static lib builder.++# -----------------------------------------------------------------------------++cat > tools/build-quickjs-lib-windows.ps1 <<'PSQJS'++$ErrorActionPreference = 'Stop'++++$Root = if ($env:OPENVIBE_ROOT) { $env:OPENVIBE_ROOT } else { Join-Path $HOME 'src/openvibe-source' }++$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'++++if (!(Test-Path (Join-Path $SrcQjs 'quickjs.c'))) {++ throw "Missing $SrcQjs/quickjs.c. Run tools/vendor-quickjs.sh first."++}++++New-Item -ItemType Directory -Force -Path $SdkQjs, $Out | Out-Null++Get-ChildItem $SrcQjs -File | ForEach-Object { Copy-Item $_.FullName (Join-Path $SdkQjs $_.Name) -Force }++++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."++}++++$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++++$objects = @()++foreach ($src in $sources) {++ $obj = Join-Path $Out ([IO.Path]::GetFileNameWithoutExtension($src) + '.obj')++ Write-Host "[openvibe-qjs-win] cl $src -> $obj"++ & cl.exe /nologo /O2 /MT /W3 /D_CRT_SECURE_NO_WARNINGS /DWIN32 /D_WINDOWS /DCONFIG_VERSION='"openvibe"' /I"$SdkQjs" /c (Join-Path $SdkQjs $src) /Fo"$obj"++ if ($LASTEXITCODE -ne 0) { throw "cl failed for $src" }++ $objects += $obj++}++++$outLib = Join-Path $Out 'libquickjs_openvibe.lib'++& lib.exe /nologo /OUT:"$outLib" $objects++if ($LASTEXITCODE -ne 0) { throw "lib.exe failed" }++Write-Host "[openvibe-qjs-win] built $outLib"++PSQJS++++# -----------------------------------------------------------------------------++# 8) Windows native/Proton DLL build helper.++# -----------------------------------------------------------------------------++cat > tools/build-sdk-windows.ps1 <<'PSWIN'++$ErrorActionPreference = 'Stop'++++$Root = if ($env:OPENVIBE_ROOT) { $env:OPENVIBE_ROOT } else { Join-Path $HOME 'src/openvibe-source' }++$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root 'engine/source-sdk-2013' }++++Write-Host "[openvibe-win] root=$Root"++Write-Host "[openvibe-win] sdk=$Sdk"++++if (!(Test-Path (Join-Path $Sdk 'src'))) { throw "Source SDK not found at $Sdk" }++++# Apply OpenVibe SDK copy/patches through Git Bash, but skip Linux QuickJS .a build.++$bash = Get-Command bash.exe -ErrorAction SilentlyContinue++if ($bash) {++ $rootForBash = $Root++ try {++ $rootForBash = (& $bash.Source -lc "cygpath -u '$Root'" 2>$null).Trim()++ } catch {}++ Write-Host "[openvibe-win] applying SDK patch through bash at $rootForBash"++ & $bash.Source -lc "cd '$rootForBash' && OPENVIBE_SKIP_QJS_BUILD=1 ./tools/apply-openvibe-sdk.sh"++ if ($LASTEXITCODE -ne 0) { throw "apply-openvibe-sdk.sh failed" }++} else {++ Write-Warning "bash.exe not found. Skipping apply-openvibe-sdk.sh. Make sure SDK files are already applied."++}++++& (Join-Path $Root 'tools/build-quickjs-lib-windows.ps1')++++Push-Location (Join-Path $Sdk 'src')++try {++ $vpc = @(++ (Join-Path $Sdk 'src/devtools/bin/vpc.exe'),++ (Join-Path $Sdk 'devtools/bin/vpc.exe')++ ) | Where-Object { Test-Path $_ } | Select-Object -First 1++++ if (Test-Path '.\createallprojects.bat') {++ Write-Host "[openvibe-win] running createallprojects.bat"++ & cmd.exe /c createallprojects.bat++ } elseif ($vpc) {++ Write-Host "[openvibe-win] running VPC: $vpc"++ $ok = $false++ $attempts = @(++ @('/2013','/hl2mp','/game','/mksln','games.sln'),++ @('/hl2mp','/game','/mksln','games.sln'),++ @('+game','/mksln','games.sln')++ )++ foreach ($args in $attempts) {++ & $vpc @args++ if ($LASTEXITCODE -eq 0) { $ok = $true; break }++ }++ if (!$ok) { throw "VPC solution generation failed" }++ } else {++ throw "Could not find createallprojects.bat or vpc.exe"++ }++++ $sln = Get-ChildItem -Path . -Recurse -Filter *.sln | Where-Object { $_.Name -match 'game|hl2mp|everything|sdk' } | Select-Object -First 1++ if (!$sln) { $sln = Get-ChildItem -Path . -Recurse -Filter *.sln | Select-Object -First 1 }++ if (!$sln) { throw "No Visual Studio solution generated" }++++ $msbuild = $null++ $vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/Installer/vswhere.exe'++ if (Test-Path $vswhere) {++ $msbuild = (& $vswhere -latest -products * -requires Microsoft.Component.MSBuild -find 'MSBuild\**\Bin\MSBuild.exe' | Select-Object -First 1)++ }++ if (!$msbuild) { $msbuild = (Get-Command msbuild.exe -ErrorAction SilentlyContinue).Source }++ if (!$msbuild) { throw "MSBuild not found. Install Visual Studio Build Tools with C++ workload." }++++ Write-Host "[openvibe-win] building $($sln.FullName)"++ $built = $false++ foreach ($platform in @('Win32','x86')) {++ foreach ($config in @('Release','Release_HL2MP','Release HL2MP')) {++ Write-Host "[openvibe-win] trying Configuration=$config Platform=$platform"++ & $msbuild $sln.FullName /m /p:Configuration=$config /p:Platform=$platform++ if ($LASTEXITCODE -eq 0) { $built = $true; break }++ }++ if ($built) { break }++ }++ if (!$built) { throw "MSBuild failed for all known configurations" }++}++finally {++ Pop-Location++}++++$modBin = Join-Path $Root 'game/openvibe.games/bin'++New-Item -ItemType Directory -Force -Path $modBin | Out-Null++++$client = Get-ChildItem $Sdk -Recurse -Filter client.dll -ErrorAction SilentlyContinue | Where-Object { $_.FullName -match 'mod_hl2mp|hl2mp|game\\client' } | Sort-Object FullName | Select-Object -First 1++$server = Get-ChildItem $Sdk -Recurse -Filter server.dll -ErrorAction SilentlyContinue | Where-Object { $_.FullName -match 'mod_hl2mp|hl2mp|game\\server' } | Sort-Object FullName | Select-Object -First 1++++if ($client) { Copy-Item $client.FullName (Join-Path $modBin 'client.dll') -Force; Write-Host "[openvibe-win] copied client.dll from $($client.FullName)" } else { Write-Warning "client.dll not found after build" }++if ($server) { Copy-Item $server.FullName (Join-Path $modBin 'server.dll') -Force; Write-Host "[openvibe-win] copied server.dll from $($server.FullName)" } else { Write-Warning "server.dll not found after build" }++++Write-Host "[openvibe-win] done. Proton/Windows client uses game/openvibe.games/bin/client.dll"++PSWIN++++# -----------------------------------------------------------------------------++# 9) Windows launcher helper.++# -----------------------------------------------------------------------------++cat > tools/run-client-windows.ps1 <<'PSRUN'++$ErrorActionPreference = 'Stop'++++$Root = if ($env:OPENVIBE_ROOT) { $env:OPENVIBE_ROOT } else { Join-Path $HOME 'src/openvibe-source' }++$GameDir = Join-Path $Root 'game/openvibe.games'++$ClientDll = Join-Path $GameDir 'bin/client.dll'++$Hl2Exe = if ($env:OPENVIBE_HL2_EXE) { $env:OPENVIBE_HL2_EXE } else { 'C:\Program Files (x86)\Steam\steamapps\common\Source SDK Base 2013 Multiplayer\hl2.exe' }++++if (!(Test-Path $Hl2Exe)) { throw "hl2.exe not found. Set OPENVIBE_HL2_EXE." }++if (!(Test-Path $ClientDll)) { throw "client.dll missing at $ClientDll. Run tools/build-sdk-windows.ps1 first." }++++$args = @('-game', $GameDir, '-console', '-dev', '-novid', '-sw', '-w', '1280', '-h', '720', '+exec', 'openvibe_proton_client.cfg')++if ($args.Count -ge 2 -and $args[0] -match '^\d+\.\d+\.\d+\.\d+$') {++ # no-op; reserved for direct powershell invocation variants++}++++if ($args.Count -ge 2) {}++++# Accept optional IP PORT positional args from after -File.++$extra = $MyInvocation.UnboundArguments++if ($extra.Count -ge 2) {++ $args += @('+connect', "$($extra[0]):$($extra[1])")++}++++Start-Process -FilePath $Hl2Exe -ArgumentList $args -WorkingDirectory (Split-Path $Hl2Exe)++PSRUN++++# -----------------------------------------------------------------------------++# 10) Local wrapper to build what can be built on this machine.++# -----------------------------------------------------------------------------++cat > tools/build-sdk-all-local.sh <<'ALLLOCAL'++#!/usr/bin/env bash++set -euo pipefail++++ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"++cd "$ROOT"++++if [[ "${RUN_LINUX_BUILD:-1}" == "1" ]]; then++ echo "[openvibe] building Linux .so modules"++ tools/build-sdk-linux.sh 2>&1 | tee "$HOME/ov-build-linux.log"++ tools/setup-openvibe-bin.sh++else++ echo "[openvibe] RUN_LINUX_BUILD=0, skipping Linux build"++fi++++echo++if command -v powershell.exe >/dev/null 2>&1; then++ echo "[openvibe] powershell.exe found. To build Windows DLLs, run from Windows Developer PowerShell:"++else++ echo "[openvibe] Windows DLL build requires Windows + Visual Studio Build Tools. Run:"++fi++cat <<TEXT++ cd <repo>++ powershell -ExecutionPolicy Bypass -File tools/build-sdk-windows.ps1++TEXT++++tools/check-openvibe-platform-binaries.sh++ALLLOCAL++chmod +x tools/build-sdk-all-local.sh++++# -----------------------------------------------------------------------------++# 11) Patch Electron main.js to call run-client-auto.sh / run-client-windows.ps1++# rather than hardcoding Proton only.++# -----------------------------------------------------------------------------++if [[ -f launcher/main.js ]]; then++ backup_file launcher/main.js++ python3 <<'PY'++from pathlib import Path++p = Path('launcher/main.js')++s = p.read_text()++start = s.find('async function launchGame(')++if start == -1:++ raise SystemExit('Could not find launchGame in launcher/main.js')++brace = s.find('{', start)++if brace == -1:++ raise SystemExit('Could not find launchGame body')++depth = 0++end = None++for i in range(brace, len(s)):++ if s[i] == '{': depth += 1++ elif s[i] == '}':++ depth -= 1++ if depth == 0:++ end = i + 1++ break++if end is None:++ raise SystemExit('Could not parse launchGame body')++new = r'''async function launchGame(serverIp, serverPort) {++ const root = path.resolve(__dirname, '..');++++ const uiReady = await ensureClientUiServer(root);++ if (!uiReady) {++ dialog.showErrorBox('OpenVibe Menu Server Failed',++ `Could not start http://127.0.0.1:${CLIENT_UI_PORT}/client for the launcher/in-game HTML menu.`);++ return false;++ }++++ const scriptArgs = serverIp && serverPort ? [serverIp, String(serverPort)] : [];++ const isWin = process.platform === 'win32';++ const launcherScript = isWin++ ? path.join(root, 'tools', 'run-client-windows.ps1')++ : path.join(root, 'tools', 'run-client-auto.sh');++++ if (!fs.existsSync(launcherScript)) {++ dialog.showErrorBox('OpenVibe Launcher Script Missing', `Could not find:\n${launcherScript}`);++ return false;++ }++++ const command = isWin ? 'powershell.exe' : 'bash';++ const args = isWin++ ? ['-ExecutionPolicy', 'Bypass', '-File', launcherScript, ...scriptArgs]++ : [launcherScript, ...scriptArgs];++++ console.log('[launcher] spawning client:', command, args.join(' '));++++ gameProcess = spawn(command, args, {++ cwd: root,++ detached: true,++ env: {++ ...process.env,++ OPENVIBE_ROOT: root,++ DISPLAY: process.env.DISPLAY || ':0',++ },++ });++++ gameProcess.stdout.on('data', (d) => console.log('[game]', d.toString().trim()));++ gameProcess.stderr.on('data', (d) => console.error('[game]', d.toString().trim()));++ gameProcess.on('exit', (code) => {++ console.log('[launcher] game exited with code', code);++ gameProcess = null;++ mainWindow?.webContents.send('game-exited', code);++ mainWindow?.show();++ mainWindow?.focus();++ });++++ mainWindow?.webContents.send('game-started', gameProcess.pid);++++ // Keep Electron visible by default so users are not dumped behind a frozen Source loading window.++ // Advanced: set OPENVIBE_HIDE_LAUNCHER_ON_GAME_READY=1 to hide after a conservative delay.++ if (process.env.OPENVIBE_HIDE_LAUNCHER_ON_GAME_READY === '1') {++ setTimeout(() => {++ if (gameProcess && mainWindow && !mainWindow.isDestroyed()) mainWindow.hide();++ }, Number(process.env.OPENVIBE_GAME_READY_HIDE_DELAY_MS || 12000));++ }++++ gameProcess.unref();++ return true;++}'''++s = s[:start] + new + s[end:]++p.write_text(s)++PY++fi++++# -----------------------------------------------------------------------------++# 12) Docs for the repo.++# -----------------------------------------------------------------------------++cat > docs/MULTIPLATFORM_SOURCE_BUILDS.md <<'DOC'++# OpenVibe: Source multi-platform build matrix++++OpenVibe needs different client/server binaries for different Source runtimes.++++| Runtime | Executable | Module loaded by the engine | Output path |++| --- | --- | --- | --- |++| Linux native client | `hl2_linux` | Linux ELF client module | `game/openvibe.games/bin/linux64/client.so` |++| Linux dedicated server | `srcds_linux` | Linux ELF server module | `game/openvibe.games/bin/linux64/server.so` |++| Windows native client | `hl2.exe` | Windows PE client DLL | `game/openvibe.games/bin/client.dll` |++| Proton client | Windows `hl2.exe` under Proton | Windows PE client DLL | `game/openvibe.games/bin/client.dll` |++| Windows dedicated server | `srcds.exe` | Windows PE server DLL | `game/openvibe.games/bin/server.dll` |++++Important: Proton does **not** load Linux `client.so`. It runs Windows `hl2.exe`, so it needs `client.dll`.++If the in-game console says `Unknown command "ov_join"` or `Unknown command "ov_menu"`, the OpenVibe client module is not loaded for that runtime.++++## Linux build++++```bash++cd ~/src/openvibe-source++tools/build-sdk-linux.sh++tools/setup-openvibe-bin.sh++tools/check-openvibe-platform-binaries.sh++```++++## Windows DLL build++++Run on Windows in a Visual Studio Developer PowerShell with C++ Build Tools installed:++++```powershell++cd C:\path\to\openvibe-source++powershell -ExecutionPolicy Bypass -File tools\build-sdk-windows.ps1++```++++That script builds QuickJS as `libquickjs_openvibe.lib`, runs VPC/MSBuild, then copies DLLs into:++++```text++game/openvibe.games/bin/client.dll++game/openvibe.games/bin/server.dll++```++++Copy those DLLs back to the Linux dev checkout if you build on a separate Windows VM. Proton will then load them.++++## Runtime selection++++```bash++# Prefer native Linux if hl2_linux + client.so exist, otherwise use Proton if client.dll exists.++tools/run-client-auto.sh 127.0.0.1 27015++++# Force native Linux.++OPENVIBE_CLIENT_MODE=linux tools/run-client-auto.sh 127.0.0.1 27015++++# Force Proton; requires client.dll.++OPENVIBE_CLIENT_MODE=proton tools/run-client-auto.sh 127.0.0.1 27015++++# Launch Proton even without client.dll, but in-game client commands/menu will not exist.++OPENVIBE_CLIENT_MODE=proton-fallback tools/run-client-auto.sh 127.0.0.1 27015++```++DOC++++# Run setup and diagnostic, but do not force a long compile here.++tools/setup-openvibe-bin.sh || true++++echo++echo "[openvibe] generated multi-platform support files"++echo++tools/check-openvibe-platform-binaries.sh || true++++echo++echo "Next optional local build:"++echo " RUN_LINUX_BUILD=1 tools/build-sdk-all-local.sh"++echo++echo "Windows DLL build, from Windows Developer PowerShell:"++echo " powershell -ExecutionPolicy Bypass -File tools/build-sdk-windows.ps1"@@@ -53,7 +53,9 @@ copy_file "$ROOT/sdk/openvibe/server/hl2mp/openvibe_js_server.cpp" \copy_tree "$ROOT/sdk/openvibe/third_party/quickjs" \"$SDK/src/game/shared/openvibe/third_party/quickjs"--"$ROOT/tools/build-quickjs-lib.sh"++if [[ "${OPENVIBE_SKIP_QJS_BUILD:-0}" != "1" ]]; then++ "$ROOT/tools/build-quickjs-lib.sh"++fiCLIENT_VPC="$SDK/src/game/client/client_hl2mp.vpc"SERVER_VPC="$SDK/src/game/server/server_hl2mp.vpc"nnew file mode 100644@@@ -0,0 +1,43 @@++$ErrorActionPreference = 'Stop'++++$Root = if ($env:OPENVIBE_ROOT) { $env:OPENVIBE_ROOT } else { Join-Path $HOME 'src/openvibe-source' }++$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'++++if (!(Test-Path (Join-Path $SrcQjs 'quickjs.c'))) {++ throw "Missing $SrcQjs/quickjs.c. Run tools/vendor-quickjs.sh first."++}++++New-Item -ItemType Directory -Force -Path $SdkQjs, $Out | Out-Null++Get-ChildItem $SrcQjs -File | ForEach-Object { Copy-Item $_.FullName (Join-Path $SdkQjs $_.Name) -Force }++++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."++}++++$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++++$objects = @()++foreach ($src in $sources) {++ $obj = Join-Path $Out ([IO.Path]::GetFileNameWithoutExtension($src) + '.obj')++ Write-Host "[openvibe-qjs-win] cl $src -> $obj"++ & cl.exe /nologo /O2 /MT /W3 /D_CRT_SECURE_NO_WARNINGS /DWIN32 /D_WINDOWS /DCONFIG_VERSION='"openvibe"' /I"$SdkQjs" /c (Join-Path $SdkQjs $src) /Fo"$obj"++ if ($LASTEXITCODE -ne 0) { throw "cl failed for $src" }++ $objects += $obj++}++++$outLib = Join-Path $Out 'libquickjs_openvibe.lib'++& lib.exe /nologo /OUT:"$outLib" $objects++if ($LASTEXITCODE -ne 0) { throw "lib.exe failed" }++Write-Host "[openvibe-qjs-win] built $outLib"nnew file mode 100755@@@ -0,0 +1,26 @@++#!/usr/bin/env bash++set -euo pipefail++++ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"++cd "$ROOT"++++if [[ "${RUN_LINUX_BUILD:-1}" == "1" ]]; then++ echo "[openvibe] building Linux .so modules"++ tools/build-sdk-linux.sh 2>&1 | tee "$HOME/ov-build-linux.log"++ tools/setup-openvibe-bin.sh++else++ echo "[openvibe] RUN_LINUX_BUILD=0, skipping Linux build"++fi++++echo++if command -v powershell.exe >/dev/null 2>&1; then++ echo "[openvibe] powershell.exe found. To build Windows DLLs, run from Windows Developer PowerShell:"++else++ echo "[openvibe] Windows DLL build requires Windows + Visual Studio Build Tools. Run:"++fi++cat <<TEXT++ cd <repo>++ powershell -ExecutionPolicy Bypass -File tools/build-sdk-windows.ps1++TEXT++++tools/check-openvibe-platform-binaries.shnnew file mode 100644@@@ -0,0 +1,91 @@++$ErrorActionPreference = 'Stop'++++$Root = if ($env:OPENVIBE_ROOT) { $env:OPENVIBE_ROOT } else { Join-Path $HOME 'src/openvibe-source' }++$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root 'engine/source-sdk-2013' }++++Write-Host "[openvibe-win] root=$Root"++Write-Host "[openvibe-win] sdk=$Sdk"++++if (!(Test-Path (Join-Path $Sdk 'src'))) { throw "Source SDK not found at $Sdk" }++++# Apply OpenVibe SDK copy/patches through Git Bash, but skip Linux QuickJS .a build.++$bash = Get-Command bash.exe -ErrorAction SilentlyContinue++if ($bash) {++ $rootForBash = $Root++ try {++ $rootForBash = (& $bash.Source -lc "cygpath -u '$Root'" 2>$null).Trim()++ } catch {}++ Write-Host "[openvibe-win] applying SDK patch through bash at $rootForBash"++ & $bash.Source -lc "cd '$rootForBash' && OPENVIBE_SKIP_QJS_BUILD=1 ./tools/apply-openvibe-sdk.sh"++ if ($LASTEXITCODE -ne 0) { throw "apply-openvibe-sdk.sh failed" }++} else {++ Write-Warning "bash.exe not found. Skipping apply-openvibe-sdk.sh. Make sure SDK files are already applied."++}++++& (Join-Path $Root 'tools/build-quickjs-lib-windows.ps1')++++Push-Location (Join-Path $Sdk 'src')++try {++ $vpc = @(++ (Join-Path $Sdk 'src/devtools/bin/vpc.exe'),++ (Join-Path $Sdk 'devtools/bin/vpc.exe')++ ) | Where-Object { Test-Path $_ } | Select-Object -First 1++++ if (Test-Path '.\createallprojects.bat') {++ Write-Host "[openvibe-win] running createallprojects.bat"++ & cmd.exe /c createallprojects.bat++ } elseif ($vpc) {++ Write-Host "[openvibe-win] running VPC: $vpc"++ $ok = $false++ $attempts = @(++ @('/2013','/hl2mp','/game','/mksln','games.sln'),++ @('/hl2mp','/game','/mksln','games.sln'),++ @('+game','/mksln','games.sln')++ )++ foreach ($args in $attempts) {++ & $vpc @args++ if ($LASTEXITCODE -eq 0) { $ok = $true; break }++ }++ if (!$ok) { throw "VPC solution generation failed" }++ } else {++ throw "Could not find createallprojects.bat or vpc.exe"++ }++++ $sln = Get-ChildItem -Path . -Recurse -Filter *.sln | Where-Object { $_.Name -match 'game|hl2mp|everything|sdk' } | Select-Object -First 1++ if (!$sln) { $sln = Get-ChildItem -Path . -Recurse -Filter *.sln | Select-Object -First 1 }++ if (!$sln) { throw "No Visual Studio solution generated" }++++ $msbuild = $null++ $vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/Installer/vswhere.exe'++ if (Test-Path $vswhere) {++ $msbuild = (& $vswhere -latest -products * -requires Microsoft.Component.MSBuild -find 'MSBuild\**\Bin\MSBuild.exe' | Select-Object -First 1)++ }++ if (!$msbuild) { $msbuild = (Get-Command msbuild.exe -ErrorAction SilentlyContinue).Source }++ if (!$msbuild) { throw "MSBuild not found. Install Visual Studio Build Tools with C++ workload." }++++ Write-Host "[openvibe-win] building $($sln.FullName)"++ $built = $false++ foreach ($platform in @('Win32','x86')) {++ foreach ($config in @('Release','Release_HL2MP','Release HL2MP')) {++ Write-Host "[openvibe-win] trying Configuration=$config Platform=$platform"++ & $msbuild $sln.FullName /m /p:Configuration=$config /p:Platform=$platform++ if ($LASTEXITCODE -eq 0) { $built = $true; break }++ }++ if ($built) { break }++ }++ if (!$built) { throw "MSBuild failed for all known configurations" }++}++finally {++ Pop-Location++}++++$modBin = Join-Path $Root 'game/openvibe.games/bin'++New-Item -ItemType Directory -Force -Path $modBin | Out-Null++++$client = Get-ChildItem $Sdk -Recurse -Filter client.dll -ErrorAction SilentlyContinue | Where-Object { $_.FullName -match 'mod_hl2mp|hl2mp|game\\client' } | Sort-Object FullName | Select-Object -First 1++$server = Get-ChildItem $Sdk -Recurse -Filter server.dll -ErrorAction SilentlyContinue | Where-Object { $_.FullName -match 'mod_hl2mp|hl2mp|game\\server' } | Sort-Object FullName | Select-Object -First 1++++if ($client) { Copy-Item $client.FullName (Join-Path $modBin 'client.dll') -Force; Write-Host "[openvibe-win] copied client.dll from $($client.FullName)" } else { Write-Warning "client.dll not found after build" }++if ($server) { Copy-Item $server.FullName (Join-Path $modBin 'server.dll') -Force; Write-Host "[openvibe-win] copied server.dll from $($server.FullName)" } else { Write-Warning "server.dll not found after build" }++++Write-Host "[openvibe-win] done. Proton/Windows client uses game/openvibe.games/bin/client.dll"nnew file mode 100755@@@ -0,0 +1,307 @@++#!/usr/bin/env bash++set -euo pipefail++++ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"++cd "$ROOT"++++echo "[openvibe] adding Windows DLL build-from-Linux workflow helpers"++echo "[openvibe] root=$ROOT"++++mkdir -p .github/workflows tools docs .tmp++++cat > .github/workflows/windows-source-sdk-dlls.yml <<'YAML'++name: Build Windows Source DLLs++++on:++ workflow_dispatch:++ push:++ branches:++ - codex/openvibe-next-steps++ paths:++ - 'sdk/openvibe/**'++ - 'game/openvibe.games/**'++ - 'tools/build-sdk-windows.ps1'++ - 'tools/build-quickjs-lib-windows.ps1'++ - '.github/workflows/windows-source-sdk-dlls.yml'++++jobs:++ windows-dlls:++ name: Windows client.dll/server.dll++ runs-on: windows-2022++ timeout-minutes: 90++++ steps:++ - name: Checkout OpenVibe++ uses: actions/checkout@v4++ with:++ fetch-depth: 1++++ - name: Configure git line endings++ shell: pwsh++ run: |++ git config --global core.autocrlf false++++ - name: Setup MSBuild++ uses: microsoft/setup-msbuild@v2++++ - name: Show toolchain++ shell: pwsh++ run: |++ where msbuild++ where cl++ git --version++ pwsh --version++++ - name: Build Windows Source SDK DLLs++ shell: pwsh++ run: |++ powershell -NoProfile -ExecutionPolicy Bypass -File tools\build-sdk-windows.ps1++++ - name: Stage artifacts++ shell: pwsh++ run: |++ New-Item -Force -ItemType Directory artifact\game\openvibe.games\bin | Out-Null++ New-Item -Force -ItemType Directory artifact\logs | Out-Null++++ $candidates = @(++ "game\openvibe.games\bin\client.dll",++ "game\openvibe.games\bin\server.dll",++ "engine\source-sdk-2013\game\mod_hl2mp\bin\client.dll",++ "engine\source-sdk-2013\game\mod_hl2mp\bin\server.dll",++ "engine\source-sdk-2013\game\mod_hl2mp\bin\client.pdb",++ "engine\source-sdk-2013\game\mod_hl2mp\bin\server.pdb"++ )++++ foreach ($file in $candidates) {++ if (Test-Path $file) {++ Copy-Item $file artifact\game\openvibe.games\bin\ -Force++ }++ }++++ if (Test-Path "windows-build.log") {++ Copy-Item windows-build.log artifact\logs\windows-build.log -Force++ }++++ Get-ChildItem artifact -Recurse | Format-Table FullName,Length++++ - name: Upload Windows DLL artifact++ uses: actions/upload-artifact@v4++ with:++ name: openvibe-windows-dlls++ path: artifact/**++ if-no-files-found: error++YAML++++cat > tools/request-windows-dll-build.sh <<'REQ'++#!/usr/bin/env bash++set -euo pipefail++++ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"++cd "$ROOT"++++WORKFLOW="${OPENVIBE_WINDOWS_WORKFLOW:-windows-source-sdk-dlls.yml}"++ARTIFACT="${OPENVIBE_WINDOWS_ARTIFACT:-openvibe-windows-dlls}"++BRANCH="${OPENVIBE_BRANCH:-$(git rev-parse --abbrev-ref HEAD)}"++OUT_DIR="${OPENVIBE_WINDOWS_ARTIFACT_DIR:-$ROOT/.tmp/windows-dll-artifact}"++++if ! command -v gh >/dev/null 2>&1; then++ echo "Missing GitHub CLI: gh" >&2++ echo "Install it, then run: 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++++if [[ -n "$(git status --porcelain)" ]]; then++ echo "[openvibe] WARNING: working tree has uncommitted changes."++ echo "[openvibe] GitHub Actions builds the pushed branch, not your uncommitted local files."++ echo++ git status --short++ echo++ read -r -p "Continue anyway? [y/N] " answer++ [[ "$answer" =~ ^[Yy]$ ]] || exit 1++fi++++echo "[openvibe] triggering GitHub Actions Windows DLL build"++echo "[openvibe] workflow=$WORKFLOW branch=$BRANCH"++++gh workflow run "$WORKFLOW" --ref "$BRANCH"++++echo "[openvibe] waiting for run to appear..."++RUN_ID=""++for _ in {1..30}; do++ RUN_ID="$(gh run list --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId,status,event -q '.[0].databaseId // empty' 2>/dev/null || true)"++ [[ -n "$RUN_ID" ]] && break++ sleep 2++done++++if [[ -z "$RUN_ID" ]]; then++ echo "Could not find the workflow run. Check GitHub Actions UI." >&2++ exit 1++fi++++echo "[openvibe] watching run $RUN_ID"++gh run watch "$RUN_ID" --exit-status++++rm -rf "$OUT_DIR"++mkdir -p "$OUT_DIR"++++echo "[openvibe] downloading artifact $ARTIFACT"++gh run download "$RUN_ID" --name "$ARTIFACT" --dir "$OUT_DIR"++++echo "[openvibe] downloaded files:"++find "$OUT_DIR" -type f -maxdepth 8 -print | sort++++echo++echo "[openvibe] installing Windows DLLs into game/openvibe.games/bin"++mkdir -p "$ROOT/game/openvibe.games/bin"++++find "$OUT_DIR" -type f \( -iname 'client.dll' -o -iname 'server.dll' -o -iname 'client.pdb' -o -iname 'server.pdb' \) -print0 |++while IFS= read -r -d '' file; do++ cp -f "$file" "$ROOT/game/openvibe.games/bin/$(basename "$file")"++ echo " installed $(basename "$file")"++done++++echo++"$ROOT/tools/check-openvibe-platform-binaries.sh" || true++++echo++echo "[openvibe] done."++echo "Proton/Windows can use:"++echo " game/openvibe.games/bin/client.dll"++echo " game/openvibe.games/bin/server.dll"++REQ++chmod +x tools/request-windows-dll-build.sh++++cat > tools/check-openvibe-platform-binaries.sh <<'CHECK'++#!/usr/bin/env bash++set -euo pipefail++++ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"++cd "$ROOT"++++linux_bin="$ROOT/game/openvibe.games/bin/linux64"++win_bin="$ROOT/game/openvibe.games/bin"++++echo "[openvibe] platform binary check"++echo++++check_file() {++ local label="$1"++ local file="$2"++ if [[ -f "$file" || -L "$file" ]]; then++ echo "[ok] $label: ${file#$ROOT/}"++ file "$file" 2>/dev/null | sed 's/^/ /' || true++ else++ echo "[missing] $label: ${file#$ROOT/}"++ fi++}++++check_file "Linux client" "$linux_bin/client.so"++check_file "Linux server" "$linux_bin/server.so"++check_file "Windows client" "$win_bin/client.dll"++check_file "Windows server" "$win_bin/server.dll"++++echo++if [[ -f "$win_bin/client.dll" ]]; then++ echo "[openvibe] Proton Windows hl2.exe should be able to load the OpenVibe client DLL."++else++ echo "[openvibe] Proton Windows hl2.exe will NOT have in-game ov_* client commands until client.dll exists."++fi++CHECK++chmod +x tools/check-openvibe-platform-binaries.sh++++cat > tools/probe-local-windows-build-on-linux.sh <<'PROBE'++#!/usr/bin/env bash++set -euo pipefail++++echo "[openvibe] local Windows build-on-Linux probe"++echo++echo "Pure MinGW cross-compile is not the recommended route for Source SDK 2013 client.dll."++echo "The Windows Source client DLL needs the MSVC ABI/toolchain and Valve's Windows project layout."++echo++echo "What this host has:"++++for tool in wine wine64 cl link msbuild x86_64-w64-mingw32-g++ gh; do++ if command -v "$tool" >/dev/null 2>&1; then++ echo "[ok] $tool -> $(command -v "$tool")"++ else++ echo "[missing] $tool"++ fi++done++++echo++echo "Recommended from Linux:"++echo " 1. Commit/push your branch."++echo " 2. Run: tools/request-windows-dll-build.sh"++echo " 3. Let GitHub Actions Windows runner build client.dll/server.dll with MSVC."++echo++echo "Experimental local path:"++echo " - Windows VM on Linux, or"++echo " - Wine with Visual Studio Build Tools installed, then run tools/build-sdk-windows.ps1 under that environment."++echo "This is brittle; use the GitHub Actions/Windows runner first."++PROBE++chmod +x tools/probe-local-windows-build-on-linux.sh++++cat > docs/BUILD_WINDOWS_DLLS_FROM_LINUX.md <<'DOC'++# Building Windows `client.dll` / `server.dll` from Linux++++OpenVibe supports two binary families:++++| Runtime | Client binary | Server binary | Notes |++| --- | --- | --- | --- |++| Native Linux Source | `game/openvibe.games/bin/linux64/client.so` | `game/openvibe.games/bin/linux64/server.so` | Built by the Linux SDK build. |++| Windows / Proton Source | `game/openvibe.games/bin/client.dll` | `game/openvibe.games/bin/server.dll` | Must be built with a Windows/MSVC-compatible toolchain. |++++## Can Windows DLLs be built on Linux?++++Operationally, yes: trigger a Windows build from Linux using GitHub Actions:++++```bash++git push++tools/request-windows-dll-build.sh++```++++That runs the build on a Windows runner, downloads the `openvibe-windows-dlls` artifact, and installs `client.dll` / `server.dll` into `game/openvibe.games/bin`.++++## Why not just MinGW?++++Source SDK 2013's Windows client/server DLLs are built around the MSVC ABI, Valve's VPC-generated Visual Studio projects, and Windows import/static libraries. A MinGW cross-compile from Linux is theoretically possible only after significant porting and import-library work, but it is not the practical path.++++## Reliable options++++1. **GitHub Actions Windows runner**: easiest from a Linux workstation.++2. **Windows VM on Linux**: build with Visual Studio Build Tools/MSBuild, copy DLLs back.++3. **Wine + Visual Studio Build Tools**: possible but brittle; treat as experimental.++++## Test++++```bash++tools/check-openvibe-platform-binaries.sh++OPENVIBE_CLIENT_MODE=proton tools/run-client-auto.sh 127.0.0.1 27015++```++++If `client.dll` is present and correct, Proton's Windows `hl2.exe` should load OpenVibe client commands like `ov_join` and `ov_menu`.++DOC++++# Make git keep workflows/docs/scripts.++git add .github/workflows/windows-source-sdk-dlls.yml \++ tools/request-windows-dll-build.sh \++ tools/check-openvibe-platform-binaries.sh \++ tools/probe-local-windows-build-on-linux.sh \++ docs/BUILD_WINDOWS_DLLS_FROM_LINUX.md++++echo++echo "[openvibe] added Windows DLL build-from-Linux support."++echo++echo "Next:"++echo " git status"++echo " git commit -m \"Add Windows DLL build workflow helpers\""++echo " git push"++echo " tools/request-windows-dll-build.sh"++echo++echo "Probe this Linux host with:"++echo " tools/probe-local-windows-build-on-linux.sh"nnew file mode 100755@@@ -0,0 +1,54 @@++#!/usr/bin/env bash++set -euo pipefail++++ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"++MODE="${OPENVIBE_CLIENT_MODE:-auto}" # auto | linux | proton | proton-fallback++GAME_DIR="$ROOT/game/openvibe.games"++LINUX_SO="$GAME_DIR/bin/linux64/client.so"++WIN_DLL="$GAME_DIR/bin/client.dll"++++has_linux_client() {++ [[ -f "$LINUX_SO" || -L "$LINUX_SO" ]] && \++ { [[ -n "${OPENVIBE_HL2_LINUX:-}" && -x "$OPENVIBE_HL2_LINUX" ]] || \++ [[ -x "$HOME/.steam/steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux" ]] || \++ [[ -x "$HOME/.local/share/Steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux" ]] || \++ [[ -x "/mnt/data-f/SteamLibrary/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux" ]]; }++}++++has_windows_client_dll() {++ [[ -f "$WIN_DLL" ]]++}++++case "$MODE" in++ linux)++ exec "$ROOT/tools/run-client-linux.sh" "${@:-}"++ ;;++ proton)++ if ! has_windows_client_dll; then++ echo "ERROR: Proton mode requires $WIN_DLL for custom in-game client DLL commands/menu." >&2++ echo "Build Windows DLLs first, or use OPENVIBE_CLIENT_MODE=proton-fallback to launch without client DLL." >&2++ exit 1++ fi++ exec "$ROOT/tools/run-client-proton.sh" "${@:-}"++ ;;++ proton-fallback)++ echo "WARNING: Proton fallback may launch, but without game/openvibe.games/bin/client.dll the in-game ov_* client commands/menu will not exist." >&2++ exec "$ROOT/tools/run-client-proton.sh" "${@:-}"++ ;;++ auto)++ if has_linux_client; then++ echo "[openvibe] auto client mode: native linux"++ exec "$ROOT/tools/run-client-linux.sh" "${@:-}"++ fi++ if has_windows_client_dll; then++ echo "[openvibe] auto client mode: proton with Windows client.dll"++ exec "$ROOT/tools/run-client-proton.sh" "${@:-}"++ fi++ echo "[openvibe] auto client mode: proton fallback, but no Windows client.dll exists yet" >&2++ exec "$ROOT/tools/run-client-proton.sh" "${@:-}"++ ;;++ *)++ echo "ERROR: unknown OPENVIBE_CLIENT_MODE=$MODE. Use auto, linux, proton, or proton-fallback." >&2++ exit 1++ ;;++esacnnew file mode 100755@@@ -0,0 +1,43 @@++#!/usr/bin/env bash++set -euo pipefail++++ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"++GAME_DIR="$ROOT/game/openvibe.games"++CLIENT_SO="$GAME_DIR/bin/linux64/client.so"++++find_hl2_linux() {++ local candidates=(++ "${OPENVIBE_HL2_LINUX:-}"++ "$HOME/.steam/steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"++ "$HOME/.local/share/Steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"++ "/mnt/data-f/SteamLibrary/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"++ "/mnt/6tb/ssd_offload/home/$USER/.steam/debian-installation/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"++ )++ for p in "${candidates[@]}"; do++ [[ -n "$p" && -x "$p" ]] && { echo "$p"; return 0; }++ done++ return 1++}++++if [[ ! -f "$CLIENT_SO" && ! -L "$CLIENT_SO" ]]; then++ echo "ERROR: Linux client.so missing at $CLIENT_SO" >&2++ echo "Run: tools/build-sdk-linux.sh && tools/setup-openvibe-bin.sh" >&2++ exit 1++fi++++HL2_LINUX="$(find_hl2_linux)" || {++ echo "ERROR: could not find hl2_linux. Set OPENVIBE_HL2_LINUX=/path/to/hl2_linux" >&2++ exit 1++}++++CONNECT_ARGS=()++if [[ "${1:-}" != "" && "${2:-}" != "" ]]; then++ CONNECT_ARGS=(+connect "$1:$2")++fi++++exec "$HL2_LINUX" \++ -game "$GAME_DIR" \++ -console -dev -novid -sw -w 1280 -h 720 \++ -port 27115 -clientport 27105 \++ +exec openvibe_proton_client.cfg \++ "${CONNECT_ARGS[@]}"@@@ -9,6 +9,7 @@ GE_PROTON="$STEAM_PATH/compatibilitytools.d/GE-Proton10-34"STEAM_COMPAT_DATA="$STEAM_PATH/steamapps/compatdata/243750"HL2_EXE="/mnt/data-f/SteamLibrary/steamapps/common/Source SDK Base 2013 Multiplayer/hl2.exe"GAME_DIR="/home/workstation/src/openvibe-source/game/openvibe.games"++CLIENT_DLL="$GAME_DIR/bin/client.dll"# Check dependenciesif [ ! -f "$HL2_EXE" ]; thennnew file mode 100644@@@ -0,0 +1,24 @@++$ErrorActionPreference = 'Stop'++++$Root = if ($env:OPENVIBE_ROOT) { $env:OPENVIBE_ROOT } else { Join-Path $HOME 'src/openvibe-source' }++$GameDir = Join-Path $Root 'game/openvibe.games'++$ClientDll = Join-Path $GameDir 'bin/client.dll'++$Hl2Exe = if ($env:OPENVIBE_HL2_EXE) { $env:OPENVIBE_HL2_EXE } else { 'C:\Program Files (x86)\Steam\steamapps\common\Source SDK Base 2013 Multiplayer\hl2.exe' }++++if (!(Test-Path $Hl2Exe)) { throw "hl2.exe not found. Set OPENVIBE_HL2_EXE." }++if (!(Test-Path $ClientDll)) { throw "client.dll missing at $ClientDll. Run tools/build-sdk-windows.ps1 first." }++++$args = @('-game', $GameDir, '-console', '-dev', '-novid', '-sw', '-w', '1280', '-h', '720', '+exec', 'openvibe_proton_client.cfg')++if ($args.Count -ge 2 -and $args[0] -match '^\d+\.\d+\.\d+\.\d+$') {++ # no-op; reserved for direct powershell invocation variants++}++++if ($args.Count -ge 2) {}++++# Accept optional IP PORT positional args from after -File.++$extra = $MyInvocation.UnboundArguments++if ($extra.Count -ge 2) {++ $args += @('+connect', "$($extra[0]):$($extra[1])")++}++++Start-Process -FilePath $Hl2Exe -ArgumentList $args -WorkingDirectory (Split-Path $Hl2Exe)@@@ -3,12 +3,14 @@ set -euo pipefailROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"TF2_SRCDS="${OPENVIBE_SRCDS:-$HOME/srcds/tf2}"--MOD_BIN="$ROOT/game/openvibe.games/bin/linux64"--SDK_HL2MP_BIN="$ROOT/engine/source-sdk-2013/game/mod_hl2mp/bin/linux64"++MOD_BIN="$ROOT/game/openvibe.games/bin"++MOD_LINUX_BIN="$MOD_BIN/linux64"++SDK_HL2MP_LINUX_BIN="$ROOT/engine/source-sdk-2013/game/mod_hl2mp/bin/linux64"SDK_LIB_BIN="$ROOT/engine/source-sdk-2013/src/lib/public/linux64"TF2_BIN="$TF2_SRCDS/bin/linux64"++SDK_HL2MP_WIN_BIN="$ROOT/engine/source-sdk-2013/game/mod_hl2mp/bin"--mkdir -p "$MOD_BIN"++mkdir -p "$MOD_LINUX_BIN" "$MOD_BIN"require_file() {if [[ ! -f "$1" ]]; then@@@ -17,28 +19,80 @@ require_file() {fi}--require_file "$SDK_HL2MP_BIN/client.so"--require_file "$SDK_HL2MP_BIN/server.so"--require_file "$SDK_HL2MP_BIN/game_shader_generic_example.so"++link_if_exists() {++ local src="$1" dst="$2"++ if [[ -f "$src" ]]; then++ ln -sfn "$src" "$dst"++ echo "[openvibe] linked ${dst#$ROOT/} -> $src"++ fi++}++++copy_if_exists() {++ local src="$1" dst="$2"++ if [[ -f "$src" ]]; then++ install -D -m 0644 "$src" "$dst"++ echo "[openvibe] copied ${dst#$ROOT/}"++ return 0++ fi++ return 1++}++++# Linux native / Linux dedicated server modules.++require_file "$SDK_HL2MP_LINUX_BIN/client.so"++require_file "$SDK_HL2MP_LINUX_BIN/server.so"++require_file "$SDK_HL2MP_LINUX_BIN/game_shader_generic_example.so"require_file "$SDK_LIB_BIN/libtier0.so"require_file "$SDK_LIB_BIN/libvstdlib.so"require_file "$SDK_LIB_BIN/libsteam_api.so"--require_file "$TF2_BIN/soundemittersystem_srv.so"--require_file "$TF2_BIN/shaderapiempty_srv.so"----ln -sfn "$SDK_HL2MP_BIN/client.so" "$MOD_BIN/client.so"--ln -sfn client.so "$MOD_BIN/client_srv.so"--ln -sfn "$SDK_HL2MP_BIN/server.so" "$MOD_BIN/server.so"--ln -sfn server.so "$MOD_BIN/server_srv.so"--ln -sfn "$SDK_HL2MP_BIN/game_shader_generic_example.so" "$MOD_BIN/game_shader_generic_example_srv.so"----ln -sfn "$SDK_LIB_BIN/libtier0.so" "$MOD_BIN/libtier0.so"--ln -sfn "$SDK_LIB_BIN/libvstdlib.so" "$MOD_BIN/libvstdlib.so"--ln -sfn "$SDK_LIB_BIN/libsteam_api.so" "$MOD_BIN/libsteam_api.so"----for module in soundemittersystem scenefilecache datacache materialsystem studiorender vphysics vscript replay shaderapiempty; do-- require_file "$TF2_BIN/${module}_srv.so"-- ln -sfn "$TF2_BIN/${module}_srv.so" "$MOD_BIN/${module}.so"++++ln -sfn "$SDK_HL2MP_LINUX_BIN/client.so" "$MOD_LINUX_BIN/client.so"++ln -sfn client.so "$MOD_LINUX_BIN/client_srv.so"++ln -sfn "$SDK_HL2MP_LINUX_BIN/server.so" "$MOD_LINUX_BIN/server.so"++ln -sfn server.so "$MOD_LINUX_BIN/server_srv.so"++ln -sfn "$SDK_HL2MP_LINUX_BIN/game_shader_generic_example.so" "$MOD_LINUX_BIN/game_shader_generic_example_srv.so"++ln -sfn "$SDK_LIB_BIN/libtier0.so" "$MOD_LINUX_BIN/libtier0.so"++ln -sfn "$SDK_LIB_BIN/libvstdlib.so" "$MOD_LINUX_BIN/libvstdlib.so"++ln -sfn "$SDK_LIB_BIN/libsteam_api.so" "$MOD_LINUX_BIN/libsteam_api.so"++++if [[ -d "$TF2_BIN" ]]; then++ for module in soundemittersystem scenefilecache datacache materialsystem studiorender vphysics vscript replay shaderapiempty; do++ if [[ -f "$TF2_BIN/${module}_srv.so" ]]; then++ ln -sfn "$TF2_BIN/${module}_srv.so" "$MOD_LINUX_BIN/${module}.so"++ fi++ done++fi++++echo "[openvibe] linux bin/linux64 compatibility links ready"++++# Windows/Proton modules. These are optional on Linux until a Windows build has run.++win_client_candidates=(++ "$SDK_HL2MP_WIN_BIN/client.dll"++ "$SDK_HL2MP_WIN_BIN/win32/client.dll"++ "$ROOT/engine/source-sdk-2013/src/game/client/Release_hl2mp/client.dll"++ "$ROOT/engine/source-sdk-2013/src/game/client/Release/client.dll"++)++win_server_candidates=(++ "$SDK_HL2MP_WIN_BIN/server.dll"++ "$SDK_HL2MP_WIN_BIN/win32/server.dll"++ "$ROOT/engine/source-sdk-2013/src/game/server/Release_hl2mp/server.dll"++ "$ROOT/engine/source-sdk-2013/src/game/server/Release/server.dll"++)++++for src in "${win_client_candidates[@]}"; do++ if copy_if_exists "$src" "$MOD_BIN/client.dll"; then break; fi++done++for src in "${win_server_candidates[@]}"; do++ if copy_if_exists "$src" "$MOD_BIN/server.dll"; then break; fidone--echo "[openvibe] bin/linux64 compatibility links ready"++if [[ -f "$MOD_BIN/client.dll" ]]; then++ echo "[openvibe] Windows/Proton client.dll present"++else++ echo "[openvibe] Windows/Proton client.dll not present yet; build on Windows to enable Proton in-game client DLL commands"++fi++++if [[ -f "$MOD_BIN/server.dll" ]]; then++ echo "[openvibe] Windows server.dll present"++else++ echo "[openvibe] Windows server.dll not present yet"++fi
new file mode 100644
new file mode 100644
@@ -0,0 +1,57 @@
@@ -0,0 +1,57 @@
+# OpenVibe: Source multi-platform build matrix
+
+OpenVibe needs different client/server binaries for different Source runtimes.
+
+| Runtime | Executable | Module loaded by the engine | Output path |
+| --- | --- | --- | --- |
+| Linux native client | `hl2_linux` | Linux ELF client module | `game/openvibe.games/bin/linux64/client.so` |
+| Linux dedicated server | `srcds_linux` | Linux ELF server module | `game/openvibe.games/bin/linux64/server.so` |
+| Windows native client | `hl2.exe` | Windows PE client DLL | `game/openvibe.games/bin/client.dll` |
+| Proton client | Windows `hl2.exe` under Proton | Windows PE client DLL | `game/openvibe.games/bin/client.dll` |
+| Windows dedicated server | `srcds.exe` | Windows PE server DLL | `game/openvibe.games/bin/server.dll` |
+
+Important: Proton does **not** load Linux `client.so`. It runs Windows `hl2.exe`, so it needs `client.dll`.
+If the in-game console says `Unknown command "ov_join"` or `Unknown command "ov_menu"`, the OpenVibe client module is not loaded for that runtime.
+
+## Linux build
+
+```bash
+cd ~/src/openvibe-source
+tools/build-sdk-linux.sh
+tools/setup-openvibe-bin.sh
+tools/check-openvibe-platform-binaries.sh
+```
+
+## Windows DLL build
+
+Run on Windows in a Visual Studio Developer PowerShell with C++ Build Tools installed:
+
+```powershell
+cd C:\path\to\openvibe-source
+powershell -ExecutionPolicy Bypass -File tools\build-sdk-windows.ps1
+```
+
+That script builds QuickJS as `libquickjs_openvibe.lib`, runs VPC/MSBuild, then copies DLLs into:
+
+```text
+game/openvibe.games/bin/client.dll
+game/openvibe.games/bin/server.dll
+```
+
+Copy those DLLs back to the Linux dev checkout if you build on a separate Windows VM. Proton will then load them.
+
+## Runtime selection
+
+```bash
+# Prefer native Linux if hl2_linux + client.so exist, otherwise use Proton if client.dll exists.
+tools/run-client-auto.sh 127.0.0.1 27015
+
+# Force native Linux.
+OPENVIBE_CLIENT_MODE=linux tools/run-client-auto.sh 127.0.0.1 27015
+
+# Force Proton; requires client.dll.
+OPENVIBE_CLIENT_MODE=proton tools/run-client-auto.sh 127.0.0.1 27015
+
+# Launch Proton even without client.dll, but in-game client commands/menu will not exist.
+OPENVIBE_CLIENT_MODE=proton-fallback tools/run-client-auto.sh 127.0.0.1 27015
+```
@@ -247,37 +247,33 @@ async function ensureClientUiServer(root) {
@@ -247,37 +247,33 @@ async function ensureClientUiServer(root) {
// ── Game launch ────────────────────────────────────────────────────────────────
// ── Game launch ────────────────────────────────────────────────────────────────
async function launchGame(serverIp, serverPort) {
async function launchGame(serverIp, serverPort) {
const root = path.resolve(__dirname, '..');
const root = path.resolve(__dirname, '..');
- const mod = path.join(root, 'game/openvibe.games');
-
- // Prefer the Proton-based launcher (Windows hl2.exe via GE-Proton)
- const protonScript = path.join(root, 'tools/run-client-proton.sh');
- const steamRoot = process.env.HOME + '/.steam/steam';
- const hl2Exe = '/mnt/data-f/SteamLibrary/steamapps/common/Source SDK Base 2013 Multiplayer/hl2.exe';
-
- const hasProton = fs.existsSync(path.join(steamRoot, 'compatibilitytools.d/GE-Proton10-34/proton'));
- const hasHl2 = fs.existsSync(hl2Exe);
-
- if (!hasProton || !hasHl2) {
- dialog.showErrorBox('Game Not Found',
- !hasHl2
- ? `hl2.exe not found at:\n${hl2Exe}`
- : 'GE-Proton10-34 not found.\nInstall it via ProtonUp-Qt or manually.');
- return false;
- }
const uiReady = await ensureClientUiServer(root);
const uiReady = await ensureClientUiServer(root);
if (!uiReady) {
if (!uiReady) {
dialog.showErrorBox('OpenVibe Menu Server Failed',
dialog.showErrorBox('OpenVibe Menu Server Failed',
- `Could not start http://127.0.0.1:${CLIENT_UI_PORT}/client for the in-game HTML menu.`);
+ `Could not start http://127.0.0.1:${CLIENT_UI_PORT}/client for the launcher/in-game HTML menu.`);
return false;
return false;
}
}
- // Build arguments to pass through the shell script
const scriptArgs = serverIp && serverPort ? [serverIp, String(serverPort)] : [];
const scriptArgs = serverIp && serverPort ? [serverIp, String(serverPort)] : [];
+ const isWin = process.platform === 'win32';
+ const launcherScript = isWin
+ ? path.join(root, 'tools', 'run-client-windows.ps1')
+ : path.join(root, 'tools', 'run-client-auto.sh');
+
+ if (!fs.existsSync(launcherScript)) {
+ dialog.showErrorBox('OpenVibe Launcher Script Missing', `Could not find:\n${launcherScript}`);
+ return false;
+ }
+
+ const command = isWin ? 'powershell.exe' : 'bash';
+ const args = isWin
+ ? ['-ExecutionPolicy', 'Bypass', '-File', launcherScript, ...scriptArgs]
+ : [launcherScript, ...scriptArgs];
- console.log('[launcher] spawning via Proton:', protonScript, scriptArgs.join(' '));
+ console.log('[launcher] spawning client:', command, args.join(' '));
- gameProcess = spawn('bash', [protonScript, ...scriptArgs], {
+ gameProcess = spawn(command, args, {
cwd: root,
cwd: root,
detached: true,
detached: true,
env: {
env: {
@@ -297,49 +293,17 @@ async function launchGame(serverIp, serverPort) {
@@ -297,49 +293,17 @@ async function launchGame(serverIp, serverPort) {
mainWindow?.focus();
mainWindow?.focus();
});
});
- emitLaunchPhase(
- 'spawned',
- 'Source has started. OpenVibe will keep this launcher visible until the game window is detected and stable.'
- );
mainWindow?.webContents.send('game-started', gameProcess.pid);
mainWindow?.webContents.send('game-started', gameProcess.pid);
- waitForGameWindowReady().then((state) => {
- if (!gameProcess) return;
-
- if (state.ready) {
- emitLaunchPhase(
- 'ready',
- HIDE_LAUNCHER_ON_GAME_READY
- ? 'Source window looks ready. Focusing game and hiding launcher.'
- : 'Source window looks ready. Launcher will stay open so you are not stuck behind a frozen loading screen.',
- state
- );
- focusGameWindow();
-
- if (HIDE_LAUNCHER_ON_GAME_READY) {
- if (mainWindow && !mainWindow.isDestroyed()) mainWindow.hide();
- } else {
- if (mainWindow && !mainWindow.isDestroyed()) {
- mainWindow.show();
- mainWindow.focus();
- }
- }
- } else {
- emitLaunchPhase(
- 'slow',
- 'Source is taking longer than expected. The launcher is staying visible; use Focus Game when the window appears.',
- state
- );
- if (mainWindow && !mainWindow.isDestroyed()) {
- mainWindow.show();
- mainWindow.focus();
- }
- }
- });
+ // Keep Electron visible by default so users are not dumped behind a frozen Source loading window.
+ // Advanced: set OPENVIBE_HIDE_LAUNCHER_ON_GAME_READY=1 to hide after a conservative delay.
+ if (process.env.OPENVIBE_HIDE_LAUNCHER_ON_GAME_READY === '1') {
+ setTimeout(() => {
+ if (gameProcess && mainWindow && !mainWindow.isDestroyed()) mainWindow.hide();
+ }, Number(process.env.OPENVIBE_GAME_READY_HIDE_DELAY_MS || 12000));
+ }
- // Detach so the game keeps running if Electron is closed
gameProcess.unref();
gameProcess.unref();
-
return true;
return true;
}
}
new file mode 100755
new file mode 100755
@@ -0,0 +1,750 @@
@@ -0,0 +1,750 @@
+#!/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] adding multi-platform client/server binary support"
+echo "[openvibe] root=$ROOT"
+echo "[openvibe] sdk=$SDK"
+
+backup_file() {
+ local f="$1"
+ [[ -f "$f" ]] && cp "$f" "$f.bak.$STAMP"
+}
+
+mkdir -p tools docs game/openvibe.games/bin game/openvibe.games/bin/linux64
+
+# -----------------------------------------------------------------------------
+# 1) Make apply-openvibe-sdk.sh usable from Windows/Git Bash by allowing the
+# QuickJS C build step to be skipped. Windows builds create a .lib via MSVC.
+# -----------------------------------------------------------------------------
+if [[ -f tools/apply-openvibe-sdk.sh ]]; then
+ backup_file tools/apply-openvibe-sdk.sh
+ python3 <<'PY'
+from pathlib import Path
+p = Path('tools/apply-openvibe-sdk.sh')
+s = p.read_text()
+old = '"$ROOT/tools/build-quickjs-lib.sh"'
+new = '''if [[ "${OPENVIBE_SKIP_QJS_BUILD:-0}" != "1" ]]; then
+ "$ROOT/tools/build-quickjs-lib.sh"
+fi'''
+if old in s and 'OPENVIBE_SKIP_QJS_BUILD' not in s:
+ s = s.replace(old, new)
+p.write_text(s)
+PY
+fi
+
+# -----------------------------------------------------------------------------
+# 2) Diagnostic: exactly which binaries exist and which runtime can load them.
+# -----------------------------------------------------------------------------
+cat > tools/check-openvibe-platform-binaries.sh <<'CHECK'
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
+MOD="$ROOT/game/openvibe.games"
+BIN="$MOD/bin"
+LINUX_BIN="$BIN/linux64"
+
+find_hl2_linux() {
+ local candidates=(
+ "${OPENVIBE_HL2_LINUX:-}"
+ "$HOME/.steam/steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"
+ "$HOME/.local/share/Steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"
+ "/mnt/data-f/SteamLibrary/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"
+ "/mnt/6tb/ssd_offload/home/$USER/.steam/debian-installation/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"
+ )
+ for p in "${candidates[@]}"; do
+ [[ -n "$p" && -x "$p" ]] && { echo "$p"; return 0; }
+ done
+ return 1
+}
+
+find_hl2_exe() {
+ local candidates=(
+ "${OPENVIBE_HL2_EXE:-}"
+ "/mnt/data-f/SteamLibrary/steamapps/common/Source SDK Base 2013 Multiplayer/hl2.exe"
+ "$HOME/.steam/steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2.exe"
+ "$HOME/.local/share/Steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2.exe"
+ )
+ for p in "${candidates[@]}"; do
+ [[ -n "$p" && -f "$p" ]] && { echo "$p"; return 0; }
+ done
+ return 1
+}
+
+ok=0
+warn=0
+
+echo "[openvibe] platform binary matrix"
+echo "mod: $MOD"
+echo
+
+check_file() {
+ local label="$1" path="$2"
+ if [[ -f "$path" || -L "$path" ]]; then
+ echo "[ok] $label: $path"
+ else
+ echo "[miss] $label: $path"
+ warn=1
+ fi
+}
+
+check_file "Linux native client module" "$LINUX_BIN/client.so"
+check_file "Linux native server module" "$LINUX_BIN/server.so"
+check_file "Windows/Proton client module" "$BIN/client.dll"
+check_file "Windows/Proton server module" "$BIN/server.dll"
+
+echo
+if hl2_linux="$(find_hl2_linux 2>/dev/null)"; then
+ echo "[ok] Native Linux client executable: $hl2_linux"
+else
+ echo "[miss] Native Linux client executable: set OPENVIBE_HL2_LINUX or install Source SDK Base 2013 MP Linux"
+ warn=1
+fi
+
+if hl2_exe="$(find_hl2_exe 2>/dev/null)"; then
+ echo "[ok] Windows/Proton client executable: $hl2_exe"
+else
+ echo "[miss] Windows hl2.exe for Proton/Windows client"
+ warn=1
+fi
+
+echo
+cat <<TEXT
+Runtime rules:
+ Linux native client loads: game/openvibe.games/bin/linux64/client.so
+ Linux native server loads: game/openvibe.games/bin/linux64/server.so
+ Windows native client loads: game/openvibe.games/bin/client.dll
+ Proton client loads: game/openvibe.games/bin/client.dll
+
+If Proton/Windows says unknown command ov_join or ov_menu, client.dll is missing or failed to load.
+Linux client.so cannot be loaded by Windows hl2.exe under Proton.
+TEXT
+
+exit 0
+CHECK
+chmod +x tools/check-openvibe-platform-binaries.sh
+
+# -----------------------------------------------------------------------------
+# 3) Setup script: keep Linux symlinks and also install/carry Windows DLLs when
+# they have been produced by a Windows build.
+# -----------------------------------------------------------------------------
+backup_file tools/setup-openvibe-bin.sh
+cat > tools/setup-openvibe-bin.sh <<'SETUP'
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
+TF2_SRCDS="${OPENVIBE_SRCDS:-$HOME/srcds/tf2}"
+MOD_BIN="$ROOT/game/openvibe.games/bin"
+MOD_LINUX_BIN="$MOD_BIN/linux64"
+SDK_HL2MP_LINUX_BIN="$ROOT/engine/source-sdk-2013/game/mod_hl2mp/bin/linux64"
+SDK_LIB_BIN="$ROOT/engine/source-sdk-2013/src/lib/public/linux64"
+TF2_BIN="$TF2_SRCDS/bin/linux64"
+SDK_HL2MP_WIN_BIN="$ROOT/engine/source-sdk-2013/game/mod_hl2mp/bin"
+
+mkdir -p "$MOD_LINUX_BIN" "$MOD_BIN"
+
+require_file() {
+ if [[ ! -f "$1" ]]; then
+ echo "Missing required file: $1" >&2
+ exit 1
+ fi
+}
+
+link_if_exists() {
+ local src="$1" dst="$2"
+ if [[ -f "$src" ]]; then
+ ln -sfn "$src" "$dst"
+ echo "[openvibe] linked ${dst#$ROOT/} -> $src"
+ fi
+}
+
+copy_if_exists() {
+ local src="$1" dst="$2"
+ if [[ -f "$src" ]]; then
+ install -D -m 0644 "$src" "$dst"
+ echo "[openvibe] copied ${dst#$ROOT/}"
+ return 0
+ fi
+ return 1
+}
+
+# Linux native / Linux dedicated server modules.
+require_file "$SDK_HL2MP_LINUX_BIN/client.so"
+require_file "$SDK_HL2MP_LINUX_BIN/server.so"
+require_file "$SDK_HL2MP_LINUX_BIN/game_shader_generic_example.so"
+require_file "$SDK_LIB_BIN/libtier0.so"
+require_file "$SDK_LIB_BIN/libvstdlib.so"
+require_file "$SDK_LIB_BIN/libsteam_api.so"
+
+ln -sfn "$SDK_HL2MP_LINUX_BIN/client.so" "$MOD_LINUX_BIN/client.so"
+ln -sfn client.so "$MOD_LINUX_BIN/client_srv.so"
+ln -sfn "$SDK_HL2MP_LINUX_BIN/server.so" "$MOD_LINUX_BIN/server.so"
+ln -sfn server.so "$MOD_LINUX_BIN/server_srv.so"
+ln -sfn "$SDK_HL2MP_LINUX_BIN/game_shader_generic_example.so" "$MOD_LINUX_BIN/game_shader_generic_example_srv.so"
+ln -sfn "$SDK_LIB_BIN/libtier0.so" "$MOD_LINUX_BIN/libtier0.so"
+ln -sfn "$SDK_LIB_BIN/libvstdlib.so" "$MOD_LINUX_BIN/libvstdlib.so"
+ln -sfn "$SDK_LIB_BIN/libsteam_api.so" "$MOD_LINUX_BIN/libsteam_api.so"
+
+if [[ -d "$TF2_BIN" ]]; then
+ for module in soundemittersystem scenefilecache datacache materialsystem studiorender vphysics vscript replay shaderapiempty; do
+ if [[ -f "$TF2_BIN/${module}_srv.so" ]]; then
+ ln -sfn "$TF2_BIN/${module}_srv.so" "$MOD_LINUX_BIN/${module}.so"
+ fi
+ done
+fi
+
+echo "[openvibe] linux bin/linux64 compatibility links ready"
+
+# Windows/Proton modules. These are optional on Linux until a Windows build has run.
+win_client_candidates=(
+ "$SDK_HL2MP_WIN_BIN/client.dll"
+ "$SDK_HL2MP_WIN_BIN/win32/client.dll"
+ "$ROOT/engine/source-sdk-2013/src/game/client/Release_hl2mp/client.dll"
+ "$ROOT/engine/source-sdk-2013/src/game/client/Release/client.dll"
+)
+win_server_candidates=(
+ "$SDK_HL2MP_WIN_BIN/server.dll"
+ "$SDK_HL2MP_WIN_BIN/win32/server.dll"
+ "$ROOT/engine/source-sdk-2013/src/game/server/Release_hl2mp/server.dll"
+ "$ROOT/engine/source-sdk-2013/src/game/server/Release/server.dll"
+)
+
+for src in "${win_client_candidates[@]}"; do
+ if copy_if_exists "$src" "$MOD_BIN/client.dll"; then break; fi
+done
+for src in "${win_server_candidates[@]}"; do
+ if copy_if_exists "$src" "$MOD_BIN/server.dll"; then break; fi
+done
+
+if [[ -f "$MOD_BIN/client.dll" ]]; then
+ echo "[openvibe] Windows/Proton client.dll present"
+else
+ echo "[openvibe] Windows/Proton client.dll not present yet; build on Windows to enable Proton in-game client DLL commands"
+fi
+
+if [[ -f "$MOD_BIN/server.dll" ]]; then
+ echo "[openvibe] Windows server.dll present"
+else
+ echo "[openvibe] Windows server.dll not present yet"
+fi
+SETUP
+chmod +x tools/setup-openvibe-bin.sh
+
+# -----------------------------------------------------------------------------
+# 4) Native Linux launcher.
+# -----------------------------------------------------------------------------
+cat > tools/run-client-linux.sh <<'LINUXRUN'
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
+GAME_DIR="$ROOT/game/openvibe.games"
+CLIENT_SO="$GAME_DIR/bin/linux64/client.so"
+
+find_hl2_linux() {
+ local candidates=(
+ "${OPENVIBE_HL2_LINUX:-}"
+ "$HOME/.steam/steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"
+ "$HOME/.local/share/Steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"
+ "/mnt/data-f/SteamLibrary/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"
+ "/mnt/6tb/ssd_offload/home/$USER/.steam/debian-installation/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"
+ )
+ for p in "${candidates[@]}"; do
+ [[ -n "$p" && -x "$p" ]] && { echo "$p"; return 0; }
+ done
+ return 1
+}
+
+if [[ ! -f "$CLIENT_SO" && ! -L "$CLIENT_SO" ]]; then
+ echo "ERROR: Linux client.so missing at $CLIENT_SO" >&2
+ echo "Run: tools/build-sdk-linux.sh && tools/setup-openvibe-bin.sh" >&2
+ exit 1
+fi
+
+HL2_LINUX="$(find_hl2_linux)" || {
+ echo "ERROR: could not find hl2_linux. Set OPENVIBE_HL2_LINUX=/path/to/hl2_linux" >&2
+ exit 1
+}
+
+CONNECT_ARGS=()
+if [[ "${1:-}" != "" && "${2:-}" != "" ]]; then
+ CONNECT_ARGS=(+connect "$1:$2")
+fi
+
+exec "$HL2_LINUX" \
+ -game "$GAME_DIR" \
+ -console -dev -novid -sw -w 1280 -h 720 \
+ -port 27115 -clientport 27105 \
+ +exec openvibe_proton_client.cfg \
+ "${CONNECT_ARGS[@]}"
+LINUXRUN
+chmod +x tools/run-client-linux.sh
+
+# -----------------------------------------------------------------------------
+# 5) Auto launcher: native Linux when available, Proton when Windows DLL exists,
+# Proton fallback if explicitly requested.
+# -----------------------------------------------------------------------------
+cat > tools/run-client-auto.sh <<'AUTORUN'
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
+MODE="${OPENVIBE_CLIENT_MODE:-auto}" # auto | linux | proton | proton-fallback
+GAME_DIR="$ROOT/game/openvibe.games"
+LINUX_SO="$GAME_DIR/bin/linux64/client.so"
+WIN_DLL="$GAME_DIR/bin/client.dll"
+
+has_linux_client() {
+ [[ -f "$LINUX_SO" || -L "$LINUX_SO" ]] && \
+ { [[ -n "${OPENVIBE_HL2_LINUX:-}" && -x "$OPENVIBE_HL2_LINUX" ]] || \
+ [[ -x "$HOME/.steam/steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux" ]] || \
+ [[ -x "$HOME/.local/share/Steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux" ]] || \
+ [[ -x "/mnt/data-f/SteamLibrary/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux" ]]; }
+}
+
+has_windows_client_dll() {
+ [[ -f "$WIN_DLL" ]]
+}
+
+case "$MODE" in
+ linux)
+ exec "$ROOT/tools/run-client-linux.sh" "${@:-}"
+ ;;
+ proton)
+ if ! has_windows_client_dll; then
+ echo "ERROR: Proton mode requires $WIN_DLL for custom in-game client DLL commands/menu." >&2
+ echo "Build Windows DLLs first, or use OPENVIBE_CLIENT_MODE=proton-fallback to launch without client DLL." >&2
+ exit 1
+ fi
+ exec "$ROOT/tools/run-client-proton.sh" "${@:-}"
+ ;;
+ proton-fallback)
+ echo "WARNING: Proton fallback may launch, but without game/openvibe.games/bin/client.dll the in-game ov_* client commands/menu will not exist." >&2
+ exec "$ROOT/tools/run-client-proton.sh" "${@:-}"
+ ;;
+ auto)
+ if has_linux_client; then
+ echo "[openvibe] auto client mode: native linux"
+ exec "$ROOT/tools/run-client-linux.sh" "${@:-}"
+ fi
+ if has_windows_client_dll; then
+ echo "[openvibe] auto client mode: proton with Windows client.dll"
+ exec "$ROOT/tools/run-client-proton.sh" "${@:-}"
+ fi
+ echo "[openvibe] auto client mode: proton fallback, but no Windows client.dll exists yet" >&2
+ exec "$ROOT/tools/run-client-proton.sh" "${@:-}"
+ ;;
+ *)
+ echo "ERROR: unknown OPENVIBE_CLIENT_MODE=$MODE. Use auto, linux, proton, or proton-fallback." >&2
+ exit 1
+ ;;
+esac
+AUTORUN
+chmod +x tools/run-client-auto.sh
+
+# -----------------------------------------------------------------------------
+# 6) Patch Proton launcher to be honest about DLL status and load fallback cfg.
+# -----------------------------------------------------------------------------
+if [[ -f tools/run-client-proton.sh ]]; then
+ backup_file tools/run-client-proton.sh
+ python3 <<'PY'
+from pathlib import Path
+p = Path('tools/run-client-proton.sh')
+s = p.read_text()
+if 'CLIENT_DLL="$GAME_DIR/bin/client.dll"' not in s:
+ s = s.replace('GAME_DIR="/home/workstation/src/openvibe-source/game/openvibe.games"', 'GAME_DIR="/home/workstation/src/openvibe-source/game/openvibe.games"\nCLIENT_DLL="$GAME_DIR/bin/client.dll"')
+if 'Proton Windows hl2.exe will not load Linux client.so' not in s:
+ marker = 'echo "Launching OpenVibe: Source..."\n'
+ s = s.replace(marker, marker + 'if [ ! -f "$CLIENT_DLL" ]; then\n echo "WARNING: $CLIENT_DLL is missing."\n echo "WARNING: Proton Windows hl2.exe will not load Linux client.so; in-game ov_* client commands/menu will be unavailable."\nfi\n')
+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 \\\n +exec openvibe_proton_client.cfg \\\n')
+p.write_text(s)
+PY
+fi
+
+# -----------------------------------------------------------------------------
+# 7) Windows QuickJS static lib builder.
+# -----------------------------------------------------------------------------
+cat > tools/build-quickjs-lib-windows.ps1 <<'PSQJS'
+$ErrorActionPreference = 'Stop'
+
+$Root = if ($env:OPENVIBE_ROOT) { $env:OPENVIBE_ROOT } else { Join-Path $HOME 'src/openvibe-source' }
+$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'
+
+if (!(Test-Path (Join-Path $SrcQjs 'quickjs.c'))) {
+ throw "Missing $SrcQjs/quickjs.c. Run tools/vendor-quickjs.sh first."
+}
+
+New-Item -ItemType Directory -Force -Path $SdkQjs, $Out | Out-Null
+Get-ChildItem $SrcQjs -File | ForEach-Object { Copy-Item $_.FullName (Join-Path $SdkQjs $_.Name) -Force }
+
+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."
+}
+
+$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
+
+$objects = @()
+foreach ($src in $sources) {
+ $obj = Join-Path $Out ([IO.Path]::GetFileNameWithoutExtension($src) + '.obj')
+ Write-Host "[openvibe-qjs-win] cl $src -> $obj"
+ & cl.exe /nologo /O2 /MT /W3 /D_CRT_SECURE_NO_WARNINGS /DWIN32 /D_WINDOWS /DCONFIG_VERSION='"openvibe"' /I"$SdkQjs" /c (Join-Path $SdkQjs $src) /Fo"$obj"
+ if ($LASTEXITCODE -ne 0) { throw "cl failed for $src" }
+ $objects += $obj
+}
+
+$outLib = Join-Path $Out 'libquickjs_openvibe.lib'
+& lib.exe /nologo /OUT:"$outLib" $objects
+if ($LASTEXITCODE -ne 0) { throw "lib.exe failed" }
+Write-Host "[openvibe-qjs-win] built $outLib"
+PSQJS
+
+# -----------------------------------------------------------------------------
+# 8) Windows native/Proton DLL build helper.
+# -----------------------------------------------------------------------------
+cat > tools/build-sdk-windows.ps1 <<'PSWIN'
+$ErrorActionPreference = 'Stop'
+
+$Root = if ($env:OPENVIBE_ROOT) { $env:OPENVIBE_ROOT } else { Join-Path $HOME 'src/openvibe-source' }
+$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root 'engine/source-sdk-2013' }
+
+Write-Host "[openvibe-win] root=$Root"
+Write-Host "[openvibe-win] sdk=$Sdk"
+
+if (!(Test-Path (Join-Path $Sdk 'src'))) { throw "Source SDK not found at $Sdk" }
+
+# Apply OpenVibe SDK copy/patches through Git Bash, but skip Linux QuickJS .a build.
+$bash = Get-Command bash.exe -ErrorAction SilentlyContinue
+if ($bash) {
+ $rootForBash = $Root
+ try {
+ $rootForBash = (& $bash.Source -lc "cygpath -u '$Root'" 2>$null).Trim()
+ } catch {}
+ Write-Host "[openvibe-win] applying SDK patch through bash at $rootForBash"
+ & $bash.Source -lc "cd '$rootForBash' && OPENVIBE_SKIP_QJS_BUILD=1 ./tools/apply-openvibe-sdk.sh"
+ if ($LASTEXITCODE -ne 0) { throw "apply-openvibe-sdk.sh failed" }
+} else {
+ Write-Warning "bash.exe not found. Skipping apply-openvibe-sdk.sh. Make sure SDK files are already applied."
+}
+
+& (Join-Path $Root 'tools/build-quickjs-lib-windows.ps1')
+
+Push-Location (Join-Path $Sdk 'src')
+try {
+ $vpc = @(
+ (Join-Path $Sdk 'src/devtools/bin/vpc.exe'),
+ (Join-Path $Sdk 'devtools/bin/vpc.exe')
+ ) | Where-Object { Test-Path $_ } | Select-Object -First 1
+
+ if (Test-Path '.\createallprojects.bat') {
+ Write-Host "[openvibe-win] running createallprojects.bat"
+ & cmd.exe /c createallprojects.bat
+ } elseif ($vpc) {
+ Write-Host "[openvibe-win] running VPC: $vpc"
+ $ok = $false
+ $attempts = @(
+ @('/2013','/hl2mp','/game','/mksln','games.sln'),
+ @('/hl2mp','/game','/mksln','games.sln'),
+ @('+game','/mksln','games.sln')
+ )
+ foreach ($args in $attempts) {
+ & $vpc @args
+ if ($LASTEXITCODE -eq 0) { $ok = $true; break }
+ }
+ if (!$ok) { throw "VPC solution generation failed" }
+ } else {
+ throw "Could not find createallprojects.bat or vpc.exe"
+ }
+
+ $sln = Get-ChildItem -Path . -Recurse -Filter *.sln | Where-Object { $_.Name -match 'game|hl2mp|everything|sdk' } | Select-Object -First 1
+ if (!$sln) { $sln = Get-ChildItem -Path . -Recurse -Filter *.sln | Select-Object -First 1 }
+ if (!$sln) { throw "No Visual Studio solution generated" }
+
+ $msbuild = $null
+ $vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/Installer/vswhere.exe'
+ if (Test-Path $vswhere) {
+ $msbuild = (& $vswhere -latest -products * -requires Microsoft.Component.MSBuild -find 'MSBuild\**\Bin\MSBuild.exe' | Select-Object -First 1)
+ }
+ if (!$msbuild) { $msbuild = (Get-Command msbuild.exe -ErrorAction SilentlyContinue).Source }
+ if (!$msbuild) { throw "MSBuild not found. Install Visual Studio Build Tools with C++ workload." }
+
+ Write-Host "[openvibe-win] building $($sln.FullName)"
+ $built = $false
+ foreach ($platform in @('Win32','x86')) {
+ foreach ($config in @('Release','Release_HL2MP','Release HL2MP')) {
+ Write-Host "[openvibe-win] trying Configuration=$config Platform=$platform"
+ & $msbuild $sln.FullName /m /p:Configuration=$config /p:Platform=$platform
+ if ($LASTEXITCODE -eq 0) { $built = $true; break }
+ }
+ if ($built) { break }
+ }
+ if (!$built) { throw "MSBuild failed for all known configurations" }
+}
+finally {
+ Pop-Location
+}
+
+$modBin = Join-Path $Root 'game/openvibe.games/bin'
+New-Item -ItemType Directory -Force -Path $modBin | Out-Null
+
+$client = Get-ChildItem $Sdk -Recurse -Filter client.dll -ErrorAction SilentlyContinue | Where-Object { $_.FullName -match 'mod_hl2mp|hl2mp|game\\client' } | Sort-Object FullName | Select-Object -First 1
+$server = Get-ChildItem $Sdk -Recurse -Filter server.dll -ErrorAction SilentlyContinue | Where-Object { $_.FullName -match 'mod_hl2mp|hl2mp|game\\server' } | Sort-Object FullName | Select-Object -First 1
+
+if ($client) { Copy-Item $client.FullName (Join-Path $modBin 'client.dll') -Force; Write-Host "[openvibe-win] copied client.dll from $($client.FullName)" } else { Write-Warning "client.dll not found after build" }
+if ($server) { Copy-Item $server.FullName (Join-Path $modBin 'server.dll') -Force; Write-Host "[openvibe-win] copied server.dll from $($server.FullName)" } else { Write-Warning "server.dll not found after build" }
+
+Write-Host "[openvibe-win] done. Proton/Windows client uses game/openvibe.games/bin/client.dll"
+PSWIN
+
+# -----------------------------------------------------------------------------
+# 9) Windows launcher helper.
+# -----------------------------------------------------------------------------
+cat > tools/run-client-windows.ps1 <<'PSRUN'
+$ErrorActionPreference = 'Stop'
+
+$Root = if ($env:OPENVIBE_ROOT) { $env:OPENVIBE_ROOT } else { Join-Path $HOME 'src/openvibe-source' }
+$GameDir = Join-Path $Root 'game/openvibe.games'
+$ClientDll = Join-Path $GameDir 'bin/client.dll'
+$Hl2Exe = if ($env:OPENVIBE_HL2_EXE) { $env:OPENVIBE_HL2_EXE } else { 'C:\Program Files (x86)\Steam\steamapps\common\Source SDK Base 2013 Multiplayer\hl2.exe' }
+
+if (!(Test-Path $Hl2Exe)) { throw "hl2.exe not found. Set OPENVIBE_HL2_EXE." }
+if (!(Test-Path $ClientDll)) { throw "client.dll missing at $ClientDll. Run tools/build-sdk-windows.ps1 first." }
+
+$args = @('-game', $GameDir, '-console', '-dev', '-novid', '-sw', '-w', '1280', '-h', '720', '+exec', 'openvibe_proton_client.cfg')
+if ($args.Count -ge 2 -and $args[0] -match '^\d+\.\d+\.\d+\.\d+$') {
+ # no-op; reserved for direct powershell invocation variants
+}
+
+if ($args.Count -ge 2) {}
+
+# Accept optional IP PORT positional args from after -File.
+$extra = $MyInvocation.UnboundArguments
+if ($extra.Count -ge 2) {
+ $args += @('+connect', "$($extra[0]):$($extra[1])")
+}
+
+Start-Process -FilePath $Hl2Exe -ArgumentList $args -WorkingDirectory (Split-Path $Hl2Exe)
+PSRUN
+
+# -----------------------------------------------------------------------------
+# 10) Local wrapper to build what can be built on this machine.
+# -----------------------------------------------------------------------------
+cat > tools/build-sdk-all-local.sh <<'ALLLOCAL'
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
+cd "$ROOT"
+
+if [[ "${RUN_LINUX_BUILD:-1}" == "1" ]]; then
+ echo "[openvibe] building Linux .so modules"
+ tools/build-sdk-linux.sh 2>&1 | tee "$HOME/ov-build-linux.log"
+ tools/setup-openvibe-bin.sh
+else
+ echo "[openvibe] RUN_LINUX_BUILD=0, skipping Linux build"
+fi
+
+echo
+if command -v powershell.exe >/dev/null 2>&1; then
+ echo "[openvibe] powershell.exe found. To build Windows DLLs, run from Windows Developer PowerShell:"
+else
+ echo "[openvibe] Windows DLL build requires Windows + Visual Studio Build Tools. Run:"
+fi
+cat <<TEXT
+ cd <repo>
+ powershell -ExecutionPolicy Bypass -File tools/build-sdk-windows.ps1
+TEXT
+
+tools/check-openvibe-platform-binaries.sh
+ALLLOCAL
+chmod +x tools/build-sdk-all-local.sh
+
+# -----------------------------------------------------------------------------
+# 11) Patch Electron main.js to call run-client-auto.sh / run-client-windows.ps1
+# rather than hardcoding Proton only.
+# -----------------------------------------------------------------------------
+if [[ -f launcher/main.js ]]; then
+ backup_file launcher/main.js
+ python3 <<'PY'
+from pathlib import Path
+p = Path('launcher/main.js')
+s = p.read_text()
+start = s.find('async function launchGame(')
+if start == -1:
+ raise SystemExit('Could not find launchGame in launcher/main.js')
+brace = s.find('{', start)
+if brace == -1:
+ raise SystemExit('Could not find launchGame body')
+depth = 0
+end = None
+for i in range(brace, len(s)):
+ if s[i] == '{': depth += 1
+ elif s[i] == '}':
+ depth -= 1
+ if depth == 0:
+ end = i + 1
+ break
+if end is None:
+ raise SystemExit('Could not parse launchGame body')
+new = r'''async function launchGame(serverIp, serverPort) {
+ const root = path.resolve(__dirname, '..');
+
+ const uiReady = await ensureClientUiServer(root);
+ if (!uiReady) {
+ dialog.showErrorBox('OpenVibe Menu Server Failed',
+ `Could not start http://127.0.0.1:${CLIENT_UI_PORT}/client for the launcher/in-game HTML menu.`);
+ return false;
+ }
+
+ const scriptArgs = serverIp && serverPort ? [serverIp, String(serverPort)] : [];
+ const isWin = process.platform === 'win32';
+ const launcherScript = isWin
+ ? path.join(root, 'tools', 'run-client-windows.ps1')
+ : path.join(root, 'tools', 'run-client-auto.sh');
+
+ if (!fs.existsSync(launcherScript)) {
+ dialog.showErrorBox('OpenVibe Launcher Script Missing', `Could not find:\n${launcherScript}`);
+ return false;
+ }
+
+ const command = isWin ? 'powershell.exe' : 'bash';
+ const args = isWin
+ ? ['-ExecutionPolicy', 'Bypass', '-File', launcherScript, ...scriptArgs]
+ : [launcherScript, ...scriptArgs];
+
+ console.log('[launcher] spawning client:', command, args.join(' '));
+
+ gameProcess = spawn(command, args, {
+ cwd: root,
+ detached: true,
+ env: {
+ ...process.env,
+ OPENVIBE_ROOT: root,
+ DISPLAY: process.env.DISPLAY || ':0',
+ },
+ });
+
+ gameProcess.stdout.on('data', (d) => console.log('[game]', d.toString().trim()));
+ gameProcess.stderr.on('data', (d) => console.error('[game]', d.toString().trim()));
+ gameProcess.on('exit', (code) => {
+ console.log('[launcher] game exited with code', code);
+ gameProcess = null;
+ mainWindow?.webContents.send('game-exited', code);
+ mainWindow?.show();
+ mainWindow?.focus();
+ });
+
+ mainWindow?.webContents.send('game-started', gameProcess.pid);
+
+ // Keep Electron visible by default so users are not dumped behind a frozen Source loading window.
+ // Advanced: set OPENVIBE_HIDE_LAUNCHER_ON_GAME_READY=1 to hide after a conservative delay.
+ if (process.env.OPENVIBE_HIDE_LAUNCHER_ON_GAME_READY === '1') {
+ setTimeout(() => {
+ if (gameProcess && mainWindow && !mainWindow.isDestroyed()) mainWindow.hide();
+ }, Number(process.env.OPENVIBE_GAME_READY_HIDE_DELAY_MS || 12000));
+ }
+
+ gameProcess.unref();
+ return true;
+}'''
+s = s[:start] + new + s[end:]
+p.write_text(s)
+PY
+fi
+
+# -----------------------------------------------------------------------------
+# 12) Docs for the repo.
+# -----------------------------------------------------------------------------
+cat > docs/MULTIPLATFORM_SOURCE_BUILDS.md <<'DOC'
+# OpenVibe: Source multi-platform build matrix
+
+OpenVibe needs different client/server binaries for different Source runtimes.
+
+| Runtime | Executable | Module loaded by the engine | Output path |
+| --- | --- | --- | --- |
+| Linux native client | `hl2_linux` | Linux ELF client module | `game/openvibe.games/bin/linux64/client.so` |
+| Linux dedicated server | `srcds_linux` | Linux ELF server module | `game/openvibe.games/bin/linux64/server.so` |
+| Windows native client | `hl2.exe` | Windows PE client DLL | `game/openvibe.games/bin/client.dll` |
+| Proton client | Windows `hl2.exe` under Proton | Windows PE client DLL | `game/openvibe.games/bin/client.dll` |
+| Windows dedicated server | `srcds.exe` | Windows PE server DLL | `game/openvibe.games/bin/server.dll` |
+
+Important: Proton does **not** load Linux `client.so`. It runs Windows `hl2.exe`, so it needs `client.dll`.
+If the in-game console says `Unknown command "ov_join"` or `Unknown command "ov_menu"`, the OpenVibe client module is not loaded for that runtime.
+
+## Linux build
+
+```bash
+cd ~/src/openvibe-source
+tools/build-sdk-linux.sh
+tools/setup-openvibe-bin.sh
+tools/check-openvibe-platform-binaries.sh
+```
+
+## Windows DLL build
+
+Run on Windows in a Visual Studio Developer PowerShell with C++ Build Tools installed:
+
+```powershell
+cd C:\path\to\openvibe-source
+powershell -ExecutionPolicy Bypass -File tools\build-sdk-windows.ps1
+```
+
+That script builds QuickJS as `libquickjs_openvibe.lib`, runs VPC/MSBuild, then copies DLLs into:
+
+```text
+game/openvibe.games/bin/client.dll
+game/openvibe.games/bin/server.dll
+```
+
+Copy those DLLs back to the Linux dev checkout if you build on a separate Windows VM. Proton will then load them.
+
+## Runtime selection
+
+```bash
+# Prefer native Linux if hl2_linux + client.so exist, otherwise use Proton if client.dll exists.
+tools/run-client-auto.sh 127.0.0.1 27015
+
+# Force native Linux.
+OPENVIBE_CLIENT_MODE=linux tools/run-client-auto.sh 127.0.0.1 27015
+
+# Force Proton; requires client.dll.
+OPENVIBE_CLIENT_MODE=proton tools/run-client-auto.sh 127.0.0.1 27015
+
+# Launch Proton even without client.dll, but in-game client commands/menu will not exist.
+OPENVIBE_CLIENT_MODE=proton-fallback tools/run-client-auto.sh 127.0.0.1 27015
+```
+DOC
+
+# Run setup and diagnostic, but do not force a long compile here.
+tools/setup-openvibe-bin.sh || true
+
+echo
+echo "[openvibe] generated multi-platform support files"
+echo
+tools/check-openvibe-platform-binaries.sh || true
+
+echo
+echo "Next optional local build:"
+echo " RUN_LINUX_BUILD=1 tools/build-sdk-all-local.sh"
+echo
+echo "Windows DLL build, from Windows Developer PowerShell:"
+echo " powershell -ExecutionPolicy Bypass -File tools/build-sdk-windows.ps1"
@@ -53,7 +53,9 @@ copy_file "$ROOT/sdk/openvibe/server/hl2mp/openvibe_js_server.cpp" \
@@ -53,7 +53,9 @@ copy_file "$ROOT/sdk/openvibe/server/hl2mp/openvibe_js_server.cpp" \
copy_tree "$ROOT/sdk/openvibe/third_party/quickjs" \
copy_tree "$ROOT/sdk/openvibe/third_party/quickjs" \
"$SDK/src/game/shared/openvibe/third_party/quickjs"
"$SDK/src/game/shared/openvibe/third_party/quickjs"
-"$ROOT/tools/build-quickjs-lib.sh"
+if [[ "${OPENVIBE_SKIP_QJS_BUILD:-0}" != "1" ]]; then
+ "$ROOT/tools/build-quickjs-lib.sh"
+fi
CLIENT_VPC="$SDK/src/game/client/client_hl2mp.vpc"
CLIENT_VPC="$SDK/src/game/client/client_hl2mp.vpc"
SERVER_VPC="$SDK/src/game/server/server_hl2mp.vpc"
SERVER_VPC="$SDK/src/game/server/server_hl2mp.vpc"
new file mode 100644
new file mode 100644
@@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
+$ErrorActionPreference = 'Stop'
+
+$Root = if ($env:OPENVIBE_ROOT) { $env:OPENVIBE_ROOT } else { Join-Path $HOME 'src/openvibe-source' }
+$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'
+
+if (!(Test-Path (Join-Path $SrcQjs 'quickjs.c'))) {
+ throw "Missing $SrcQjs/quickjs.c. Run tools/vendor-quickjs.sh first."
+}
+
+New-Item -ItemType Directory -Force -Path $SdkQjs, $Out | Out-Null
+Get-ChildItem $SrcQjs -File | ForEach-Object { Copy-Item $_.FullName (Join-Path $SdkQjs $_.Name) -Force }
+
+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."
+}
+
+$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
+
+$objects = @()
+foreach ($src in $sources) {
+ $obj = Join-Path $Out ([IO.Path]::GetFileNameWithoutExtension($src) + '.obj')
+ Write-Host "[openvibe-qjs-win] cl $src -> $obj"
+ & cl.exe /nologo /O2 /MT /W3 /D_CRT_SECURE_NO_WARNINGS /DWIN32 /D_WINDOWS /DCONFIG_VERSION='"openvibe"' /I"$SdkQjs" /c (Join-Path $SdkQjs $src) /Fo"$obj"
+ if ($LASTEXITCODE -ne 0) { throw "cl failed for $src" }
+ $objects += $obj
+}
+
+$outLib = Join-Path $Out 'libquickjs_openvibe.lib'
+& lib.exe /nologo /OUT:"$outLib" $objects
+if ($LASTEXITCODE -ne 0) { throw "lib.exe failed" }
+Write-Host "[openvibe-qjs-win] built $outLib"
new file mode 100755
new file mode 100755
@@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
+cd "$ROOT"
+
+if [[ "${RUN_LINUX_BUILD:-1}" == "1" ]]; then
+ echo "[openvibe] building Linux .so modules"
+ tools/build-sdk-linux.sh 2>&1 | tee "$HOME/ov-build-linux.log"
+ tools/setup-openvibe-bin.sh
+else
+ echo "[openvibe] RUN_LINUX_BUILD=0, skipping Linux build"
+fi
+
+echo
+if command -v powershell.exe >/dev/null 2>&1; then
+ echo "[openvibe] powershell.exe found. To build Windows DLLs, run from Windows Developer PowerShell:"
+else
+ echo "[openvibe] Windows DLL build requires Windows + Visual Studio Build Tools. Run:"
+fi
+cat <<TEXT
+ cd <repo>
+ powershell -ExecutionPolicy Bypass -File tools/build-sdk-windows.ps1
+TEXT
+
+tools/check-openvibe-platform-binaries.sh
new file mode 100644
new file mode 100644
@@ -0,0 +1,91 @@
@@ -0,0 +1,91 @@
+$ErrorActionPreference = 'Stop'
+
+$Root = if ($env:OPENVIBE_ROOT) { $env:OPENVIBE_ROOT } else { Join-Path $HOME 'src/openvibe-source' }
+$Sdk = if ($env:OPENVIBE_SDK) { $env:OPENVIBE_SDK } else { Join-Path $Root 'engine/source-sdk-2013' }
+
+Write-Host "[openvibe-win] root=$Root"
+Write-Host "[openvibe-win] sdk=$Sdk"
+
+if (!(Test-Path (Join-Path $Sdk 'src'))) { throw "Source SDK not found at $Sdk" }
+
+# Apply OpenVibe SDK copy/patches through Git Bash, but skip Linux QuickJS .a build.
+$bash = Get-Command bash.exe -ErrorAction SilentlyContinue
+if ($bash) {
+ $rootForBash = $Root
+ try {
+ $rootForBash = (& $bash.Source -lc "cygpath -u '$Root'" 2>$null).Trim()
+ } catch {}
+ Write-Host "[openvibe-win] applying SDK patch through bash at $rootForBash"
+ & $bash.Source -lc "cd '$rootForBash' && OPENVIBE_SKIP_QJS_BUILD=1 ./tools/apply-openvibe-sdk.sh"
+ if ($LASTEXITCODE -ne 0) { throw "apply-openvibe-sdk.sh failed" }
+} else {
+ Write-Warning "bash.exe not found. Skipping apply-openvibe-sdk.sh. Make sure SDK files are already applied."
+}
+
+& (Join-Path $Root 'tools/build-quickjs-lib-windows.ps1')
+
+Push-Location (Join-Path $Sdk 'src')
+try {
+ $vpc = @(
+ (Join-Path $Sdk 'src/devtools/bin/vpc.exe'),
+ (Join-Path $Sdk 'devtools/bin/vpc.exe')
+ ) | Where-Object { Test-Path $_ } | Select-Object -First 1
+
+ if (Test-Path '.\createallprojects.bat') {
+ Write-Host "[openvibe-win] running createallprojects.bat"
+ & cmd.exe /c createallprojects.bat
+ } elseif ($vpc) {
+ Write-Host "[openvibe-win] running VPC: $vpc"
+ $ok = $false
+ $attempts = @(
+ @('/2013','/hl2mp','/game','/mksln','games.sln'),
+ @('/hl2mp','/game','/mksln','games.sln'),
+ @('+game','/mksln','games.sln')
+ )
+ foreach ($args in $attempts) {
+ & $vpc @args
+ if ($LASTEXITCODE -eq 0) { $ok = $true; break }
+ }
+ if (!$ok) { throw "VPC solution generation failed" }
+ } else {
+ throw "Could not find createallprojects.bat or vpc.exe"
+ }
+
+ $sln = Get-ChildItem -Path . -Recurse -Filter *.sln | Where-Object { $_.Name -match 'game|hl2mp|everything|sdk' } | Select-Object -First 1
+ if (!$sln) { $sln = Get-ChildItem -Path . -Recurse -Filter *.sln | Select-Object -First 1 }
+ if (!$sln) { throw "No Visual Studio solution generated" }
+
+ $msbuild = $null
+ $vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/Installer/vswhere.exe'
+ if (Test-Path $vswhere) {
+ $msbuild = (& $vswhere -latest -products * -requires Microsoft.Component.MSBuild -find 'MSBuild\**\Bin\MSBuild.exe' | Select-Object -First 1)
+ }
+ if (!$msbuild) { $msbuild = (Get-Command msbuild.exe -ErrorAction SilentlyContinue).Source }
+ if (!$msbuild) { throw "MSBuild not found. Install Visual Studio Build Tools with C++ workload." }
+
+ Write-Host "[openvibe-win] building $($sln.FullName)"
+ $built = $false
+ foreach ($platform in @('Win32','x86')) {
+ foreach ($config in @('Release','Release_HL2MP','Release HL2MP')) {
+ Write-Host "[openvibe-win] trying Configuration=$config Platform=$platform"
+ & $msbuild $sln.FullName /m /p:Configuration=$config /p:Platform=$platform
+ if ($LASTEXITCODE -eq 0) { $built = $true; break }
+ }
+ if ($built) { break }
+ }
+ if (!$built) { throw "MSBuild failed for all known configurations" }
+}
+finally {
+ Pop-Location
+}
+
+$modBin = Join-Path $Root 'game/openvibe.games/bin'
+New-Item -ItemType Directory -Force -Path $modBin | Out-Null
+
+$client = Get-ChildItem $Sdk -Recurse -Filter client.dll -ErrorAction SilentlyContinue | Where-Object { $_.FullName -match 'mod_hl2mp|hl2mp|game\\client' } | Sort-Object FullName | Select-Object -First 1
+$server = Get-ChildItem $Sdk -Recurse -Filter server.dll -ErrorAction SilentlyContinue | Where-Object { $_.FullName -match 'mod_hl2mp|hl2mp|game\\server' } | Sort-Object FullName | Select-Object -First 1
+
+if ($client) { Copy-Item $client.FullName (Join-Path $modBin 'client.dll') -Force; Write-Host "[openvibe-win] copied client.dll from $($client.FullName)" } else { Write-Warning "client.dll not found after build" }
+if ($server) { Copy-Item $server.FullName (Join-Path $modBin 'server.dll') -Force; Write-Host "[openvibe-win] copied server.dll from $($server.FullName)" } else { Write-Warning "server.dll not found after build" }
+
+Write-Host "[openvibe-win] done. Proton/Windows client uses game/openvibe.games/bin/client.dll"
new file mode 100755
new file mode 100755
@@ -0,0 +1,307 @@
@@ -0,0 +1,307 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
+cd "$ROOT"
+
+echo "[openvibe] adding Windows DLL build-from-Linux workflow helpers"
+echo "[openvibe] root=$ROOT"
+
+mkdir -p .github/workflows tools docs .tmp
+
+cat > .github/workflows/windows-source-sdk-dlls.yml <<'YAML'
+name: Build Windows Source DLLs
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - codex/openvibe-next-steps
+ paths:
+ - 'sdk/openvibe/**'
+ - 'game/openvibe.games/**'
+ - 'tools/build-sdk-windows.ps1'
+ - 'tools/build-quickjs-lib-windows.ps1'
+ - '.github/workflows/windows-source-sdk-dlls.yml'
+
+jobs:
+ windows-dlls:
+ name: Windows client.dll/server.dll
+ runs-on: windows-2022
+ timeout-minutes: 90
+
+ steps:
+ - name: Checkout OpenVibe
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 1
+
+ - name: Configure git line endings
+ shell: pwsh
+ run: |
+ git config --global core.autocrlf false
+
+ - name: Setup MSBuild
+ uses: microsoft/setup-msbuild@v2
+
+ - name: Show toolchain
+ shell: pwsh
+ run: |
+ where msbuild
+ where cl
+ git --version
+ pwsh --version
+
+ - name: Build Windows Source SDK DLLs
+ shell: pwsh
+ run: |
+ powershell -NoProfile -ExecutionPolicy Bypass -File tools\build-sdk-windows.ps1
+
+ - name: Stage artifacts
+ shell: pwsh
+ run: |
+ New-Item -Force -ItemType Directory artifact\game\openvibe.games\bin | Out-Null
+ New-Item -Force -ItemType Directory artifact\logs | Out-Null
+
+ $candidates = @(
+ "game\openvibe.games\bin\client.dll",
+ "game\openvibe.games\bin\server.dll",
+ "engine\source-sdk-2013\game\mod_hl2mp\bin\client.dll",
+ "engine\source-sdk-2013\game\mod_hl2mp\bin\server.dll",
+ "engine\source-sdk-2013\game\mod_hl2mp\bin\client.pdb",
+ "engine\source-sdk-2013\game\mod_hl2mp\bin\server.pdb"
+ )
+
+ foreach ($file in $candidates) {
+ if (Test-Path $file) {
+ Copy-Item $file artifact\game\openvibe.games\bin\ -Force
+ }
+ }
+
+ if (Test-Path "windows-build.log") {
+ Copy-Item windows-build.log artifact\logs\windows-build.log -Force
+ }
+
+ Get-ChildItem artifact -Recurse | Format-Table FullName,Length
+
+ - name: Upload Windows DLL artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: openvibe-windows-dlls
+ path: artifact/**
+ if-no-files-found: error
+YAML
+
+cat > tools/request-windows-dll-build.sh <<'REQ'
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
+cd "$ROOT"
+
+WORKFLOW="${OPENVIBE_WINDOWS_WORKFLOW:-windows-source-sdk-dlls.yml}"
+ARTIFACT="${OPENVIBE_WINDOWS_ARTIFACT:-openvibe-windows-dlls}"
+BRANCH="${OPENVIBE_BRANCH:-$(git rev-parse --abbrev-ref HEAD)}"
+OUT_DIR="${OPENVIBE_WINDOWS_ARTIFACT_DIR:-$ROOT/.tmp/windows-dll-artifact}"
+
+if ! command -v gh >/dev/null 2>&1; then
+ echo "Missing GitHub CLI: gh" >&2
+ echo "Install it, then run: 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
+
+if [[ -n "$(git status --porcelain)" ]]; then
+ echo "[openvibe] WARNING: working tree has uncommitted changes."
+ echo "[openvibe] GitHub Actions builds the pushed branch, not your uncommitted local files."
+ echo
+ git status --short
+ echo
+ read -r -p "Continue anyway? [y/N] " answer
+ [[ "$answer" =~ ^[Yy]$ ]] || exit 1
+fi
+
+echo "[openvibe] triggering GitHub Actions Windows DLL build"
+echo "[openvibe] workflow=$WORKFLOW branch=$BRANCH"
+
+gh workflow run "$WORKFLOW" --ref "$BRANCH"
+
+echo "[openvibe] waiting for run to appear..."
+RUN_ID=""
+for _ in {1..30}; do
+ RUN_ID="$(gh run list --workflow "$WORKFLOW" --branch "$BRANCH" --limit 1 --json databaseId,status,event -q '.[0].databaseId // empty' 2>/dev/null || true)"
+ [[ -n "$RUN_ID" ]] && break
+ sleep 2
+done
+
+if [[ -z "$RUN_ID" ]]; then
+ echo "Could not find the workflow run. Check GitHub Actions UI." >&2
+ exit 1
+fi
+
+echo "[openvibe] watching run $RUN_ID"
+gh run watch "$RUN_ID" --exit-status
+
+rm -rf "$OUT_DIR"
+mkdir -p "$OUT_DIR"
+
+echo "[openvibe] downloading artifact $ARTIFACT"
+gh run download "$RUN_ID" --name "$ARTIFACT" --dir "$OUT_DIR"
+
+echo "[openvibe] downloaded files:"
+find "$OUT_DIR" -type f -maxdepth 8 -print | sort
+
+echo
+echo "[openvibe] installing Windows DLLs into game/openvibe.games/bin"
+mkdir -p "$ROOT/game/openvibe.games/bin"
+
+find "$OUT_DIR" -type f \( -iname 'client.dll' -o -iname 'server.dll' -o -iname 'client.pdb' -o -iname 'server.pdb' \) -print0 |
+while IFS= read -r -d '' file; do
+ cp -f "$file" "$ROOT/game/openvibe.games/bin/$(basename "$file")"
+ echo " installed $(basename "$file")"
+done
+
+echo
+"$ROOT/tools/check-openvibe-platform-binaries.sh" || true
+
+echo
+echo "[openvibe] done."
+echo "Proton/Windows can use:"
+echo " game/openvibe.games/bin/client.dll"
+echo " game/openvibe.games/bin/server.dll"
+REQ
+chmod +x tools/request-windows-dll-build.sh
+
+cat > tools/check-openvibe-platform-binaries.sh <<'CHECK'
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
+cd "$ROOT"
+
+linux_bin="$ROOT/game/openvibe.games/bin/linux64"
+win_bin="$ROOT/game/openvibe.games/bin"
+
+echo "[openvibe] platform binary check"
+echo
+
+check_file() {
+ local label="$1"
+ local file="$2"
+ if [[ -f "$file" || -L "$file" ]]; then
+ echo "[ok] $label: ${file#$ROOT/}"
+ file "$file" 2>/dev/null | sed 's/^/ /' || true
+ else
+ echo "[missing] $label: ${file#$ROOT/}"
+ fi
+}
+
+check_file "Linux client" "$linux_bin/client.so"
+check_file "Linux server" "$linux_bin/server.so"
+check_file "Windows client" "$win_bin/client.dll"
+check_file "Windows server" "$win_bin/server.dll"
+
+echo
+if [[ -f "$win_bin/client.dll" ]]; then
+ echo "[openvibe] Proton Windows hl2.exe should be able to load the OpenVibe client DLL."
+else
+ echo "[openvibe] Proton Windows hl2.exe will NOT have in-game ov_* client commands until client.dll exists."
+fi
+CHECK
+chmod +x tools/check-openvibe-platform-binaries.sh
+
+cat > tools/probe-local-windows-build-on-linux.sh <<'PROBE'
+#!/usr/bin/env bash
+set -euo pipefail
+
+echo "[openvibe] local Windows build-on-Linux probe"
+echo
+echo "Pure MinGW cross-compile is not the recommended route for Source SDK 2013 client.dll."
+echo "The Windows Source client DLL needs the MSVC ABI/toolchain and Valve's Windows project layout."
+echo
+echo "What this host has:"
+
+for tool in wine wine64 cl link msbuild x86_64-w64-mingw32-g++ gh; do
+ if command -v "$tool" >/dev/null 2>&1; then
+ echo "[ok] $tool -> $(command -v "$tool")"
+ else
+ echo "[missing] $tool"
+ fi
+done
+
+echo
+echo "Recommended from Linux:"
+echo " 1. Commit/push your branch."
+echo " 2. Run: tools/request-windows-dll-build.sh"
+echo " 3. Let GitHub Actions Windows runner build client.dll/server.dll with MSVC."
+echo
+echo "Experimental local path:"
+echo " - Windows VM on Linux, or"
+echo " - Wine with Visual Studio Build Tools installed, then run tools/build-sdk-windows.ps1 under that environment."
+echo "This is brittle; use the GitHub Actions/Windows runner first."
+PROBE
+chmod +x tools/probe-local-windows-build-on-linux.sh
+
+cat > docs/BUILD_WINDOWS_DLLS_FROM_LINUX.md <<'DOC'
+# Building Windows `client.dll` / `server.dll` from Linux
+
+OpenVibe supports two binary families:
+
+| Runtime | Client binary | Server binary | Notes |
+| --- | --- | --- | --- |
+| Native Linux Source | `game/openvibe.games/bin/linux64/client.so` | `game/openvibe.games/bin/linux64/server.so` | Built by the Linux SDK build. |
+| Windows / Proton Source | `game/openvibe.games/bin/client.dll` | `game/openvibe.games/bin/server.dll` | Must be built with a Windows/MSVC-compatible toolchain. |
+
+## Can Windows DLLs be built on Linux?
+
+Operationally, yes: trigger a Windows build from Linux using GitHub Actions:
+
+```bash
+git push
+tools/request-windows-dll-build.sh
+```
+
+That runs the build on a Windows runner, downloads the `openvibe-windows-dlls` artifact, and installs `client.dll` / `server.dll` into `game/openvibe.games/bin`.
+
+## Why not just MinGW?
+
+Source SDK 2013's Windows client/server DLLs are built around the MSVC ABI, Valve's VPC-generated Visual Studio projects, and Windows import/static libraries. A MinGW cross-compile from Linux is theoretically possible only after significant porting and import-library work, but it is not the practical path.
+
+## Reliable options
+
+1. **GitHub Actions Windows runner**: easiest from a Linux workstation.
+2. **Windows VM on Linux**: build with Visual Studio Build Tools/MSBuild, copy DLLs back.
+3. **Wine + Visual Studio Build Tools**: possible but brittle; treat as experimental.
+
+## Test
+
+```bash
+tools/check-openvibe-platform-binaries.sh
+OPENVIBE_CLIENT_MODE=proton tools/run-client-auto.sh 127.0.0.1 27015
+```
+
+If `client.dll` is present and correct, Proton's Windows `hl2.exe` should load OpenVibe client commands like `ov_join` and `ov_menu`.
+DOC
+
+# Make git keep workflows/docs/scripts.
+git add .github/workflows/windows-source-sdk-dlls.yml \
+ tools/request-windows-dll-build.sh \
+ tools/check-openvibe-platform-binaries.sh \
+ tools/probe-local-windows-build-on-linux.sh \
+ docs/BUILD_WINDOWS_DLLS_FROM_LINUX.md
+
+echo
+echo "[openvibe] added Windows DLL build-from-Linux support."
+echo
+echo "Next:"
+echo " git status"
+echo " git commit -m \"Add Windows DLL build workflow helpers\""
+echo " git push"
+echo " tools/request-windows-dll-build.sh"
+echo
+echo "Probe this Linux host with:"
+echo " tools/probe-local-windows-build-on-linux.sh"
new file mode 100755
new file mode 100755
@@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
+MODE="${OPENVIBE_CLIENT_MODE:-auto}" # auto | linux | proton | proton-fallback
+GAME_DIR="$ROOT/game/openvibe.games"
+LINUX_SO="$GAME_DIR/bin/linux64/client.so"
+WIN_DLL="$GAME_DIR/bin/client.dll"
+
+has_linux_client() {
+ [[ -f "$LINUX_SO" || -L "$LINUX_SO" ]] && \
+ { [[ -n "${OPENVIBE_HL2_LINUX:-}" && -x "$OPENVIBE_HL2_LINUX" ]] || \
+ [[ -x "$HOME/.steam/steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux" ]] || \
+ [[ -x "$HOME/.local/share/Steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux" ]] || \
+ [[ -x "/mnt/data-f/SteamLibrary/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux" ]]; }
+}
+
+has_windows_client_dll() {
+ [[ -f "$WIN_DLL" ]]
+}
+
+case "$MODE" in
+ linux)
+ exec "$ROOT/tools/run-client-linux.sh" "${@:-}"
+ ;;
+ proton)
+ if ! has_windows_client_dll; then
+ echo "ERROR: Proton mode requires $WIN_DLL for custom in-game client DLL commands/menu." >&2
+ echo "Build Windows DLLs first, or use OPENVIBE_CLIENT_MODE=proton-fallback to launch without client DLL." >&2
+ exit 1
+ fi
+ exec "$ROOT/tools/run-client-proton.sh" "${@:-}"
+ ;;
+ proton-fallback)
+ echo "WARNING: Proton fallback may launch, but without game/openvibe.games/bin/client.dll the in-game ov_* client commands/menu will not exist." >&2
+ exec "$ROOT/tools/run-client-proton.sh" "${@:-}"
+ ;;
+ auto)
+ if has_linux_client; then
+ echo "[openvibe] auto client mode: native linux"
+ exec "$ROOT/tools/run-client-linux.sh" "${@:-}"
+ fi
+ if has_windows_client_dll; then
+ echo "[openvibe] auto client mode: proton with Windows client.dll"
+ exec "$ROOT/tools/run-client-proton.sh" "${@:-}"
+ fi
+ echo "[openvibe] auto client mode: proton fallback, but no Windows client.dll exists yet" >&2
+ exec "$ROOT/tools/run-client-proton.sh" "${@:-}"
+ ;;
+ *)
+ echo "ERROR: unknown OPENVIBE_CLIENT_MODE=$MODE. Use auto, linux, proton, or proton-fallback." >&2
+ exit 1
+ ;;
+esac
new file mode 100755
new file mode 100755
@@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
+GAME_DIR="$ROOT/game/openvibe.games"
+CLIENT_SO="$GAME_DIR/bin/linux64/client.so"
+
+find_hl2_linux() {
+ local candidates=(
+ "${OPENVIBE_HL2_LINUX:-}"
+ "$HOME/.steam/steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"
+ "$HOME/.local/share/Steam/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"
+ "/mnt/data-f/SteamLibrary/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"
+ "/mnt/6tb/ssd_offload/home/$USER/.steam/debian-installation/steamapps/common/Source SDK Base 2013 Multiplayer/hl2_linux"
+ )
+ for p in "${candidates[@]}"; do
+ [[ -n "$p" && -x "$p" ]] && { echo "$p"; return 0; }
+ done
+ return 1
+}
+
+if [[ ! -f "$CLIENT_SO" && ! -L "$CLIENT_SO" ]]; then
+ echo "ERROR: Linux client.so missing at $CLIENT_SO" >&2
+ echo "Run: tools/build-sdk-linux.sh && tools/setup-openvibe-bin.sh" >&2
+ exit 1
+fi
+
+HL2_LINUX="$(find_hl2_linux)" || {
+ echo "ERROR: could not find hl2_linux. Set OPENVIBE_HL2_LINUX=/path/to/hl2_linux" >&2
+ exit 1
+}
+
+CONNECT_ARGS=()
+if [[ "${1:-}" != "" && "${2:-}" != "" ]]; then
+ CONNECT_ARGS=(+connect "$1:$2")
+fi
+
+exec "$HL2_LINUX" \
+ -game "$GAME_DIR" \
+ -console -dev -novid -sw -w 1280 -h 720 \
+ -port 27115 -clientport 27105 \
+ +exec openvibe_proton_client.cfg \
+ "${CONNECT_ARGS[@]}"
@@ -9,6 +9,7 @@ GE_PROTON="$STEAM_PATH/compatibilitytools.d/GE-Proton10-34"
@@ -9,6 +9,7 @@ GE_PROTON="$STEAM_PATH/compatibilitytools.d/GE-Proton10-34"
STEAM_COMPAT_DATA="$STEAM_PATH/steamapps/compatdata/243750"
STEAM_COMPAT_DATA="$STEAM_PATH/steamapps/compatdata/243750"
HL2_EXE="/mnt/data-f/SteamLibrary/steamapps/common/Source SDK Base 2013 Multiplayer/hl2.exe"
HL2_EXE="/mnt/data-f/SteamLibrary/steamapps/common/Source SDK Base 2013 Multiplayer/hl2.exe"
GAME_DIR="/home/workstation/src/openvibe-source/game/openvibe.games"
GAME_DIR="/home/workstation/src/openvibe-source/game/openvibe.games"
+CLIENT_DLL="$GAME_DIR/bin/client.dll"
# Check dependencies
# Check dependencies
if [ ! -f "$HL2_EXE" ]; then
if [ ! -f "$HL2_EXE" ]; then
new file mode 100644
new file mode 100644
@@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
+$ErrorActionPreference = 'Stop'
+
+$Root = if ($env:OPENVIBE_ROOT) { $env:OPENVIBE_ROOT } else { Join-Path $HOME 'src/openvibe-source' }
+$GameDir = Join-Path $Root 'game/openvibe.games'
+$ClientDll = Join-Path $GameDir 'bin/client.dll'
+$Hl2Exe = if ($env:OPENVIBE_HL2_EXE) { $env:OPENVIBE_HL2_EXE } else { 'C:\Program Files (x86)\Steam\steamapps\common\Source SDK Base 2013 Multiplayer\hl2.exe' }
+
+if (!(Test-Path $Hl2Exe)) { throw "hl2.exe not found. Set OPENVIBE_HL2_EXE." }
+if (!(Test-Path $ClientDll)) { throw "client.dll missing at $ClientDll. Run tools/build-sdk-windows.ps1 first." }
+
+$args = @('-game', $GameDir, '-console', '-dev', '-novid', '-sw', '-w', '1280', '-h', '720', '+exec', 'openvibe_proton_client.cfg')
+if ($args.Count -ge 2 -and $args[0] -match '^\d+\.\d+\.\d+\.\d+$') {
+ # no-op; reserved for direct powershell invocation variants
+}
+
+if ($args.Count -ge 2) {}
+
+# Accept optional IP PORT positional args from after -File.
+$extra = $MyInvocation.UnboundArguments
+if ($extra.Count -ge 2) {
+ $args += @('+connect', "$($extra[0]):$($extra[1])")
+}
+
+Start-Process -FilePath $Hl2Exe -ArgumentList $args -WorkingDirectory (Split-Path $Hl2Exe)
@@ -3,12 +3,14 @@ set -euo pipefail
@@ -3,12 +3,14 @@ set -euo pipefail
ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
TF2_SRCDS="${OPENVIBE_SRCDS:-$HOME/srcds/tf2}"
TF2_SRCDS="${OPENVIBE_SRCDS:-$HOME/srcds/tf2}"
-MOD_BIN="$ROOT/game/openvibe.games/bin/linux64"
-SDK_HL2MP_BIN="$ROOT/engine/source-sdk-2013/game/mod_hl2mp/bin/linux64"
+MOD_BIN="$ROOT/game/openvibe.games/bin"
+MOD_LINUX_BIN="$MOD_BIN/linux64"
+SDK_HL2MP_LINUX_BIN="$ROOT/engine/source-sdk-2013/game/mod_hl2mp/bin/linux64"
SDK_LIB_BIN="$ROOT/engine/source-sdk-2013/src/lib/public/linux64"
SDK_LIB_BIN="$ROOT/engine/source-sdk-2013/src/lib/public/linux64"
TF2_BIN="$TF2_SRCDS/bin/linux64"
TF2_BIN="$TF2_SRCDS/bin/linux64"
+SDK_HL2MP_WIN_BIN="$ROOT/engine/source-sdk-2013/game/mod_hl2mp/bin"
-mkdir -p "$MOD_BIN"
+mkdir -p "$MOD_LINUX_BIN" "$MOD_BIN"
require_file() {
require_file() {
if [[ ! -f "$1" ]]; then
if [[ ! -f "$1" ]]; then
@@ -17,28 +19,80 @@ require_file() {
@@ -17,28 +19,80 @@ require_file() {
fi
fi
}
}
-require_file "$SDK_HL2MP_BIN/client.so"
-require_file "$SDK_HL2MP_BIN/server.so"
-require_file "$SDK_HL2MP_BIN/game_shader_generic_example.so"
+link_if_exists() {
+ local src="$1" dst="$2"
+ if [[ -f "$src" ]]; then
+ ln -sfn "$src" "$dst"
+ echo "[openvibe] linked ${dst#$ROOT/} -> $src"
+ fi
+}
+
+copy_if_exists() {
+ local src="$1" dst="$2"
+ if [[ -f "$src" ]]; then
+ install -D -m 0644 "$src" "$dst"
+ echo "[openvibe] copied ${dst#$ROOT/}"
+ return 0
+ fi
+ return 1
+}
+
+# Linux native / Linux dedicated server modules.
+require_file "$SDK_HL2MP_LINUX_BIN/client.so"
+require_file "$SDK_HL2MP_LINUX_BIN/server.so"
+require_file "$SDK_HL2MP_LINUX_BIN/game_shader_generic_example.so"
require_file "$SDK_LIB_BIN/libtier0.so"
require_file "$SDK_LIB_BIN/libtier0.so"
require_file "$SDK_LIB_BIN/libvstdlib.so"
require_file "$SDK_LIB_BIN/libvstdlib.so"
require_file "$SDK_LIB_BIN/libsteam_api.so"
require_file "$SDK_LIB_BIN/libsteam_api.so"
-require_file "$TF2_BIN/soundemittersystem_srv.so"
-require_file "$TF2_BIN/shaderapiempty_srv.so"
-
-ln -sfn "$SDK_HL2MP_BIN/client.so" "$MOD_BIN/client.so"
-ln -sfn client.so "$MOD_BIN/client_srv.so"
-ln -sfn "$SDK_HL2MP_BIN/server.so" "$MOD_BIN/server.so"
-ln -sfn server.so "$MOD_BIN/server_srv.so"
-ln -sfn "$SDK_HL2MP_BIN/game_shader_generic_example.so" "$MOD_BIN/game_shader_generic_example_srv.so"
-
-ln -sfn "$SDK_LIB_BIN/libtier0.so" "$MOD_BIN/libtier0.so"
-ln -sfn "$SDK_LIB_BIN/libvstdlib.so" "$MOD_BIN/libvstdlib.so"
-ln -sfn "$SDK_LIB_BIN/libsteam_api.so" "$MOD_BIN/libsteam_api.so"
-
-for module in soundemittersystem scenefilecache datacache materialsystem studiorender vphysics vscript replay shaderapiempty; do
- require_file "$TF2_BIN/${module}_srv.so"
- ln -sfn "$TF2_BIN/${module}_srv.so" "$MOD_BIN/${module}.so"
+
+ln -sfn "$SDK_HL2MP_LINUX_BIN/client.so" "$MOD_LINUX_BIN/client.so"
+ln -sfn client.so "$MOD_LINUX_BIN/client_srv.so"
+ln -sfn "$SDK_HL2MP_LINUX_BIN/server.so" "$MOD_LINUX_BIN/server.so"
+ln -sfn server.so "$MOD_LINUX_BIN/server_srv.so"
+ln -sfn "$SDK_HL2MP_LINUX_BIN/game_shader_generic_example.so" "$MOD_LINUX_BIN/game_shader_generic_example_srv.so"
+ln -sfn "$SDK_LIB_BIN/libtier0.so" "$MOD_LINUX_BIN/libtier0.so"
+ln -sfn "$SDK_LIB_BIN/libvstdlib.so" "$MOD_LINUX_BIN/libvstdlib.so"
+ln -sfn "$SDK_LIB_BIN/libsteam_api.so" "$MOD_LINUX_BIN/libsteam_api.so"
+
+if [[ -d "$TF2_BIN" ]]; then
+ for module in soundemittersystem scenefilecache datacache materialsystem studiorender vphysics vscript replay shaderapiempty; do
+ if [[ -f "$TF2_BIN/${module}_srv.so" ]]; then
+ ln -sfn "$TF2_BIN/${module}_srv.so" "$MOD_LINUX_BIN/${module}.so"
+ fi
+ done
+fi
+
+echo "[openvibe] linux bin/linux64 compatibility links ready"
+
+# Windows/Proton modules. These are optional on Linux until a Windows build has run.
+win_client_candidates=(
+ "$SDK_HL2MP_WIN_BIN/client.dll"
+ "$SDK_HL2MP_WIN_BIN/win32/client.dll"
+ "$ROOT/engine/source-sdk-2013/src/game/client/Release_hl2mp/client.dll"
+ "$ROOT/engine/source-sdk-2013/src/game/client/Release/client.dll"
+)
+win_server_candidates=(
+ "$SDK_HL2MP_WIN_BIN/server.dll"
+ "$SDK_HL2MP_WIN_BIN/win32/server.dll"
+ "$ROOT/engine/source-sdk-2013/src/game/server/Release_hl2mp/server.dll"
+ "$ROOT/engine/source-sdk-2013/src/game/server/Release/server.dll"
+)
+
+for src in "${win_client_candidates[@]}"; do
+ if copy_if_exists "$src" "$MOD_BIN/client.dll"; then break; fi
+done
+for src in "${win_server_candidates[@]}"; do
+ if copy_if_exists "$src" "$MOD_BIN/server.dll"; then break; fi
done
done
-echo "[openvibe] bin/linux64 compatibility links ready"
+if [[ -f "$MOD_BIN/client.dll" ]]; then
+ echo "[openvibe] Windows/Proton client.dll present"
+else
+ echo "[openvibe] Windows/Proton client.dll not present yet; build on Windows to enable Proton in-game client DLL commands"
+fi
+
+if [[ -f "$MOD_BIN/server.dll" ]]; then
+ echo "[openvibe] Windows server.dll present"
+else
+ echo "[openvibe] Windows server.dll not present yet"
+fi