openvibers/source.openvibe.games Commits

Fix embedded JS console command duplication

CO Copilot committed 29 Jun 2026 · 3b3941ec95 · 10 files, +1579 −574


Changes

ddiff --git a/game/openvibe.games/js/gamemodes/base/server.js b/game/openvibe.games/js/gamemodes/base/server.js
iindex 9b5a15e..787eb56 100644
---- a/game/openvibe.games/js/gamemodes/base/server.js
++++ b/game/openvibe.games/js/gamemodes/base/server.js
@@@ -6,6 +6,10 @@ const GM = {
OV.log("Base Initialize fired");
},
++ MapInitialize(mapName) {
++ OV.log(`Base MapInitialize: ${mapName}`);
++ },
++
PlayerInitialSpawn(ply) {
ply.chat("Welcome to OpenVibe: Source.");
},
@@@ -20,7 +24,16 @@ const GM = {
return undefined;
},
-- Think() {}
++ ConsoleCommand(text) {
++ OV.log(`ConsoleCommand: ${text}`);
++ return undefined;
++ },
++
++ Think() {},
++
++ConsoleCommand(text) {
++ OV.log(`Base ConsoleCommand: ${text}`);
++ }
};
gamemode.set(GM);
ddiff --git a/game/openvibe.games/js/gamemodes/hub/server.js b/game/openvibe.games/js/gamemodes/hub/server.js
iindex 3095bed..a0e0693 100644
---- a/game/openvibe.games/js/gamemodes/hub/server.js
++++ b/game/openvibe.games/js/gamemodes/hub/server.js
@@@ -38,6 +38,17 @@ const GM = {
return undefined;
},
++ ConsoleCommand(text) {
++ OV.log(`Hub ConsoleCommand: ${text}`);
++
++ if (text === "smoke") {
++ OV.broadcast("OpenVibe embedded JS smoke test passed.");
++ return false;
++ }
++
++ return undefined;
++ },
++
Think() {}
};
ddiff --git a/sdk/openvibe/server/hl2mp/openvibe_js_server.cpp b/sdk/openvibe/server/hl2mp/openvibe_js_server.cpp
iindex e83d7a7..46a6c1d 100644
---- a/sdk/openvibe/server/hl2mp/openvibe_js_server.cpp
++++ b/sdk/openvibe/server/hl2mp/openvibe_js_server.cpp
@@@ -28,34 +28,53 @@ static bool OpenVibeJS_IsRunning()
static void OpenVibeJS_EnsureStarted()
{
-- if (g_OVServerJSStarted)
++ if ( g_OVServerJSStarted )
return;
g_OVServerJSStarted = true;
-- if (!ov_js_enabled.GetBool())
++ if ( !ov_js_enabled.GetBool() )
{
-- Msg("[OV JS] disabled by ov_js_enabled=0\n");
++ Msg( "[OV JS] disabled by ov_js_enabled=0\n" );
return;
}
-- if (g_OVServerJS.Init(true, ov_mode.GetString()))
++ if ( g_OVServerJS.Init( true, ov_mode.GetString() ) )
{
-- Msg("[OV JS] server runtime initialized for mode '%s'\n", ov_mode.GetString());
-- g_OVServerJS.CallHookVoid("Initialize");
++ Msg( "[OV JS] server runtime initialized for mode '%s'\n", ov_mode.GetString() );
++ g_OVServerJS.CallHookVoid( "Initialize" );
JSContext *ctx = g_OVServerJS.Context();
-- JSValue mapName = JS_NewString(ctx, gpGlobals ? STRING(gpGlobals->mapname) : "");
++ JSValue mapName = JS_NewString( ctx, gpGlobals ? STRING( gpGlobals->mapname ) : "" );
JSValueConst argv[] = { mapName };
-- g_OVServerJS.CallHookVoid("MapInitialize", 1, argv);
-- JS_FreeValue(ctx, mapName);
++ g_OVServerJS.CallHookVoid( "MapInitialize", 1, argv );
++ JS_FreeValue( ctx, mapName );
}
else
{
-- Warning("[OV JS] server runtime failed to initialize\n");
++ Warning( "[OV JS] server runtime failed to initialize\n" );
}
}
++static void OpenVibeJS_CallConsoleCommand( const char *pszCommand )
++{
++ OpenVibeJS_EnsureStarted();
++
++ if ( !OpenVibeJS_IsRunning() )
++ {
++ Warning( "[OV JS] runtime is not running; command ignored: %s\n", pszCommand ? pszCommand : "" );
++ return;
++ }
++
++ JSContext *ctx = g_OVServerJS.Context();
++ JSValue command = JS_NewString( ctx, pszCommand ? pszCommand : "" );
++ JSValueConst argv[] = { command };
++
++ g_OVServerJS.CallHookVoid( "ConsoleCommand", 1, argv );
++
++ JS_FreeValue( ctx, command );
++}
++
void OpenVibeJS_ServerInit()
{
OpenVibeJS_EnsureStarted();
@@@ -63,8 +82,8 @@ void OpenVibeJS_ServerInit()
void OpenVibeJS_ServerShutdown()
{
-- if (OpenVibeJS_IsRunning())
-- g_OVServerJS.CallHookVoid("Shutdown");
++ if ( OpenVibeJS_IsRunning() )
++ g_OVServerJS.CallHookVoid( "Shutdown" );
g_OVServerJS.Shutdown();
g_OVServerJSStarted = false;
@@@ -74,101 +93,101 @@ void OpenVibeJS_ServerThink()
{
OpenVibeJS_EnsureStarted();
-- if (!OpenVibeJS_IsRunning())
++ if ( !OpenVibeJS_IsRunning() )
return;
-- g_OVServerJS.CallHookVoid("Think");
++ g_OVServerJS.CallHookVoid( "Think" );
}
--void OpenVibeJS_Server_PlayerInitialSpawn(CHL2MP_Player *player)
++void OpenVibeJS_Server_PlayerInitialSpawn( CHL2MP_Player *player )
{
OpenVibeJS_EnsureStarted();
-- if (!OpenVibeJS_IsRunning() || !player)
++ if ( !OpenVibeJS_IsRunning() || !player )
return;
JSContext *ctx = g_OVServerJS.Context();
-- JSValue ply = OVJS_NewPlayer(ctx, player);
++ JSValue ply = OVJS_NewPlayer( ctx, player );
JSValueConst argv[] = { ply };
-- g_OVServerJS.CallHookVoid("PlayerInitialSpawn", 1, argv);
++ g_OVServerJS.CallHookVoid( "PlayerInitialSpawn", 1, argv );
-- JS_FreeValue(ctx, ply);
++ JS_FreeValue( ctx, ply );
}
--void OpenVibeJS_Server_PlayerSpawn(CHL2MP_Player *player)
++void OpenVibeJS_Server_PlayerSpawn( CHL2MP_Player *player )
{
OpenVibeJS_EnsureStarted();
-- if (!OpenVibeJS_IsRunning() || !player)
++ if ( !OpenVibeJS_IsRunning() || !player )
return;
JSContext *ctx = g_OVServerJS.Context();
-- JSValue ply = OVJS_NewPlayer(ctx, player);
++ JSValue ply = OVJS_NewPlayer( ctx, player );
JSValueConst argv[] = { ply };
-- g_OVServerJS.CallHookVoid("PlayerSpawn", 1, argv);
++ g_OVServerJS.CallHookVoid( "PlayerSpawn", 1, argv );
-- JS_FreeValue(ctx, ply);
++ JS_FreeValue( ctx, ply );
}
--void OpenVibeJS_Server_PlayerDeath(CHL2MP_Player *victim, CBaseEntity *attacker, CBaseEntity *inflictor)
++void OpenVibeJS_Server_PlayerDeath( CHL2MP_Player *victim, CBaseEntity *attacker, CBaseEntity *inflictor )
{
OpenVibeJS_EnsureStarted();
-- if (!OpenVibeJS_IsRunning() || !victim)
++ if ( !OpenVibeJS_IsRunning() || !victim )
return;
JSContext *ctx = g_OVServerJS.Context();
-- JSValue jsVictim = OVJS_NewPlayer(ctx, victim);
++ JSValue jsVictim = OVJS_NewPlayer( ctx, victim );
JSValue jsAttacker = JS_NULL;
-- if (attacker && attacker->IsPlayer())
-- jsAttacker = OVJS_NewPlayer(ctx, ToHL2MPPlayer(static_cast<CBasePlayer *>(attacker)));
++ if ( attacker && attacker->IsPlayer() )
++ jsAttacker = OVJS_NewPlayer( ctx, ToHL2MPPlayer( static_cast<CBasePlayer *>( attacker ) ) );
JSValueConst argv[] = { jsVictim, jsAttacker };
-- g_OVServerJS.CallHookVoid("PlayerDeath", 2, argv);
++ g_OVServerJS.CallHookVoid( "PlayerDeath", 2, argv );
-- JS_FreeValue(ctx, jsVictim);
-- JS_FreeValue(ctx, jsAttacker);
++ JS_FreeValue( ctx, jsVictim );
++ JS_FreeValue( ctx, jsAttacker );
}
--void OpenVibeJS_Server_PlayerDisconnected(CHL2MP_Player *player)
++void OpenVibeJS_Server_PlayerDisconnected( CHL2MP_Player *player )
{
OpenVibeJS_EnsureStarted();
-- if (!OpenVibeJS_IsRunning() || !player)
++ if ( !OpenVibeJS_IsRunning() || !player )
return;
JSContext *ctx = g_OVServerJS.Context();
-- JSValue ply = OVJS_NewPlayer(ctx, player);
++ JSValue ply = OVJS_NewPlayer( ctx, player );
JSValueConst argv[] = { ply };
-- g_OVServerJS.CallHookVoid("PlayerDisconnected", 1, argv);
++ g_OVServerJS.CallHookVoid( "PlayerDisconnected", 1, argv );
-- JS_FreeValue(ctx, ply);
++ JS_FreeValue( ctx, ply );
}
--bool OpenVibeJS_Server_PlayerSay(CHL2MP_Player *player, const char *text)
++bool OpenVibeJS_Server_PlayerSay( CHL2MP_Player *player, const char *text )
{
OpenVibeJS_EnsureStarted();
-- if (!OpenVibeJS_IsRunning() || !player || !text)
++ if ( !OpenVibeJS_IsRunning() || !player || !text )
return false;
JSContext *ctx = g_OVServerJS.Context();
-- JSValue ply = OVJS_NewPlayer(ctx, player);
-- JSValue msg = JS_NewString(ctx, text);
++ JSValue ply = OVJS_NewPlayer( ctx, player );
++ JSValue msg = JS_NewString( ctx, text );
JSValueConst argv[] = { ply, msg };
bool value = false;
-- bool returned = g_OVServerJS.CallHookBool("PlayerSay", &value, 2, argv);
++ bool returned = g_OVServerJS.CallHookBool( "PlayerSay", &value, 2, argv );
-- JS_FreeValue(ctx, ply);
-- JS_FreeValue(ctx, msg);
++ JS_FreeValue( ctx, ply );
++ JS_FreeValue( ctx, msg );
return returned && value == false;
}
@@@ -177,7 +196,7 @@ static void OV_JSReload_f()
{
OpenVibeJS_ServerShutdown();
OpenVibeJS_ServerInit();
-- Msg("[OV JS] reloaded\n");
++ Msg( "[OV JS] reloaded\n" );
}
static ConCommand ov_js_reload(
@@@ -186,3 +205,63 @@ static ConCommand ov_js_reload(
"Reload OpenVibe JavaScript runtime.",
FCVAR_GAMEDLL
);
++
++static void OV_JSStatus_f()
++{
++ Msg( "[OV JS] enabled=%d started=%d running=%d mode=%s\n",
++ ov_js_enabled.GetBool() ? 1 : 0,
++ g_OVServerJSStarted ? 1 : 0,
++ OpenVibeJS_IsRunning() ? 1 : 0,
++ ov_mode.GetString() );
++}
++
++static ConCommand ov_js_status(
++ "ov_js_status",
++ OV_JSStatus_f,
++ "Print OpenVibe JavaScript runtime status.",
++ FCVAR_GAMEDLL
++);
++
++static void OV_JSFire_f( const CCommand &args )
++{
++ if ( args.ArgC() < 2 )
++ {
++ Msg( "Usage: ov_js_fire <HookName>\n" );
++ return;
++ }
++
++ OpenVibeJS_EnsureStarted();
++
++ if ( !OpenVibeJS_IsRunning() )
++ {
++ Warning( "[OV JS] runtime is not running\n" );
++ return;
++ }
++
++ g_OVServerJS.CallHookVoid( args[1] );
++}
++
++static ConCommand ov_js_fire(
++ "ov_js_fire",
++ OV_JSFire_f,
++ "Fire an OpenVibe JavaScript hook with no arguments.",
++ FCVAR_GAMEDLL
++);
++
++static void OV_JSCmd_f( const CCommand &args )
++{
++ if ( args.ArgC() < 2 )
++ {
++ Msg( "Usage: ov_js_cmd <text>\n" );
++ return;
++ }
++
++ OpenVibeJS_CallConsoleCommand( args.ArgS() );
++}
++
++static ConCommand ov_js_cmd(
++ "ov_js_cmd",
++ OV_JSCmd_f,
++ "Send a ConsoleCommand event into the embedded OpenVibe JavaScript runtime.",
++ FCVAR_GAMEDLL
++);
ddiff --git a/sdk/openvibe/server/hl2mp/openvibe_js_server.h b/sdk/openvibe/server/hl2mp/openvibe_js_server.h
iindex b076c2f..744a7ad 100644
---- a/sdk/openvibe/server/hl2mp/openvibe_js_server.h
++++ b/sdk/openvibe/server/hl2mp/openvibe_js_server.h
@@@ -13,3 +13,6 @@ void OpenVibeJS_Server_PlayerSpawn(CHL2MP_Player *player);
void OpenVibeJS_Server_PlayerDeath(CHL2MP_Player *victim, CBaseEntity *attacker, CBaseEntity *inflictor);
void OpenVibeJS_Server_PlayerDisconnected(CHL2MP_Player *player);
bool OpenVibeJS_Server_PlayerSay(CHL2MP_Player *player, const char *text);
++
++void OpenVibeJS_Server_ConsoleCommand(const char *text);
++void OpenVibeJS_Server_FireHook(const char *hookName);
ddiff --git a/sdk/openvibe/server/hl2mp/openvibe_server.cpp b/sdk/openvibe/server/hl2mp/openvibe_server.cpp
iindex ab658ef..2f7fc96 100644
---- a/sdk/openvibe/server/hl2mp/openvibe_server.cpp
++++ b/sdk/openvibe/server/hl2mp/openvibe_server.cpp
@@@ -17,18 +17,6 @@
#include "steam/isteamhttp.h"
// Standard library and socket includes for JS bridge
--#include <thread>
--#include <mutex>
--#include <queue>
--#include <string>
--#include <condition_variable>
--#include <sys/socket.h>
--#include <sys/un.h>
--#include <unistd.h>
--#include <fcntl.h>
--#include <sstream>
--#include <sys/select.h>
--#include <sys/time.h>
// memdbgon must be the last include file in a .cpp file.
#include "tier0/memdbgon.h"
@@@ -382,413 +370,9 @@ static ConCommand ov_fortwars_spawn_cmd(
// =================================══════════════════════════════════════════
// OpenVibe.JS - Scripting Engine C++ Bridge
--// =================================══════════════════════════════════════════
--
--class COpenVibeJsBridge
--{
--private:
-- std::thread m_workerThread;
-- std::mutex m_outgoingMutex;
-- std::queue<std::string> m_outgoingQueue;
-- std::mutex m_incomingMutex;
-- std::queue<std::string> m_incomingQueue;
-- bool m_bRunning;
-- bool m_bConnected;
-- int m_socketFd;
-- std::string m_socketPath;
-- std::string m_incomingBuffer;
--
-- void ThreadFunc()
-- {
-- while ( m_bRunning )
-- {
-- if ( !m_bConnected )
-- {
-- m_socketFd = socket( AF_UNIX, SOCK_STREAM, 0 );
-- if ( m_socketFd >= 0 )
-- {
-- struct sockaddr_un addr;
-- memset( &addr, 0, sizeof( addr ) );
-- addr.sun_family = AF_UNIX;
-- Q_strncpy( addr.sun_path, m_socketPath.c_str(), sizeof( addr.sun_path ) );
--
-- if ( connect( m_socketFd, (struct sockaddr*)&addr, sizeof( addr ) ) == 0 )
-- {
-- m_bConnected = true;
-- Msg( "[OV JS] Connected to Node.js scripting bridge at %s\n", m_socketPath.c_str() );
-- }
-- else
-- {
-- close( m_socketFd );
-- m_socketFd = -1;
-- SpawnNodeJs();
-- std::this_thread::sleep_for( std::chrono::milliseconds( 2000 ) );
-- continue;
-- }
-- }
-- }
--
-- if ( m_bConnected )
-- {
-- fd_set readFds;
-- fd_set writeFds;
-- FD_ZERO( &readFds );
-- FD_ZERO( &writeFds );
-- FD_SET( m_socketFd, &readFds );
--
-- bool bHasWrite = false;
-- {
-- std::lock_guard<std::mutex> lock( m_outgoingMutex );
-- bHasWrite = !m_outgoingQueue.empty();
-- }
-- if ( bHasWrite )
-- {
-- FD_SET( m_socketFd, &writeFds );
-- }
--
-- struct timeval tv;
-- tv.tv_sec = 0;
-- tv.tv_usec = 50000;
--
-- int activity = select( m_socketFd + 1, &readFds, &writeFds, NULL, &tv );
-- if ( activity < 0 )
-- {
-- Disconnect();
-- continue;
-- }
--
-- if ( FD_ISSET( m_socketFd, &readFds ) )
-- {
-- char buffer[4096];
-- int bytesRead = recv( m_socketFd, buffer, sizeof( buffer ) - 1, 0 );
-- if ( bytesRead <= 0 )
-- {
-- Disconnect();
-- continue;
-- }
-- buffer[bytesRead] = '\0';
-- ProcessIncomingBuffer( buffer );
-- }
--
-- if ( bHasWrite && FD_ISSET( m_socketFd, &writeFds ) )
-- {
-- std::string msg;
-- {
-- std::lock_guard<std::mutex> lock( m_outgoingMutex );
-- if ( !m_outgoingQueue.empty() )
-- {
-- msg = m_outgoingQueue.front();
-- m_outgoingQueue.pop();
-- }
-- }
-- if ( !msg.empty() )
-- {
-- int sent = send( m_socketFd, msg.c_str(), msg.length(), 0 );
-- if ( sent < 0 )
-- {
-- Disconnect();
-- continue;
-- }
-- }
-- }
-- }
-- }
-- }
--
-- void SpawnNodeJs()
-- {
-- static float lastSpawnTime = 0.0f;
-- float curTime = gpGlobals ? gpGlobals->realtime : 0.0f;
-- if ( curTime > 0.0f && curTime - lastSpawnTime < 10.0f )
-- {
-- return;
-- }
-- lastSpawnTime = curTime;
--
-- Msg( "[OV JS] Starting Node.js OpenVibe.JS runtime...\n" );
-- char cmd[1024];
-- const char *root = getenv( "OPENVIBE_ROOT" );
-- if ( !root )
-- {
-- root = "/home/workstation/src/openvibe-source";
-- }
-- Q_snprintf( cmd, sizeof( cmd ), "node %s/engine/openvibe-js-runtime/bridge.js %s > /tmp/openvibe-js-%s.log 2>&1 &",
-- root, m_socketPath.c_str(), ov_server_id.GetString() );
-- int ret = system( cmd );
-- (void)ret;
-- }
--
-- void Disconnect()
-- {
-- if ( m_socketFd >= 0 )
-- {
-- close( m_socketFd );
-- m_socketFd = -1;
-- }
-- m_bConnected = false;
-- Msg( "[OV JS] Disconnected from Node.js scripting bridge.\n" );
-- }
--
-- void ProcessIncomingBuffer( const char *data )
-- {
-- m_incomingBuffer += data;
-- size_t pos;
-- while ( ( pos = m_incomingBuffer.find( '\n' ) ) != std::string::npos )
-- {
-- std::string line = m_incomingBuffer.substr( 0, pos );
-- m_incomingBuffer.erase( 0, pos + 1 );
-- if ( !line.empty() )
-- {
-- std::lock_guard<std::mutex> lock( m_incomingMutex );
-- m_incomingQueue.push( line );
-- }
-- }
-- }
--
--public:
-- COpenVibeJsBridge() :
-- m_bRunning( false ),
-- m_bConnected( false ),
-- m_socketFd( -1 )
-- {
-- }
--
-- ~COpenVibeJsBridge()
-- {
-- Stop();
-- }
--
-- void Start()
-- {
-- if ( m_bRunning ) return;
--
-- char szSocket[256];
-- Q_snprintf( szSocket, sizeof( szSocket ), "/tmp/openvibe-js-%s.sock", ov_server_id.GetString() );
-- m_socketPath = szSocket;
--
-- m_bRunning = true;
-- m_workerThread = std::thread( &COpenVibeJsBridge::ThreadFunc, this );
-- Msg( "[OV JS] Scripting bridge service thread started. socket=%s\n", m_socketPath.c_str() );
-- }
--
-- void Stop()
-- {
-- m_bRunning = false;
-- if ( m_workerThread.joinable() )
-- {
-- m_workerThread.join();
-- }
-- Disconnect();
-- }
--
-- void SendMessage( const std::string &msg )
-- {
-- if ( !m_bConnected ) return;
-- std::lock_guard<std::mutex> lock( m_outgoingMutex );
-- m_outgoingQueue.push( msg + "\n" );
-- }
--
-- void OnPlayerJoined( CBasePlayer *pPlayer )
-- {
-- if ( !pPlayer ) return;
--
-- char szSteamID[32];
-- CSteamID steamID;
-- if ( pPlayer->GetSteamID( &steamID ) )
-- {
-- Q_snprintf( szSteamID, sizeof( szSteamID ), "%llu", steamID.ConvertToUint64() );
-- }
-- else
-- {
-- Q_strncpy( szSteamID, "0", sizeof( szSteamID ) );
-- }
--
-- std::stringstream ss;
-- ss << "{\"type\":\"player_join\",\"data\":{"
-- << "\"steamId\":\"" << szSteamID << "\","
-- << "\"name\":\"" << pPlayer->GetPlayerName() << "\""
-- << "}}";
-- SendMessage( ss.str() );
-- }
--
-- void OnPlayerLeft( CBasePlayer *pPlayer )
-- {
-- if ( !pPlayer ) return;
--
-- char szSteamID[32];
-- CSteamID steamID;
-- if ( pPlayer->GetSteamID( &steamID ) )
-- {
-- Q_snprintf( szSteamID, sizeof( szSteamID ), "%llu", steamID.ConvertToUint64() );
-- }
-- else
-- {
-- Q_strncpy( szSteamID, "0", sizeof( szSteamID ) );
-- }
-- std::stringstream ss;
-- ss << "{\"type\":\"player_leave\",\"data\":{"
-- << "\"steamId\":\"" << szSteamID << "\""
-- << "}}";
-- SendMessage( ss.str() );
-- }
-- void OnPlayerDeath( CBasePlayer *pPlayer, CBaseEntity *pKiller )
-- {
-- if ( !pPlayer ) return;
--
-- char szDeadSteamID[32];
-- CSteamID deadSteamID;
-- if ( pPlayer->GetSteamID( &deadSteamID ) )
-- {
-- Q_snprintf( szDeadSteamID, sizeof( szDeadSteamID ), "%llu", deadSteamID.ConvertToUint64() );
-- }
-- else
-- {
-- Q_strncpy( szDeadSteamID, "0", sizeof( szDeadSteamID ) );
-- }
--
-- char szKillerSteamID[32] = "0";
-- if ( pKiller && pKiller->IsPlayer() )
-- {
-- CBasePlayer *pKillerPlayer = (CBasePlayer*)pKiller;
-- CSteamID killerSteamID;
-- if ( pKillerPlayer->GetSteamID( &killerSteamID ) )
-- {
-- Q_snprintf( szKillerSteamID, sizeof( szKillerSteamID ), "%llu", killerSteamID.ConvertToUint64() );
-- }
-- }
--
-- std::stringstream ss;
-- ss << "{\"type\":\"player_death\",\"data\":{"
-- << "\"deadSteamId\":\"" << szDeadSteamID << "\","
-- << "\"killerSteamId\":\"" << szKillerSteamID << "\""
-- << "}}";
-- SendMessage( ss.str() );
-- }
--
-- void OnCommand( const char *cmd )
-- {
-- std::stringstream ss;
-- ss << "{\"type\":\"command\",\"data\":{"
-- << "\"cmd\":\"" << cmd << "\""
-- << "}}";
-- SendMessage( ss.str() );
-- }
--
-- void Update()
-- {
-- if ( !m_bRunning )
-- {
-- Start();
-- }
--
-- static float lastTickTime = 0.0f;
-- float curTime = gpGlobals->curtime;
-- if ( curTime - lastTickTime >= 1.0f )
-- {
-- lastTickTime = curTime;
-- SendMessage( "{\"type\":\"tick\",\"data\":{}}" );
-- }
--
-- std::queue<std::string> localQueue;
-- {
-- std::lock_guard<std::mutex> lock( m_incomingMutex );
-- while ( !m_incomingQueue.empty() )
-- {
-- localQueue.push( m_incomingQueue.front() );
-- m_incomingQueue.pop();
-- }
-- }
--
-- while ( !localQueue.empty() )
-- {
-- std::string line = localQueue.front();
-- localQueue.pop();
-- ProcessMessage( line );
-- }
-- }
--
--private:
-- void ProcessMessage( const std::string &line )
-- {
-- if ( line.find( "\"type\":\"broadcast\"" ) != std::string::npos )
-- {
-- size_t msgPos = line.find( "\"message\":\"" );
-- if ( msgPos != std::string::npos )
-- {
-- msgPos += 11;
-- size_t endPos = line.find( "\"", msgPos );
-- if ( endPos != std::string::npos )
-- {
-- std::string msg = line.substr( msgPos, endPos - msgPos );
-- UTIL_ClientPrintAll( HUD_PRINTTALK, "[OV JS] %s", msg.c_str() );
-- }
-- }
-- }
-- else if ( line.find( "\"type\":\"entity_spawn\"" ) != std::string::npos )
-- {
-- std::string type = "prop_physics_override";
-- std::string model = "";
-- Vector pos( 0, 0, 0 );
--
-- size_t tPos = line.find( "\"type\":\"" );
-- if ( tPos != std::string::npos )
-- {
-- size_t dataPos = line.find( "\"data\":{" );
-- if ( dataPos != std::string::npos )
-- {
-- size_t innerType = line.find( "\"type\":\"", dataPos );
-- if ( innerType != std::string::npos )
-- {
-- innerType += 8;
-- size_t end = line.find( "\"", innerType );
-- if ( end != std::string::npos )
-- {
-- type = line.substr( innerType, end - innerType );
-- }
-- }
-- size_t innerModel = line.find( "\"model\":\"", dataPos );
-- if ( innerModel != std::string::npos )
-- {
-- innerModel += 9;
-- size_t end = line.find( "\"", innerModel );
-- if ( end != std::string::npos )
-- {
-- model = line.substr( innerModel, end - innerModel );
-- }
-- }
-- size_t xPos = line.find( "\"x\":", dataPos );
-- size_t yPos = line.find( "\"y\":", dataPos );
-- size_t zPos = line.find( "\"z\":", dataPos );
-- if ( xPos != std::string::npos && yPos != std::string::npos && zPos != std::string::npos )
-- {
-- pos.x = atof( line.c_str() + xPos + 4 );
-- pos.y = atof( line.c_str() + yPos + 4 );
-- pos.z = atof( line.c_str() + zPos + 4 );
-- }
-- }
-- }
--
-- if ( !model.empty() )
-- {
-- CBaseEntity::PrecacheModel( model.c_str() );
-- CBaseEntity *pEnt = CreateEntityByName( type.c_str() );
-- if ( pEnt )
-- {
-- pEnt->KeyValue( "model", model.c_str() );
-- pEnt->SetAbsOrigin( pos );
-- DispatchSpawn( pEnt );
-- pEnt->Activate();
-- Msg( "[OV JS] Spawned entity %s with model %s at (%f, %f, %f)\n",
-- type.c_str(), model.c_str(), pos.x, pos.y, pos.z );
-- }
-- }
-- }
-- }
--};
--
--static COpenVibeJsBridge g_OpenVibeJsBridge;
++// ======================================\n// OpenVibe embedded JavaScript hook bridge\n// ======================================
void OpenVibe_OnClientDisconnect( CBasePlayer *pPlayer )
{
@@@ -810,18 +394,3 @@ void OpenVibe_OnFrame()
OpenVibeJS_ServerThink();
}
--static void OV_JsCmd_f( const CCommand &args )
--{
-- if ( args.ArgC() < 2 )
-- {
-- Msg( "Usage: ov_js_cmd <command_or_json>\n" );
-- return;
-- }
-- g_OpenVibeJsBridge.OnCommand( args.ArgS() );
--}
--
--static ConCommand ov_js_cmd(
-- "ov_js_cmd",
-- OV_JsCmd_f,
-- "Send a command or JSON message to the OpenVibe.JS runtime.",
-- FCVAR_GAMEDLL );
ddiff --git a/tools/apply-openvibe-sdk.sh b/tools/apply-openvibe-sdk.sh
iindex 437187e..106a4da 100755
---- a/tools/apply-openvibe-sdk.sh
++++ b/tools/apply-openvibe-sdk.sh
@@@ -55,25 +55,6 @@ copy_tree "$ROOT/sdk/openvibe/third_party/quickjs" \
"$ROOT/tools/build-quickjs-lib.sh"
--QJS_C="$SDK/src/game/shared/openvibe/third_party/quickjs/quickjs.c"
--if [[ -f "$QJS_C" ]] && ! grep -q 'OPENVIBE_QUICKJS_CONFIG' "$QJS_C"; then
-- tmp="$(mktemp)"
-- cat > "$tmp" <<'QCFG'
--#ifndef OPENVIBE_QUICKJS_CONFIG
--#define OPENVIBE_QUICKJS_CONFIG
--#ifndef _GNU_SOURCE
--#define _GNU_SOURCE
--#endif
--#ifndef CONFIG_VERSION
--#define CONFIG_VERSION "openvibe"
--#endif
--#endif
--
--QCFG
-- cat "$QJS_C" >> "$tmp"
-- mv "$tmp" "$QJS_C"
--fi
--
CLIENT_VPC="$SDK/src/game/client/client_hl2mp.vpc"
SERVER_VPC="$SDK/src/game/server/server_hl2mp.vpc"
HL2MP_CLIENT="$SDK/src/game/server/hl2mp/hl2mp_client.cpp"
@@@ -81,7 +62,11 @@ HL2MP_CLIENTMODE="$SDK/src/game/client/hl2mp/clientmode_hl2mpnormal.cpp"
GAMEINTERFACE="$SDK/src/game/server/gameinterface.cpp"
HL2MP_PLAYER="$SDK/src/game/server/hl2mp/hl2mp_player.cpp"
--perl -0pi -e 's/^.*hl2mp\\openvibe_client\.cpp.*\n//mg; s/^.*hl2mp\\vgui_openvibe_menu\.cpp.*\n//mg; s/(\$File\s+"hl2mp\\clientmode_hl2mpnormal\.h"\n)/$1\t\t\t\$File\t"hl2mp\\openvibe_client.cpp"\n\t\t\t\$File\t"hl2mp\\vgui_openvibe_menu.cpp"\n/s' "$CLIENT_VPC"
++perl -0pi -e '
++ s/^.*hl2mp\\openvibe_client\.cpp.*\n//mg;
++ s/^.*hl2mp\\vgui_openvibe_menu\.cpp.*\n//mg;
++ s/(\$File\s+"hl2mp\\clientmode_hl2mpnormal\.h"\n)/$1\t\t\t\$File\t"hl2mp\\openvibe_client.cpp"\n\t\t\t\$File\t"hl2mp\\vgui_openvibe_menu.cpp"\n/s;
++' "$CLIENT_VPC"
echo "[openvibe-sdk] patched client_hl2mp.vpc"
perl -0pi -e '
@@@ -90,7 +75,14 @@ perl -0pi -e '
s/^.*openvibe\\ov_js_runtime\.cpp.*\n//mg;
s/^.*openvibe\\ov_js_bindings\.cpp.*\n//mg;
s/^.*openvibe\\ov_js_player\.cpp.*\n//mg;
-- s/(\$File\s+"hl2mp\\hl2mp_player\.h"\n)/$1\t\t\t\$File\t"hl2mp\\openvibe_server.cpp"\n\t\t\t\$File\t"hl2mp\\openvibe_js_server.cpp"\n\t\t\t\$File\t"..\\shared\\openvibe\\ov_js_runtime.cpp"\n\t\t\t\$File\t"..\\shared\\openvibe\\ov_js_bindings.cpp"\n\t\t\t\$File\t"..\\shared\\openvibe\\ov_js_player.cpp"\n/s;
++ s/^.*quickjs\\quickjs\.c.*\n//mg;
++ s/^.*quickjs\\libregexp\.c.*\n//mg;
++ s/^.*quickjs\\libunicode\.c.*\n//mg;
++ s/^.*quickjs\\cutils\.c.*\n//mg;
++ s/^.*quickjs\\dtoa\.c.*\n//mg;
++ s/^.*quickjs\\libbf\.c.*\n//mg;
++ s/^.*libquickjs_openvibe(?:\.a)?".*\n//mg;
++ s/(\$File\s+"hl2mp\\hl2mp_player\.h"\n)/$1\t\t\t\$File\t"hl2mp\\openvibe_server.cpp"\n\t\t\t\$File\t"hl2mp\\openvibe_js_server.cpp"\n\t\t\t\$File\t"..\\shared\\openvibe\\ov_js_runtime.cpp"\n\t\t\t\$File\t"..\\shared\\openvibe\\ov_js_bindings.cpp"\n\t\t\t\$File\t"..\\shared\\openvibe\\ov_js_player.cpp"\n\t\t\t\$Lib\t"..\\shared\\openvibe\\third_party\\quickjs\\build\\libquickjs_openvibe"\n/s;
' "$SERVER_VPC"
echo "[openvibe-sdk] patched server_hl2mp.vpc"
@@@ -147,9 +139,6 @@ import re, sys
p = Path(sys.argv[1])
s = p.read_text()
--# Best-effort patch for common Source SDK 2013 HL2MP Host_Say shape:
--# CHL2MP_Player *client = ...
--# const char *p = args.ArgS();
if "OpenVibeJS_Server_PlayerSay( client," not in s:
s2 = re.sub(
r'(const char \*p\s*=\s*args\.ArgS\(\);\s*)',
@@@ -166,75 +155,4 @@ p.write_text(s)
PY
fi
--
--# OPENVIBE_CONTINUE_QUICKJS_POSTPATCH
--patch_openvibe_quickjs_after_apply() {
-- local qjs_dir="$SDK/src/game/shared/openvibe/third_party/quickjs"
-- local qjs_c="$qjs_dir/quickjs.c"
--
-- if [[ -f "$qjs_c" ]] && ! grep -q 'OPENVIBE_QUICKJS_STRICT_C_SHIM' "$qjs_c"; then
-- local tmp
-- tmp="$(mktemp)"
-- cat > "$tmp" <<'QJS_SHIM'
--#ifndef OPENVIBE_QUICKJS_STRICT_C_SHIM
--#define OPENVIBE_QUICKJS_STRICT_C_SHIM
--
--#ifndef _GNU_SOURCE
--#define _GNU_SOURCE
--#endif
--
--#ifndef CONFIG_VERSION
--#define CONFIG_VERSION "openvibe"
--#endif
--
--#if defined(__GNUC__) && !defined(__cplusplus) && defined(__STRICT_ANSI__) && !defined(asm)
--#define asm __asm__
--#endif
--
--#endif
--
--QJS_SHIM
-- cat "$qjs_c" >> "$tmp"
-- mv "$tmp" "$qjs_c"
-- echo "[openvibe-sdk] patched QuickJS strict-C shim"
-- fi
--
-- if [[ ! -f "$qjs_dir/libbf.c" ]]; then
-- perl -0pi -e 's/^.*openvibe\\\\third_party\\\\quickjs\\\\libbf\.c.*\n//mg; s/^.*openvibe\\third_party\\quickjs\\libbf\.c.*\n//mg' "$SERVER_VPC" || true
-- echo "[openvibe-sdk] libbf.c missing; removed libbf.c from server_hl2mp.vpc"
-- fi
--}
--
--patch_openvibe_quickjs_after_apply
--
--
--# OPENVIBE_FIX_QUICKJS_LINK_FINAL_V2
--# QuickJS is C, so do not compile its .c files through Source SDK/VPC C++.
--# We build libquickjs_openvibe.a with cc and link it as a library.
--if [[ -f "$SERVER_VPC" ]]; then
-- perl -0pi -e '
-- s/^.*quickjs\\quickjs\.c.*\n//mg;
-- s/^.*quickjs\\libregexp\.c.*\n//mg;
-- s/^.*quickjs\\libunicode\.c.*\n//mg;
-- s/^.*quickjs\\cutils\.c.*\n//mg;
-- s/^.*quickjs\\dtoa\.c.*\n//mg;
-- s/^.*quickjs\\libbf\.c.*\n//mg;
-- ' "$SERVER_VPC"
--
-- perl -0pi -e 's/^.*libquickjs_openvibe(?:\.a)?".*\n//mg' "$SERVER_VPC"
--
-- perl -0pi -e 's/(\$File\s+"hl2mp\\openvibe_js_server\.cpp"\n)/$1\t\t\t\$Lib\t"..\\shared\\openvibe\\third_party\\quickjs\\build\\libquickjs_openvibe"\n/s' "$SERVER_VPC"
--
-- echo "[openvibe-sdk] linked QuickJS static library"
--fi
--
echo "[openvibe-sdk] Source SDK OpenVibe patch applied"
--
--
--# Link prebuilt QuickJS C static library. QuickJS must be compiled as C, not C++.
--if ! grep -q 'libquickjs_openvibe' "$SERVER_VPC"; then
-- perl -0pi -e 's/(\$File\s+"hl2mp\\openvibe_js_server\.cpp"\n)/$1\t\t\t\$Lib\t"..\\shared\\openvibe\\third_party\\quickjs\\build\\libquickjs_openvibe"\n/s' "$SERVER_VPC"
-- echo "[openvibe-sdk] linked QuickJS static library"
--fi
--
--
ddiff --git a/tools/build-quickjs-lib.sh b/tools/build-quickjs-lib.sh
iindex fd42066..39a66f0 100755
---- a/tools/build-quickjs-lib.sh
++++ b/tools/build-quickjs-lib.sh
@@@ -16,7 +16,7 @@ fi
mkdir -p "$SDK_QJS"
--# Exclude build/ so rsync does not delete our .o/.a output dir.
++# Exclude build/ so rsync does not delete object/library output.
rsync -a --delete --exclude='build/' "$SRC_QJS/" "$SDK_QJS/"
mkdir -p "$OUT"
ddiff --git a/tools/continue-openvibe-embedded-js-phase.sh b/tools/continue-openvibe-embedded-js-phase.sh
nnew file mode 100755
iindex 0000000..55371db
---- /dev/null
++++ b/tools/continue-openvibe-embedded-js-phase.sh
@@@ -0,0 +1,588 @@
++#!/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)"
++RUN_BUILD="${RUN_BUILD:-1}"
++RUN_SETUP="${RUN_SETUP:-1}"
++
++cd "$ROOT"
++
++backup_file() {
++ local file="$1"
++ [[ -f "$file" ]] && cp "$file" "$file.bak.$STAMP"
++}
++
++echo "[openvibe] continue after QuickJS build pass"
++echo "[openvibe] root=$ROOT"
++echo "[openvibe] sdk=$SDK"
++
++mkdir -p tools sdk/openvibe/shared sdk/openvibe/server/hl2mp game/openvibe.games/js/gamemodes/base game/openvibe.games/js/gamemodes/hub
++
++echo "[openvibe] clean local generated junk from git tracking if present"
++git rm -f .tmp/quickjs-smoke/smoke-quickjs 2>/dev/null || true
++git rm -f tools/apply-openvibe-sdk.sh.bak.* 2>/dev/null || true
++
++touch .gitignore
++grep -qxF '.tmp/' .gitignore || cat >> .gitignore <<'GITIGNORE_ADD'
++
++# OpenVibe local/generated build junk
++.tmp/
++*.bak.*
++tools/*.bak.*
++engine/source-sdk-2013/src/game/shared/openvibe/third_party/quickjs/build/
++GITIGNORE_ADD
++
++echo "[openvibe] write canonical tools/build-quickjs-lib.sh"
++
++cat > tools/build-quickjs-lib.sh <<'BUILDQJS'
++#!/usr/bin/env bash
++set -euo pipefail
++
++ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
++SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"
++
++SRC_QJS="$ROOT/sdk/openvibe/third_party/quickjs"
++SDK_QJS="$SDK/src/game/shared/openvibe/third_party/quickjs"
++OUT="$SDK_QJS/build"
++
++if [[ ! -f "$SRC_QJS/quickjs.c" ]]; then
++ echo "Missing $SRC_QJS/quickjs.c" >&2
++ echo "Run: tools/vendor-quickjs.sh" >&2
++ exit 1
++fi
++
++mkdir -p "$SDK_QJS"
++
++# Exclude build/ so rsync does not delete object/library output.
++rsync -a --delete --exclude='build/' "$SRC_QJS/" "$SDK_QJS/"
++
++mkdir -p "$OUT"
++
++sources=(
++ quickjs.c
++ libregexp.c
++ libunicode.c
++ cutils.c
++)
++
++[[ -f "$SDK_QJS/dtoa.c" ]] && sources+=(dtoa.c)
++[[ -f "$SDK_QJS/libbf.c" ]] && sources+=(libbf.c)
++
++rm -f "$OUT"/*.o "$OUT"/libquickjs_openvibe.a
++
++for src in "${sources[@]}"; do
++ obj="$OUT/${src%.c}.o"
++ echo "[openvibe-qjs] cc $src -> $obj"
++
++ cc \
++ -std=gnu11 \
++ -O2 \
++ -fPIC \
++ -D_GNU_SOURCE \
++ -DCONFIG_VERSION=\"openvibe\" \
++ -I"$SDK_QJS" \
++ -c "$SDK_QJS/$src" \
++ -o "$obj"
++done
++
++ar rcs "$OUT/libquickjs_openvibe.a" "$OUT"/*.o
++
++echo "[openvibe-qjs] built $OUT/libquickjs_openvibe.a"
++ls -lh "$OUT/libquickjs_openvibe.a"
++BUILDQJS
++
++chmod +x tools/build-quickjs-lib.sh
++
++echo "[openvibe] write clean canonical tools/apply-openvibe-sdk.sh"
++
++backup_file tools/apply-openvibe-sdk.sh
++
++cat > tools/apply-openvibe-sdk.sh <<'APPLY'
++#!/usr/bin/env bash
++set -euo pipefail
++
++ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
++SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"
++
++if [[ ! -d "$SDK/src/game/client/hl2mp" || ! -d "$SDK/src/game/server/hl2mp" ]]; then
++ echo "Source SDK 2013 checkout not found at $SDK" >&2
++ echo "Set OPENVIBE_SDK=/path/to/source-sdk-2013 if it lives elsewhere." >&2
++ exit 1
++fi
++
++copy_file() {
++ local src="$1"
++ local dst="$2"
++ install -D -m 0644 "$src" "$dst"
++ echo "[openvibe-sdk] copied ${dst#$SDK/}"
++}
++
++copy_tree() {
++ local src="$1"
++ local dst="$2"
++ mkdir -p "$dst"
++ rsync -a --delete "$src/" "$dst/"
++ echo "[openvibe-sdk] copied tree ${dst#$SDK/}"
++}
++
++copy_file "$ROOT/sdk/openvibe/client/hl2mp/openvibe_client.cpp" \
++ "$SDK/src/game/client/hl2mp/openvibe_client.cpp"
++copy_file "$ROOT/sdk/openvibe/client/hl2mp/vgui_openvibe_menu.cpp" \
++ "$SDK/src/game/client/hl2mp/vgui_openvibe_menu.cpp"
++copy_file "$ROOT/sdk/openvibe/server/hl2mp/openvibe_server.cpp" \
++ "$SDK/src/game/server/hl2mp/openvibe_server.cpp"
++
++copy_file "$ROOT/sdk/openvibe/shared/ov_js_runtime.h" \
++ "$SDK/src/game/shared/openvibe/ov_js_runtime.h"
++copy_file "$ROOT/sdk/openvibe/shared/ov_js_runtime.cpp" \
++ "$SDK/src/game/shared/openvibe/ov_js_runtime.cpp"
++copy_file "$ROOT/sdk/openvibe/shared/ov_js_bindings.h" \
++ "$SDK/src/game/shared/openvibe/ov_js_bindings.h"
++copy_file "$ROOT/sdk/openvibe/shared/ov_js_bindings.cpp" \
++ "$SDK/src/game/shared/openvibe/ov_js_bindings.cpp"
++copy_file "$ROOT/sdk/openvibe/shared/ov_js_player.h" \
++ "$SDK/src/game/shared/openvibe/ov_js_player.h"
++copy_file "$ROOT/sdk/openvibe/shared/ov_js_player.cpp" \
++ "$SDK/src/game/shared/openvibe/ov_js_player.cpp"
++
++copy_file "$ROOT/sdk/openvibe/server/hl2mp/openvibe_js_server.h" \
++ "$SDK/src/game/server/hl2mp/openvibe_js_server.h"
++copy_file "$ROOT/sdk/openvibe/server/hl2mp/openvibe_js_server.cpp" \
++ "$SDK/src/game/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"
++
++CLIENT_VPC="$SDK/src/game/client/client_hl2mp.vpc"
++SERVER_VPC="$SDK/src/game/server/server_hl2mp.vpc"
++HL2MP_CLIENT="$SDK/src/game/server/hl2mp/hl2mp_client.cpp"
++HL2MP_CLIENTMODE="$SDK/src/game/client/hl2mp/clientmode_hl2mpnormal.cpp"
++GAMEINTERFACE="$SDK/src/game/server/gameinterface.cpp"
++HL2MP_PLAYER="$SDK/src/game/server/hl2mp/hl2mp_player.cpp"
++
++perl -0pi -e '
++ s/^.*hl2mp\\openvibe_client\.cpp.*\n//mg;
++ s/^.*hl2mp\\vgui_openvibe_menu\.cpp.*\n//mg;
++ s/(\$File\s+"hl2mp\\clientmode_hl2mpnormal\.h"\n)/$1\t\t\t\$File\t"hl2mp\\openvibe_client.cpp"\n\t\t\t\$File\t"hl2mp\\vgui_openvibe_menu.cpp"\n/s;
++' "$CLIENT_VPC"
++echo "[openvibe-sdk] patched client_hl2mp.vpc"
++
++perl -0pi -e '
++ s/^.*hl2mp\\openvibe_server\.cpp.*\n//mg;
++ s/^.*hl2mp\\openvibe_js_server\.cpp.*\n//mg;
++ s/^.*openvibe\\ov_js_runtime\.cpp.*\n//mg;
++ s/^.*openvibe\\ov_js_bindings\.cpp.*\n//mg;
++ s/^.*openvibe\\ov_js_player\.cpp.*\n//mg;
++ s/^.*quickjs\\quickjs\.c.*\n//mg;
++ s/^.*quickjs\\libregexp\.c.*\n//mg;
++ s/^.*quickjs\\libunicode\.c.*\n//mg;
++ s/^.*quickjs\\cutils\.c.*\n//mg;
++ s/^.*quickjs\\dtoa\.c.*\n//mg;
++ s/^.*quickjs\\libbf\.c.*\n//mg;
++ s/^.*libquickjs_openvibe(?:\.a)?".*\n//mg;
++ s/(\$File\s+"hl2mp\\hl2mp_player\.h"\n)/$1\t\t\t\$File\t"hl2mp\\openvibe_server.cpp"\n\t\t\t\$File\t"hl2mp\\openvibe_js_server.cpp"\n\t\t\t\$File\t"..\\shared\\openvibe\\ov_js_runtime.cpp"\n\t\t\t\$File\t"..\\shared\\openvibe\\ov_js_bindings.cpp"\n\t\t\t\$File\t"..\\shared\\openvibe\\ov_js_player.cpp"\n\t\t\t\$Lib\t"..\\shared\\openvibe\\third_party\\quickjs\\build\\libquickjs_openvibe"\n/s;
++' "$SERVER_VPC"
++echo "[openvibe-sdk] patched server_hl2mp.vpc"
++
++if ! grep -q 'OpenVibe_OnClientActive' "$HL2MP_CLIENT"; then
++ sed -i '/void Host_Say/a void OpenVibe_OnClientActive( CHL2MP_Player *pPlayer );' "$HL2MP_CLIENT"
++ sed -i '/FinishClientPutInServer( pPlayer );/a \ OpenVibe_OnClientActive( pPlayer );' "$HL2MP_CLIENT"
++ echo "[openvibe-sdk] patched hl2mp_client.cpp arrival hook"
++fi
++
++perl -0pi -e 's/[^\S\r\n]*\x0boid OpenVibe_OnClientActive/void OpenVibe_OnClientActive/g; s/\\tOpenVibe_OnClientActive/\tOpenVibe_OnClientActive/g; s/^[ \t]*void OpenVibe_OnClientActive/void OpenVibe_OnClientActive/m' "$HL2MP_CLIENT"
++
++if ! grep -q 'OpenVibe_OnClientModeInit' "$HL2MP_CLIENTMODE"; then
++ sed -i '/#include "ienginevgui.h"/a void OpenVibe_OnClientModeInit();' "$HL2MP_CLIENTMODE"
++ sed -i '/BaseClass::Init();/a \ OpenVibe_OnClientModeInit();' "$HL2MP_CLIENTMODE"
++ echo "[openvibe-sdk] patched clientmode_hl2mpnormal.cpp OpenVibe menu hook"
++fi
++
++perl -0pi -e 's/^[ \t]*tOpenVibe_OnClientModeInit\(\);/ OpenVibe_OnClientModeInit();/m' "$HL2MP_CLIENTMODE"
++
++if [[ -f "$GAMEINTERFACE" ]]; then
++ if ! grep -q 'OpenVibe_OnFrame' "$GAMEINTERFACE"; then
++ sed -i '/CServerGameDLL::GameFrame/i #ifdef HL2MP\nvoid OpenVibe_OnFrame();\n#endif' "$GAMEINTERFACE"
++ sed -i '/VPROF( "CServerGameDLL::GameFrame" );/a #ifdef HL2MP\n\tOpenVibe_OnFrame();\n#endif' "$GAMEINTERFACE"
++ echo "[openvibe-sdk] patched gameinterface.cpp frame hook"
++ fi
++
++ if ! grep -q 'OpenVibe_OnClientDisconnect' "$GAMEINTERFACE"; then
++ perl -pi -e 's/void CServerGameClients::ClientDisconnect/#ifdef HL2MP\nvoid OpenVibe_OnClientDisconnect( CBasePlayer *pPlayer );\n#endif\nvoid CServerGameClients::ClientDisconnect/' "$GAMEINTERFACE"
++ perl -0777 -pi -e 's/(void CServerGameClients::ClientDisconnect\( edict_t \*pEdict \)\s*\{\s*extern bool\s+g_fGameOver;\s*CBasePlayer \*player = \( CBasePlayer \* \)CBaseEntity::Instance\( pEdict \);)/$1\n#ifdef HL2MP\n\tif ( player ) { OpenVibe_OnClientDisconnect( player ); }\n#endif/g' "$GAMEINTERFACE"
++ echo "[openvibe-sdk] patched gameinterface.cpp disconnect hook"
++ fi
++fi
++
++if [[ -f "$HL2MP_PLAYER" ]]; then
++ if ! grep -q 'OpenVibe_OnPlayerDeath' "$HL2MP_PLAYER"; then
++ sed -i '/CHL2MP_Player::Event_Killed/i void OpenVibe_OnPlayerDeath( CHL2MP_Player *pPlayer, CBaseEntity *pKiller );' "$HL2MP_PLAYER"
++ sed -i '/CTakeDamageInfo subinfo = info;/i \ OpenVibe_OnPlayerDeath( this, info.GetAttacker() );' "$HL2MP_PLAYER"
++ echo "[openvibe-sdk] patched hl2mp_player.cpp death hook"
++ fi
++
++ if ! grep -q 'OpenVibeJS_Server_PlayerSpawn' "$HL2MP_PLAYER"; then
++ sed -i '/CHL2MP_Player::Spawn/i void OpenVibeJS_Server_PlayerSpawn( CHL2MP_Player *pPlayer );' "$HL2MP_PLAYER"
++ sed -i '0,/BaseClass::Spawn();/s//BaseClass::Spawn();\n\tOpenVibeJS_Server_PlayerSpawn( this );/' "$HL2MP_PLAYER"
++ echo "[openvibe-sdk] patched hl2mp_player.cpp spawn hook"
++ fi
++fi
++
++if [[ -f "$HL2MP_CLIENT" ]] && ! grep -q 'OpenVibeJS_Server_PlayerSay' "$HL2MP_CLIENT"; then
++ sed -i '/void Host_Say/a bool OpenVibeJS_Server_PlayerSay( CHL2MP_Player *pPlayer, const char *pszText );' "$HL2MP_CLIENT"
++ python3 - "$HL2MP_CLIENT" <<'PY'
++from pathlib import Path
++import re, sys
++
++p = Path(sys.argv[1])
++s = p.read_text()
++
++if "OpenVibeJS_Server_PlayerSay( client," not in s:
++ s2 = re.sub(
++ r'(const char \*p\s*=\s*args\.ArgS\(\);\s*)',
++ r'\1\n\tif ( client && OpenVibeJS_Server_PlayerSay( client, p ) )\n\t\treturn;\n',
++ s,
++ count=1,
++ )
++ if s2 != s:
++ s = s2
++ print("[openvibe-sdk] patched hl2mp_client.cpp PlayerSay hook")
++ else:
++ print("[openvibe-sdk] WARNING: could not auto-patch Host_Say PlayerSay body")
++p.write_text(s)
++PY
++fi
++
++echo "[openvibe-sdk] Source SDK OpenVibe patch applied"
++APPLY
++
++chmod +x tools/apply-openvibe-sdk.sh
++
++echo "[openvibe] remove legacy Node bridge from sdk/openvibe/server/hl2mp/openvibe_server.cpp"
++
++backup_file sdk/openvibe/server/hl2mp/openvibe_server.cpp
++
++python3 <<'PY'
++from pathlib import Path
++import re
++
++p = Path("sdk/openvibe/server/hl2mp/openvibe_server.cpp")
++s = p.read_text()
++
++if '#include "openvibe_js_server.h"' not in s:
++ s = re.sub(
++ r'(#include\s+"hl2mp_player\.h"\s*\n)',
++ r'\1#include "openvibe_js_server.h"\n',
++ s,
++ count=1,
++ )
++
++start = s.find("// =========================================")
++bridge = s.find("OpenVibe.JS - Scripting Engine C++ Bridge")
++if start != -1 and bridge != -1 and start < bridge:
++ end = s.find("void OpenVibe_OnClientDisconnect", bridge)
++ if end != -1:
++ s = s[:start].rstrip() + "\n\n" + s[end:]
++ print("[openvibe] removed legacy OpenVibe.JS Node bridge block")
++
++s = re.sub(r'\n\s*static\s+COpenVibeJsBridge\s+g_OpenVibeJsBridge\s*;\s*\n', '\n', s)
++
++legacy_cmd = re.compile(
++ r'\nstatic\s+void\s+OV_JsCmd_f\s*\([^)]*\)\s*\{.*?\nstatic\s+ConCommand\s+ov_js_cmd\s*\(.*?\);\s*',
++ re.S,
++)
++s2 = legacy_cmd.sub('\n', s)
++if s2 != s:
++ s = s2
++ print("[openvibe] removed legacy ov_js_cmd command")
++
++def replace_func(src, name, args, body):
++ m = re.search(rf'void\s+{name}\s*\({args}\)\s*\{{', src)
++ if not m:
++ return src
++ brace = src.find("{", m.end() - 1)
++ depth = 0
++ end = None
++ for i in range(brace, len(src)):
++ if src[i] == "{":
++ depth += 1
++ elif src[i] == "}":
++ depth -= 1
++ if depth == 0:
++ end = i
++ break
++ if end is None:
++ return src
++ return src[:brace + 1] + "\n" + body.rstrip() + "\n" + src[end:]
++
++s = replace_func(
++ s,
++ "OpenVibe_OnClientDisconnect",
++ r'\s*CBasePlayer\s*\*\s*pPlayer\s*',
++ '''
++ CHL2MP_Player *pHL2MP = ToHL2MPPlayer( pPlayer );
++ if ( pHL2MP )
++ OpenVibeJS_Server_PlayerDisconnected( pHL2MP );
++'''
++)
++
++s = replace_func(
++ s,
++ "OpenVibe_OnPlayerDeath",
++ r'\s*CHL2MP_Player\s*\*\s*pPlayer\s*,\s*CBaseEntity\s*\*\s*pKiller\s*',
++ '''
++ OpenVibeJS_Server_PlayerDeath( pPlayer, pKiller, NULL );
++'''
++)
++
++s = replace_func(
++ s,
++ "OpenVibe_OnFrame",
++ r'\s*',
++ '''
++ OpenVibeJS_ServerThink();
++'''
++)
++
++if "OpenVibeJS_Server_PlayerInitialSpawn( pPlayer );" not in s:
++ m = re.search(r'void\s+OpenVibe_OnClientActive\s*\(\s*CHL2MP_Player\s*\*\s*pPlayer\s*\)\s*\{', s)
++ if m:
++ brace = s.find("{", m.end() - 1)
++ 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
++ break
++ if end is not None:
++ s = s[:end] + "\n OpenVibeJS_Server_PlayerInitialSpawn( pPlayer );\n" + s[end:]
++
++p.write_text(s)
++PY
++
++echo "[openvibe] add embedded JS console commands to openvibe_js_server.cpp"
++
++backup_file sdk/openvibe/server/hl2mp/openvibe_js_server.cpp
++
++python3 <<'PY'
++from pathlib import Path
++import re
++
++p = Path("sdk/openvibe/server/hl2mp/openvibe_js_server.cpp")
++s = p.read_text()
++
++s = re.sub(r'\nstatic void OV_JSStatus_f\(\).*?static ConCommand ov_js_status\s*\(.*?\);\s*', '\n', s, flags=re.S)
++s = re.sub(r'\nstatic void OV_JSFire_f\(.*?static ConCommand ov_js_fire\s*\(.*?\);\s*', '\n', s, flags=re.S)
++s = re.sub(r'\nstatic void OV_JSCmd_f\(.*?static ConCommand ov_js_cmd\s*\(.*?\);\s*', '\n', s, flags=re.S)
++
++block = r'''
++static void OV_JSStatus_f()
++{
++ Msg("[OV JS] enabled=%d running=%d mode=%s\n",
++ ov_js_enabled.GetBool() ? 1 : 0,
++ OpenVibeJS_IsRunning() ? 1 : 0,
++ ov_mode.GetString());
++}
++
++static ConCommand ov_js_status(
++ "ov_js_status",
++ OV_JSStatus_f,
++ "Print OpenVibe JavaScript runtime status.",
++ FCVAR_GAMEDLL
++);
++
++static void OV_JSFire_f(const CCommand &args)
++{
++ if (args.ArgC() < 2)
++ {
++ Msg("Usage: ov_js_fire <HookName>\n");
++ return;
++ }
++
++ OpenVibeJS_EnsureStarted();
++
++ if (!OpenVibeJS_IsRunning())
++ {
++ Warning("[OV JS] runtime is not running.\n");
++ return;
++ }
++
++ g_OVServerJS.CallHookVoid(args[1]);
++}
++
++static ConCommand ov_js_fire(
++ "ov_js_fire",
++ OV_JSFire_f,
++ "Fire an OpenVibe JavaScript hook with no arguments.",
++ FCVAR_GAMEDLL
++);
++
++static void OV_JSCmd_f(const CCommand &args)
++{
++ OpenVibeJS_EnsureStarted();
++
++ if (!OpenVibeJS_IsRunning())
++ {
++ Warning("[OV JS] runtime is not running.\n");
++ return;
++ }
++
++ JSContext *ctx = g_OVServerJS.Context();
++ JSValue payload = JS_NewString(ctx, args.ArgS() ? args.ArgS() : "");
++
++ JSValueConst argv[] = { payload };
++ g_OVServerJS.CallHookVoid("ConsoleCommand", 1, argv);
++
++ JS_FreeValue(ctx, payload);
++}
++
++static ConCommand ov_js_cmd(
++ "ov_js_cmd",
++ OV_JSCmd_f,
++ "Send text to the OpenVibe embedded JavaScript ConsoleCommand hook.",
++ FCVAR_GAMEDLL
++);
++'''
++
++s = s.rstrip() + "\n\n" + block.lstrip()
++p.write_text(s)
++PY
++
++echo "[openvibe] normalize base/hub JS ConsoleCommand handlers"
++
++cat > game/openvibe.games/js/gamemodes/base/server.js <<'JS_BASE'
++const GM = {
++ mode: "base",
++ name: "OpenVibe Base",
++
++ Initialize() {
++ OV.log("Base Initialize fired");
++ },
++
++ MapInitialize(mapName) {
++ OV.log(`Base MapInitialize: ${mapName}`);
++ },
++
++ PlayerInitialSpawn(ply) {
++ ply.chat("Welcome to OpenVibe: Source.");
++ },
++
++ PlayerSpawn(_ply) {},
++
++ PlayerDeath(_victim, _attacker) {},
++
++ PlayerDisconnected(_ply) {},
++
++ PlayerSay(_ply, _text) {
++ return undefined;
++ },
++
++ ConsoleCommand(text) {
++ OV.log(`ConsoleCommand: ${text}`);
++ return undefined;
++ },
++
++ Think() {}
++};
++
++gamemode.set(GM);
++JS_BASE
++
++cat > game/openvibe.games/js/gamemodes/hub/server.js <<'JS_HUB'
++const GM = {
++ mode: "hub",
++ name: "OpenVibe Hub",
++
++ Initialize() {
++ OV.log("Hub Initialize fired");
++ },
++
++ MapInitialize(mapName) {
++ OV.log(`Map initialized: ${mapName}`);
++ },
++
++ PlayerInitialSpawn(ply) {
++ ply.chat("Welcome to OpenVibe: Source JS runtime.");
++ OV.broadcast(`${ply.name()} joined the hub.`);
++ },
++
++ PlayerSpawn(ply) {
++ ply.chat("PlayerSpawn hook fired.");
++ },
++
++ PlayerSay(ply, text) {
++ if (text === "!js") {
++ ply.chat("JavaScript hooks are working.");
++ return false;
++ }
++
++ if (text === "!hp") {
++ ply.chat(`Health: ${ply.health()}`);
++ return false;
++ }
++
++ if (text === "!players") {
++ ply.chat(`Players online: ${OV.players().length}`);
++ return false;
++ }
++
++ return undefined;
++ },
++
++ ConsoleCommand(text) {
++ OV.log(`Hub ConsoleCommand: ${text}`);
++
++ if (text === "smoke") {
++ OV.broadcast("OpenVibe embedded JS smoke test passed.");
++ return false;
++ }
++
++ return undefined;
++ },
++
++ Think() {}
++};
++
++gamemode.set(GM);
++JS_HUB
++
++echo "[openvibe] apply SDK patch"
++tools/apply-openvibe-sdk.sh
++
++if [[ "$RUN_BUILD" = "1" ]]; then
++ echo "[openvibe] run SDK build"
++ tools/build-sdk-linux.sh 2>&1 | tee "$HOME/ov-build.log"
++ tail -80 "$HOME/ov-build.log"
++else
++ echo "[openvibe] skipping build because RUN_BUILD=$RUN_BUILD"
++fi
++
++if [[ "$RUN_SETUP" = "1" && -x tools/setup-openvibe-bin.sh ]]; then
++ echo "[openvibe] setup OpenVibe binaries"
++ tools/setup-openvibe-bin.sh
++fi
++
++echo
++echo "[openvibe] done."
++echo
++echo "Next manual test:"
++echo " OPENVIBE_SRCDS_MAP_DELAY=3 tools/dev-up.sh"
++echo
++echo "In server console:"
++echo " ov_js_status"
++echo " ov_js_fire Initialize"
++echo " ov_js_cmd smoke"
++echo
++echo "In client:"
++echo " connect 127.0.0.1:27015"
++echo " say !js"
++echo " say !hp"
++echo " say !players"
ddiff --git a/tools/fix-duplicate-ov-js-cmd.sh b/tools/fix-duplicate-ov-js-cmd.sh
nnew file mode 100755
iindex 0000000..9701d85
---- /dev/null
++++ b/tools/fix-duplicate-ov-js-cmd.sh
@@@ -0,0 +1,441 @@
++#!/usr/bin/env bash
++set -euo pipefail
++
++ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
++SDK="${OPENVIBE_SDK:-$ROOT/engine/source-sdk-2013}"
++STAMP="$(date +%Y%m%d-%H%M%S)"
++
++cd "$ROOT"
++
++backup_file() {
++ local file="$1"
++ [[ -f "$file" ]] && cp "$file" "$file.bak.$STAMP"
++}
++
++echo "[openvibe] fixing duplicate ov_js_cmd and canonicalizing embedded JS server bridge"
++
++mkdir -p sdk/openvibe/server/hl2mp
++
++backup_file sdk/openvibe/server/hl2mp/openvibe_js_server.cpp
++backup_file sdk/openvibe/server/hl2mp/openvibe_server.cpp
++
++cat > sdk/openvibe/server/hl2mp/openvibe_js_server.cpp <<'CPP'
++#include "cbase.h"
++#include "hl2mp_player.h"
++#include "openvibe_js_server.h"
++#include "openvibe/ov_js_runtime.h"
++#include "openvibe/ov_js_player.h"
++
++#include "tier0/memdbgon.h"
++
++static ConVar ov_mode(
++ "ov_mode",
++ "hub",
++ FCVAR_GAMEDLL,
++ "OpenVibe mode: hub, prophunt, deathrun, fortwars, traitortown." );
++
++static ConVar ov_js_enabled(
++ "ov_js_enabled",
++ "1",
++ FCVAR_GAMEDLL,
++ "Enable OpenVibe JavaScript runtime." );
++
++static COpenVibeJSRuntime g_OVServerJS;
++static bool g_OVServerJSStarted = false;
++
++static bool OpenVibeJS_IsRunning()
++{
++ return ov_js_enabled.GetBool() && g_OVServerJS.Context() != nullptr;
++}
++
++static void OpenVibeJS_EnsureStarted()
++{
++ if ( g_OVServerJSStarted )
++ return;
++
++ g_OVServerJSStarted = true;
++
++ if ( !ov_js_enabled.GetBool() )
++ {
++ Msg( "[OV JS] disabled by ov_js_enabled=0\n" );
++ return;
++ }
++
++ if ( g_OVServerJS.Init( true, ov_mode.GetString() ) )
++ {
++ Msg( "[OV JS] server runtime initialized for mode '%s'\n", ov_mode.GetString() );
++ g_OVServerJS.CallHookVoid( "Initialize" );
++
++ JSContext *ctx = g_OVServerJS.Context();
++ JSValue mapName = JS_NewString( ctx, gpGlobals ? STRING( gpGlobals->mapname ) : "" );
++ JSValueConst argv[] = { mapName };
++ g_OVServerJS.CallHookVoid( "MapInitialize", 1, argv );
++ JS_FreeValue( ctx, mapName );
++ }
++ else
++ {
++ Warning( "[OV JS] server runtime failed to initialize\n" );
++ }
++}
++
++static void OpenVibeJS_CallConsoleCommand( const char *pszCommand )
++{
++ OpenVibeJS_EnsureStarted();
++
++ if ( !OpenVibeJS_IsRunning() )
++ {
++ Warning( "[OV JS] runtime is not running; command ignored: %s\n", pszCommand ? pszCommand : "" );
++ return;
++ }
++
++ JSContext *ctx = g_OVServerJS.Context();
++ JSValue command = JS_NewString( ctx, pszCommand ? pszCommand : "" );
++ JSValueConst argv[] = { command };
++
++ g_OVServerJS.CallHookVoid( "ConsoleCommand", 1, argv );
++
++ JS_FreeValue( ctx, command );
++}
++
++void OpenVibeJS_ServerInit()
++{
++ OpenVibeJS_EnsureStarted();
++}
++
++void OpenVibeJS_ServerShutdown()
++{
++ if ( OpenVibeJS_IsRunning() )
++ g_OVServerJS.CallHookVoid( "Shutdown" );
++
++ g_OVServerJS.Shutdown();
++ g_OVServerJSStarted = false;
++}
++
++void OpenVibeJS_ServerThink()
++{
++ OpenVibeJS_EnsureStarted();
++
++ if ( !OpenVibeJS_IsRunning() )
++ return;
++
++ g_OVServerJS.CallHookVoid( "Think" );
++}
++
++void OpenVibeJS_Server_PlayerInitialSpawn( CHL2MP_Player *player )
++{
++ OpenVibeJS_EnsureStarted();
++
++ if ( !OpenVibeJS_IsRunning() || !player )
++ return;
++
++ JSContext *ctx = g_OVServerJS.Context();
++ JSValue ply = OVJS_NewPlayer( ctx, player );
++
++ JSValueConst argv[] = { ply };
++ g_OVServerJS.CallHookVoid( "PlayerInitialSpawn", 1, argv );
++
++ JS_FreeValue( ctx, ply );
++}
++
++void OpenVibeJS_Server_PlayerSpawn( CHL2MP_Player *player )
++{
++ OpenVibeJS_EnsureStarted();
++
++ if ( !OpenVibeJS_IsRunning() || !player )
++ return;
++
++ JSContext *ctx = g_OVServerJS.Context();
++ JSValue ply = OVJS_NewPlayer( ctx, player );
++
++ JSValueConst argv[] = { ply };
++ g_OVServerJS.CallHookVoid( "PlayerSpawn", 1, argv );
++
++ JS_FreeValue( ctx, ply );
++}
++
++void OpenVibeJS_Server_PlayerDeath( CHL2MP_Player *victim, CBaseEntity *attacker, CBaseEntity *inflictor )
++{
++ OpenVibeJS_EnsureStarted();
++
++ if ( !OpenVibeJS_IsRunning() || !victim )
++ return;
++
++ JSContext *ctx = g_OVServerJS.Context();
++
++ JSValue jsVictim = OVJS_NewPlayer( ctx, victim );
++ JSValue jsAttacker = JS_NULL;
++
++ if ( attacker && attacker->IsPlayer() )
++ jsAttacker = OVJS_NewPlayer( ctx, ToHL2MPPlayer( static_cast<CBasePlayer *>( attacker ) ) );
++
++ JSValueConst argv[] = { jsVictim, jsAttacker };
++ g_OVServerJS.CallHookVoid( "PlayerDeath", 2, argv );
++
++ JS_FreeValue( ctx, jsVictim );
++ JS_FreeValue( ctx, jsAttacker );
++}
++
++void OpenVibeJS_Server_PlayerDisconnected( CHL2MP_Player *player )
++{
++ OpenVibeJS_EnsureStarted();
++
++ if ( !OpenVibeJS_IsRunning() || !player )
++ return;
++
++ JSContext *ctx = g_OVServerJS.Context();
++ JSValue ply = OVJS_NewPlayer( ctx, player );
++
++ JSValueConst argv[] = { ply };
++ g_OVServerJS.CallHookVoid( "PlayerDisconnected", 1, argv );
++
++ JS_FreeValue( ctx, ply );
++}
++
++bool OpenVibeJS_Server_PlayerSay( CHL2MP_Player *player, const char *text )
++{
++ OpenVibeJS_EnsureStarted();
++
++ if ( !OpenVibeJS_IsRunning() || !player || !text )
++ return false;
++
++ JSContext *ctx = g_OVServerJS.Context();
++
++ JSValue ply = OVJS_NewPlayer( ctx, player );
++ JSValue msg = JS_NewString( ctx, text );
++
++ JSValueConst argv[] = { ply, msg };
++
++ bool value = false;
++ bool returned = g_OVServerJS.CallHookBool( "PlayerSay", &value, 2, argv );
++
++ JS_FreeValue( ctx, ply );
++ JS_FreeValue( ctx, msg );
++
++ return returned && value == false;
++}
++
++static void OV_JSReload_f()
++{
++ OpenVibeJS_ServerShutdown();
++ OpenVibeJS_ServerInit();
++ Msg( "[OV JS] reloaded\n" );
++}
++
++static ConCommand ov_js_reload(
++ "ov_js_reload",
++ OV_JSReload_f,
++ "Reload OpenVibe JavaScript runtime.",
++ FCVAR_GAMEDLL
++);
++
++static void OV_JSStatus_f()
++{
++ Msg( "[OV JS] enabled=%d started=%d running=%d mode=%s\n",
++ ov_js_enabled.GetBool() ? 1 : 0,
++ g_OVServerJSStarted ? 1 : 0,
++ OpenVibeJS_IsRunning() ? 1 : 0,
++ ov_mode.GetString() );
++}
++
++static ConCommand ov_js_status(
++ "ov_js_status",
++ OV_JSStatus_f,
++ "Print OpenVibe JavaScript runtime status.",
++ FCVAR_GAMEDLL
++);
++
++static void OV_JSFire_f( const CCommand &args )
++{
++ if ( args.ArgC() < 2 )
++ {
++ Msg( "Usage: ov_js_fire <HookName>\n" );
++ return;
++ }
++
++ OpenVibeJS_EnsureStarted();
++
++ if ( !OpenVibeJS_IsRunning() )
++ {
++ Warning( "[OV JS] runtime is not running\n" );
++ return;
++ }
++
++ g_OVServerJS.CallHookVoid( args[1] );
++}
++
++static ConCommand ov_js_fire(
++ "ov_js_fire",
++ OV_JSFire_f,
++ "Fire an OpenVibe JavaScript hook with no arguments.",
++ FCVAR_GAMEDLL
++);
++
++static void OV_JSCmd_f( const CCommand &args )
++{
++ if ( args.ArgC() < 2 )
++ {
++ Msg( "Usage: ov_js_cmd <text>\n" );
++ return;
++ }
++
++ OpenVibeJS_CallConsoleCommand( args.ArgS() );
++}
++
++static ConCommand ov_js_cmd(
++ "ov_js_cmd",
++ OV_JSCmd_f,
++ "Send a ConsoleCommand event into the embedded OpenVibe JavaScript runtime.",
++ FCVAR_GAMEDLL
++);
++CPP
++
++echo "[openvibe] canonical openvibe_js_server.cpp written"
++
++echo "[openvibe] removing stale legacy Node bridge remnants from openvibe_server.cpp"
++
++python3 <<'PY'
++from pathlib import Path
++import re
++
++p = Path("sdk/openvibe/server/hl2mp/openvibe_server.cpp")
++s = p.read_text()
++
++# Remove legacy COpenVibeJsBridge class, if present.
++s = re.sub(
++ r'\n// ={10,}\n// OpenVibe\.JS - Scripting Engine C\+\+ Bridge\n// ={10,}\n\nclass COpenVibeJsBridge\b.*?\nstatic COpenVibeJsBridge g_OpenVibeJsBridge;\n',
++ '\n',
++ s,
++ flags=re.S,
++)
++
++# More tolerant fallback if banner text differs.
++s = re.sub(
++ r'\nclass COpenVibeJsBridge\b.*?\nstatic COpenVibeJsBridge g_OpenVibeJsBridge;\n',
++ '\n',
++ s,
++ flags=re.S,
++)
++
++# Remove old Node-bridge ov_js_cmd command if it survived.
++s = re.sub(
++ r'\nstatic void OV_JsCmd_f\s*\([^)]*\)\s*\{.*?\n\}\n\nstatic ConCommand ov_js_cmd\s*\(.*?\);\n',
++ '\n',
++ s,
++ flags=re.S,
++)
++
++# Remove obvious unused Node/socket includes from our tracked patch file.
++for inc in [
++ '#include <thread>\n',
++ '#include <mutex>\n',
++ '#include <queue>\n',
++ '#include <string>\n',
++ '#include <sstream>\n',
++ '#include <sys/socket.h>\n',
++ '#include <sys/un.h>\n',
++ '#include <unistd.h>\n',
++]:
++ s = s.replace(inc, '')
++
++# Ensure embedded JS include exists.
++if '#include "openvibe_js_server.h"' not in s:
++ s = re.sub(
++ r'(#include\s+"hl2mp_player\.h"\s*\n)',
++ r'\1#include "openvibe_js_server.h"\n',
++ s,
++ count=1,
++ )
++
++p.write_text(s)
++PY
++
++echo "[openvibe] removing duplicate ov_js_cmd from already-applied SDK file if present"
++
++SDK_SERVER_JS="$SDK/src/game/server/hl2mp/openvibe_js_server.cpp"
++if [[ -f "$SDK_SERVER_JS" ]]; then
++ cp "$SDK_SERVER_JS" "$SDK_SERVER_JS.bak.$STAMP"
++ cp sdk/openvibe/server/hl2mp/openvibe_js_server.cpp "$SDK_SERVER_JS"
++fi
++
++echo "[openvibe] ensure JS ConsoleCommand handlers are present"
++
++mkdir -p game/openvibe.games/js/gamemodes/base game/openvibe.games/js/gamemodes/hub
++
++python3 <<'PY'
++from pathlib import Path
++
++def ensure_console_handler(path: str, marker: str, handler_src: str):
++ p = Path(path)
++ s = p.read_text() if p.exists() else "const GM = {\n};\n\ngamemode.set(GM);\n"
++ if marker in s:
++ return
++ idx = s.rfind("};")
++ if idx == -1:
++ s += "\n" + handler_src + "\n"
++ else:
++ before = s[:idx].rstrip()
++ after = s[idx:]
++ if before.endswith("{"):
++ before = before + "\n" + handler_src.strip()
++ else:
++ before = before + ",\n\n" + handler_src.strip()
++ s = before + "\n" + after
++ p.write_text(s)
++
++ensure_console_handler(
++ "game/openvibe.games/js/gamemodes/base/server.js",
++ "Base ConsoleCommand",
++ '''ConsoleCommand(text) {
++ OV.log(`Base ConsoleCommand: ${text}`);
++ }'''
++)
++
++ensure_console_handler(
++ "game/openvibe.games/js/gamemodes/hub/server.js",
++ "Hub ConsoleCommand",
++ '''ConsoleCommand(text) {
++ OV.log(`Hub ConsoleCommand: ${text}`);
++ if (text === "smoke") {
++ OV.broadcast("Embedded JS smoke command worked.");
++ }
++ }'''
++)
++PY
++
++echo "[openvibe] apply SDK patch"
++tools/apply-openvibe-sdk.sh
++
++echo "[openvibe] verify only one ov_js_cmd exists in SDK copied source"
++grep -n "ConCommand ov_js_cmd" "$SDK/src/game/server/hl2mp/openvibe_js_server.cpp" || true
++count="$(grep -c "ConCommand ov_js_cmd" "$SDK/src/game/server/hl2mp/openvibe_js_server.cpp" || true)"
++if [[ "$count" != "1" ]]; then
++ echo "[openvibe] ERROR: expected exactly one ov_js_cmd, got $count" >&2
++ exit 1
++fi
++
++echo "[openvibe] build SDK"
++tools/build-sdk-linux.sh 2>&1 | tee "$HOME/ov-build.log"
++
++echo "[openvibe] setup OpenVibe bin"
++if [[ -x tools/setup-openvibe-bin.sh ]]; then
++ tools/setup-openvibe-bin.sh
++else
++ echo "[openvibe] WARNING: tools/setup-openvibe-bin.sh not found/executable; skipping"
++fi
++
++echo
++echo "[openvibe] phase build complete."
++echo
++echo "Next runtime test:"
++echo " OPENVIBE_SRCDS_MAP_DELAY=3 tools/dev-up.sh"
++echo
++echo "Server console tests:"
++echo " ov_js_status"
++echo " ov_js_fire Initialize"
++echo " ov_js_cmd smoke"
++echo
++echo "Client tests:"
++echo " connect 127.0.0.1:27015"
++echo " say !js"
++echo " say !hp"
++echo " say !players"
ddiff --git a/tools/next-openvibe-embedded-js-cleanup.sh b/tools/next-openvibe-embedded-js-cleanup.sh
nnew file mode 100755
iindex 0000000..8c30efc
---- /dev/null
++++ b/tools/next-openvibe-embedded-js-cleanup.sh
@@@ -0,0 +1,383 @@
++#!/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)"
++RUN_BUILD="${RUN_BUILD:-1}"
++
++cd "$ROOT"
++
++backup_file() {
++ local file="$1"
++ [[ -f "$file" ]] && cp "$file" "$file.bak.$STAMP"
++}
++
++need_file() {
++ [[ -f "$1" ]] || { echo "[openvibe] missing required file: $1" >&2; exit 1; }
++}
++
++need_file "sdk/openvibe/server/hl2mp/openvibe_server.cpp"
++need_file "sdk/openvibe/server/hl2mp/openvibe_js_server.cpp"
++need_file "sdk/openvibe/server/hl2mp/openvibe_js_server.h"
++need_file "tools/apply-openvibe-sdk.sh"
++need_file "tools/build-quickjs-lib.sh"
++
++echo "[openvibe] next phase: remove legacy Node bridge, add embedded JS console hooks, rebuild"
++
++mkdir -p tools
++
++backup_file "sdk/openvibe/server/hl2mp/openvibe_server.cpp"
++backup_file "sdk/openvibe/server/hl2mp/openvibe_js_server.cpp"
++backup_file "sdk/openvibe/server/hl2mp/openvibe_js_server.h"
++backup_file "tools/apply-openvibe-sdk.sh"
++
++python3 <<'PY'
++from pathlib import Path
++import re
++
++p = Path("sdk/openvibe/server/hl2mp/openvibe_server.cpp")
++s = p.read_text()
++
++# Make sure the embedded JS bridge header is included.
++if '#include "openvibe_js_server.h"' not in s:
++ s = re.sub(
++ r'(#include\s+"hl2mp_player\.h"\s*\n)',
++ r'\1#include "openvibe_js_server.h"\n',
++ s,
++ count=1,
++ )
++
++# Remove legacy Node/Unix-socket bridge includes that are no longer needed.
++for inc in [
++ '#include <thread>',
++ '#include <mutex>',
++ '#include <queue>',
++ '#include <string>',
++ '#include <condition_variable>',
++ '#include <sys/socket.h>',
++ '#include <sys/un.h>',
++ '#include <unistd.h>',
++ '#include <fcntl.h>',
++ '#include <sstream>',
++ '#include <sys/select.h>',
++ '#include <sys/time.h>',
++]:
++ s = s.replace(inc + "\n", "")
++
++bottom = r'''
++// ======================================\n// OpenVibe embedded JavaScript hook bridge\n// ======================================
++
++void OpenVibe_OnClientDisconnect( CBasePlayer *pPlayer )
++{
++ CHL2MP_Player *pHL2MP = ToHL2MPPlayer( pPlayer );
++ if ( pHL2MP )
++ OpenVibeJS_Server_PlayerDisconnected( pHL2MP );
++}
++
++void OpenVibe_OnPlayerDeath( CHL2MP_Player *pPlayer, CBaseEntity *pKiller )
++{
++ OpenVibeJS_Server_PlayerDeath( pPlayer, pKiller, NULL );
++}
++
++void OpenVibe_OnFrame()
++{
++ OpenVibeJS_ServerThink();
++}
++'''
++
++# Remove the huge legacy COpenVibeJsBridge class and old ov_js_cmd that spawned Node.
++marker = "// ==========================================\n// OpenVibe.JS - Scripting Engine C++ Bridge"
++start = s.find(marker)
++if start == -1:
++ # The exact banner can vary because of long ===== lines. Fall back to class start.
++ start = s.find("class COpenVibeJsBridge")
++ if start != -1:
++ # Include preceding banner if present.
++ banner = s.rfind("// =================================", 0, start)
++ if banner != -1:
++ start = banner
++
++if start != -1:
++ s = s[:start].rstrip() + "\n\n" + bottom + "\n"
++else:
++ # If already cleaned, normalize the bottom hook functions only.
++ def replace_function(src, regex, body):
++ m = re.search(regex, src)
++ if not m:
++ return src
++ brace = src.find('{', m.end())
++ if brace < 0:
++ return src
++ depth = 0
++ end = None
++ for i in range(brace, len(src)):
++ if src[i] == '{':
++ depth += 1
++ elif src[i] == '}':
++ depth -= 1
++ if depth == 0:
++ end = i
++ break
++ if end is None:
++ return src
++ return src[:brace+1] + "\n" + body.strip() + "\n" + src[end:]
++
++ s = replace_function(s, r'void\s+OpenVibe_OnClientDisconnect\s*\(\s*CBasePlayer\s*\*\s*pPlayer\s*\)',
++ ' CHL2MP_Player *pHL2MP = ToHL2MPPlayer( pPlayer );\n if ( pHL2MP )\n OpenVibeJS_Server_PlayerDisconnected( pHL2MP );')
++ s = replace_function(s, r'void\s+OpenVibe_OnPlayerDeath\s*\(\s*CHL2MP_Player\s*\*\s*pPlayer\s*,\s*CBaseEntity\s*\*\s*pKiller\s*\)',
++ ' OpenVibeJS_Server_PlayerDeath( pPlayer, pKiller, NULL );')
++ s = replace_function(s, r'void\s+OpenVibe_OnFrame\s*\(\s*\)', ' OpenVibeJS_ServerThink();')
++
++p.write_text(s)
++print("[openvibe] cleaned legacy Node bridge from openvibe_server.cpp")
++PY
++
++cat > sdk/openvibe/server/hl2mp/openvibe_js_server.h <<'CPP_H'
++#pragma once
++
++class CHL2MP_Player;
++class CBasePlayer;
++class CBaseEntity;
++
++void OpenVibeJS_ServerInit();
++void OpenVibeJS_ServerShutdown();
++void OpenVibeJS_ServerThink();
++
++void OpenVibeJS_Server_PlayerInitialSpawn(CHL2MP_Player *player);
++void OpenVibeJS_Server_PlayerSpawn(CHL2MP_Player *player);
++void OpenVibeJS_Server_PlayerDeath(CHL2MP_Player *victim, CBaseEntity *attacker, CBaseEntity *inflictor);
++void OpenVibeJS_Server_PlayerDisconnected(CHL2MP_Player *player);
++bool OpenVibeJS_Server_PlayerSay(CHL2MP_Player *player, const char *text);
++
++void OpenVibeJS_Server_ConsoleCommand(const char *text);
++void OpenVibeJS_Server_FireHook(const char *hookName);
++CPP_H
++
++python3 <<'PY'
++from pathlib import Path
++
++p = Path("sdk/openvibe/server/hl2mp/openvibe_js_server.cpp")
++s = p.read_text()
++
++if "OpenVibeJS_Server_ConsoleCommand" not in s:
++ insert = r'''
++void OpenVibeJS_Server_ConsoleCommand(const char *text)
++{
++ OpenVibeJS_EnsureStarted();
++
++ if (!OpenVibeJS_IsRunning() || !text)
++ return;
++
++ JSContext *ctx = g_OVServerJS.Context();
++ JSValue arg = JS_NewString(ctx, text);
++
++ JSValueConst argv[] = { arg };
++ g_OVServerJS.CallHookVoid("ConsoleCommand", 1, argv);
++
++ JS_FreeValue(ctx, arg);
++}
++
++void OpenVibeJS_Server_FireHook(const char *hookName)
++{
++ OpenVibeJS_EnsureStarted();
++
++ if (!OpenVibeJS_IsRunning() || !hookName || !hookName[0])
++ return;
++
++ g_OVServerJS.CallHookVoid(hookName);
++}
++
++static void OV_JsCmd_f(const CCommand &args)
++{
++ if (args.ArgC() < 2)
++ {
++ Msg("Usage: ov_js_cmd <text>\n");
++ return;
++ }
++
++ OpenVibeJS_Server_ConsoleCommand(args.ArgS());
++}
++
++static ConCommand ov_js_cmd(
++ "ov_js_cmd",
++ OV_JsCmd_f,
++ "Send a string to the embedded OpenVibe JS ConsoleCommand hook.",
++ FCVAR_GAMEDLL
++);
++
++static void OV_JSFire_f(const CCommand &args)
++{
++ if (args.ArgC() < 2)
++ {
++ Msg("Usage: ov_js_fire <HookName>\n");
++ return;
++ }
++
++ OpenVibeJS_Server_FireHook(args[1]);
++}
++
++static ConCommand ov_js_fire(
++ "ov_js_fire",
++ OV_JSFire_f,
++ "Fire an embedded OpenVibe JS hook by name.",
++ FCVAR_GAMEDLL
++);
++
++static void OV_JSStatus_f()
++{
++ Msg("[OV JS] enabled=%d running=%d mode=%s map=%s\n",
++ ov_js_enabled.GetBool() ? 1 : 0,
++ OpenVibeJS_IsRunning() ? 1 : 0,
++ ov_mode.GetString(),
++ gpGlobals ? STRING(gpGlobals->mapname) : "" );
++}
++
++static ConCommand ov_js_status(
++ "ov_js_status",
++ OV_JSStatus_f,
++ "Print embedded OpenVibe JS runtime status.",
++ FCVAR_GAMEDLL
++);
++'''
++ # Put commands after ov_js_reload command block.
++ s = s.rstrip() + "\n\n" + insert.lstrip() + "\n"
++
++p.write_text(s)
++print("[openvibe] added embedded ov_js_cmd / ov_js_fire / ov_js_status commands")
++PY
++
++python3 <<'PY'
++from pathlib import Path
++
++for path in [
++ Path("game/openvibe.games/js/gamemodes/base/server.js"),
++ Path("game/openvibe.games/js/gamemodes/hub/server.js"),
++]:
++ if not path.exists():
++ continue
++ s = path.read_text()
++ if "ConsoleCommand(" in s:
++ continue
++
++ snippet = '''
++
++ ConsoleCommand(text) {
++ OV.log(`ConsoleCommand: ${text}`);
++
++ if (text === "smoke" || text === "test") {
++ OV.broadcast("Embedded JS ConsoleCommand hook is working.");
++ return false;
++ }
++
++ return undefined;
++ },
++'''
++
++ marker = "\n Think()"
++ if marker in s:
++ s = s.replace(marker, snippet + marker, 1)
++ else:
++ s = s.replace("\n};", snippet.rstrip(",\n") + "\n};", 1)
++ path.write_text(s)
++ print(f"[openvibe] added ConsoleCommand hook to {path}")
++PY
++
++echo "[openvibe] cleaning apply-openvibe-sdk.sh duplicate stale link block"
++python3 <<'PY'
++from pathlib import Path
++import re
++
++p = Path("tools/apply-openvibe-sdk.sh")
++s = p.read_text()
++
++# Remove stale block after final echo, if present. It is harmless but confusing.
++s = re.sub(
++ r'\n\n# Link prebuilt QuickJS C static library\. QuickJS must be compiled as C, not C\+\+\.\nif ! grep -q '\''libquickjs_openvibe'\'' "\$SERVER_VPC"; then\n perl -0pi -e '\''s/\(\\\$File\\s\+"hl2mp\\\\openvibe_js_server\\\.cpp"\\n\)/\$1\\t\\t\\t\\\$Lib\\t"\.\.\\\\shared\\\\openvibe\\\\third_party\\\\quickjs\\\\build\\\\libquickjs_openvibe"\\n/s'\'' "\$SERVER_VPC"\n echo "\[openvibe-sdk\] linked QuickJS static library"\nfi\n*',
++ '\n',
++ s,
++ flags=re.S,
++)
++
++# Ensure final V2 block exists.
++if "OPENVIBE_FIX_QUICKJS_LINK_FINAL_V2" not in s:
++ final_block = r'''
++
++# OPENVIBE_FIX_QUICKJS_LINK_FINAL_V2
++# QuickJS is C, so do not compile its .c files through Source SDK/VPC C++.
++# We build libquickjs_openvibe.a with cc and link it as a library.
++if [[ -f "$SERVER_VPC" ]]; then
++ perl -0pi -e '
++ s/^.*quickjs\\quickjs\.c.*\n//mg;
++ s/^.*quickjs\\libregexp\.c.*\n//mg;
++ s/^.*quickjs\\libunicode\.c.*\n//mg;
++ s/^.*quickjs\\cutils\.c.*\n//mg;
++ s/^.*quickjs\\dtoa\.c.*\n//mg;
++ s/^.*quickjs\\libbf\.c.*\n//mg;
++ ' "$SERVER_VPC"
++
++ perl -0pi -e 's/^.*libquickjs_openvibe(?:\.a)?".*\n//mg' "$SERVER_VPC"
++
++ perl -0pi -e 's/(\$File\s+"hl2mp\\openvibe_js_server\.cpp"\n)/$1\t\t\t\$Lib\t"..\\shared\\openvibe\\third_party\\quickjs\\build\\libquickjs_openvibe"\n/s' "$SERVER_VPC"
++
++ echo "[openvibe-sdk] linked QuickJS static library"
++fi
++'''
++ s = s.replace('echo "[openvibe-sdk] Source SDK OpenVibe patch applied"', final_block + '\n\necho "[openvibe-sdk] Source SDK OpenVibe patch applied"')
++
++p.write_text(s)
++PY
++
++echo "[openvibe] writing embedded JS runtime smoke helper"
++cat > tools/smoke-embedded-js-runtime.sh <<'SMOKE'
++#!/usr/bin/env bash
++set -euo pipefail
++
++ROOT="${OPENVIBE_ROOT:-$HOME/src/openvibe-source}"
++cd "$ROOT"
++
++tools/apply-openvibe-sdk.sh
++tools/build-sdk-linux.sh
++tools/setup-openvibe-bin.sh
++
++echo
++cat <<'MSG'
++Embedded JS build/setup completed.
++
++Start the dev stack:
++ OPENVIBE_SRCDS_MAP_DELAY=3 tools/dev-up.sh
++
++Then in server console or via rcon/tmux pane, test:
++ ov_js_status
++ ov_js_fire Initialize
++ ov_js_cmd smoke
++
++Then in game:
++ connect 127.0.0.1:27015
++ say !js
++ say !hp
++ say !players
++MSG
++SMOKE
++chmod +x tools/smoke-embedded-js-runtime.sh
++
++echo "[openvibe] applying SDK patch"
++tools/apply-openvibe-sdk.sh
++
++if [[ "$RUN_BUILD" = "1" ]]; then
++ echo "[openvibe] running SDK build"
++ tools/build-sdk-linux.sh 2>&1 | tee "$HOME/ov-build.log"
++ echo "[openvibe] running setup-openvibe-bin.sh"
++ tools/setup-openvibe-bin.sh
++fi
++
++echo
++echo "[openvibe] next phase complete. Useful checks:"
++echo " grep -R \"COpenVibeJsBridge\|SpawnNodeJs\|system( cmd\" sdk/openvibe/server/hl2mp/openvibe_server.cpp || true"
++echo " grep -nE 'ov_js_cmd|ov_js_fire|ov_js_status' sdk/openvibe/server/hl2mp/openvibe_js_server.cpp"
++echo " tail -80 ~/ov-build.log"
++echo
++echo "Commit after testing:"
++echo " git add ."
++echo " git commit -m \"Remove legacy Node JS bridge and add embedded JS console hooks\""
++echo " git push"