// DESCRIPTION:
// monitoring window for running BSP processes (and possibly various other stuff)
-#include "stdafx.h"
#include "watchbsp.h"
+#include "globaldefs.h"
+
+#include <algorithm>
+
+#include "cmdlib.h"
+#include "convert.h"
+#include "string/string.h"
+#include "stream/stringstream.h"
+
+#include "gtkutil/messagebox.h"
+#include "xmlstuff.h"
+#include "console.h"
+#include "preferences.h"
+#include "points.h"
#include "feedback.h"
+#include "mainframe.h"
+#include "sockets.h"
-#ifdef _WIN32
-#include <winsock2.h>
-#endif
+void message_flush( message_info_t* self ){
+ Sys_Print( self->msg_level, self->m_buffer, self->m_length );
+ self->m_length = 0;
+}
-#if defined ( __linux__ ) || defined ( __APPLE__ )
-#include <sys/time.h>
-#define SOCKET_ERROR -1
-#endif
+void message_print( message_info_t* self, const char* characters, std::size_t length ){
+ const char* end = characters + length;
+ while ( characters != end )
+ {
+ std::size_t space = message_info_t::bufsize - 1 - self->m_length;
+ if ( space == 0 ) {
+ message_flush( self );
+ }
+ else
+ {
+ std::size_t size = std::min( space, std::size_t( end - characters ) );
+ memcpy( self->m_buffer + self->m_length, characters, size );
+ self->m_length += size;
+ characters += size;
+ }
+ }
+}
-#ifdef __APPLE__
-#include <unistd.h>
-#endif
-#include <assert.h>
+#include <glib.h>
+#include <uilib/uilib.h>
+#include "xmlstuff.h"
+
+class CWatchBSP
+{
+private:
+ // a flag we have set to true when using an external BSP plugin
+ // the resulting code with that is a bit dirty, cleaner solution would be to seperate the succession of commands from the listening loop
+ // (in two seperate classes probably)
+ bool m_bBSPPlugin;
+
+ // EIdle: we are not listening
+ // DoMonitoringLoop will change state to EBeginStep
+ // EBeginStep: the socket is up for listening, we are expecting incoming connection
+ // incoming connection will change state to EWatching
+ // EWatching: we have a connection, monitor it
+ // connection closed will see if we start a new step (EBeginStep) or launch Quake3 and end (EIdle)
+ enum EWatchBSPState { EIdle, EBeginStep, EWatching } m_eState;
+ socket_t *m_pListenSocket;
+ socket_t *m_pInSocket;
+ netmessage_t msg;
+ GPtrArray *m_pCmd;
+ // used to timeout EBeginStep
+ GTimer *m_pTimer;
+ std::size_t m_iCurrentStep;
+ // name of the map so we can run the engine
+ char *m_sBSPName;
+ // buffer we use in push mode to receive data directly from the network
+ xmlParserInputBufferPtr m_xmlInputBuffer;
+ xmlParserInputPtr m_xmlInput;
+ xmlParserCtxtPtr m_xmlParserCtxt;
+ // call this to switch the set listening mode
+ bool SetupListening();
+ // start a new EBeginStep
+ void DoEBeginStep();
+ // the xml and sax parser state
+ char m_xmlBuf[MAX_NETMESSAGE];
+ bool m_bNeedCtxtInit;
+ message_info_t m_message_info;
+
+public:
+ CWatchBSP(){
+ m_pCmd = 0;
+ m_bBSPPlugin = false;
+ m_pListenSocket = NULL;
+ m_pInSocket = NULL;
+ m_eState = EIdle;
+ m_pTimer = g_timer_new();
+ m_sBSPName = NULL;
+ m_xmlInputBuffer = NULL;
+ m_bNeedCtxtInit = true;
+ }
+ virtual ~CWatchBSP(){
+ EndMonitoringLoop();
+ Net_Shutdown();
+
+ g_timer_destroy( m_pTimer );
+ }
+
+ bool HasBSPPlugin() const
+ { return m_bBSPPlugin; }
+
+ // called regularly to keep listening
+ void RoutineProcessing();
+ // start a monitoring loop with the following steps
+ void DoMonitoringLoop( GPtrArray *pCmd, const char *sBSPName );
+ void EndMonitoringLoop(){
+ Reset();
+ if ( m_sBSPName ) {
+ string_release( m_sBSPName, string_length( m_sBSPName ) );
+ m_sBSPName = 0;
+ }
+ if ( m_pCmd ) {
+ g_ptr_array_free( m_pCmd, TRUE );
+ m_pCmd = 0;
+ }
+ }
+ // close everything - may be called from the outside to abort the process
+ void Reset();
+ // start a listening loop for an external process, possibly a BSP plugin
+ void ExternalListen();
+};
+
+CWatchBSP* g_pWatchBSP;
+
+// watch the BSP process through network connections
+// true: trigger the BSP steps one by one and monitor them through the network
+// false: create a BAT / .sh file and execute it. don't bother monitoring it.
+bool g_WatchBSP_Enabled = true;
+// do we stop the compilation process if we come accross a leak?
+bool g_WatchBSP_LeakStop = true;
+bool g_WatchBSP_RunQuake = false;
+// store prefs setting for automatic sleep mode activation
+bool g_WatchBSP_DoSleep = true;
+// timeout when beginning a step (in seconds)
+// if we don't get a connection quick enough we assume something failed and go back to idling
+int g_WatchBSP_Timeout = 10;
+
+
+void Build_constructPreferences( PreferencesPage& page ){
+ ui::CheckButton monitorbsp = page.appendCheckBox( "", "Enable Build Process Monitoring", g_WatchBSP_Enabled );
+ ui::CheckButton leakstop = page.appendCheckBox( "", "Stop Compilation on Leak", g_WatchBSP_LeakStop );
+ ui::CheckButton runengine = page.appendCheckBox( "", "Run Engine After Compile", g_WatchBSP_RunQuake );
+ ui::CheckButton sleep = page.appendCheckBox ( "", "Sleep When Running the Engine", g_WatchBSP_DoSleep );
+ Widget_connectToggleDependency( leakstop, monitorbsp );
+ Widget_connectToggleDependency( runengine, monitorbsp );
+ Widget_connectToggleDependency( sleep, runengine );
+}
+void Build_constructPage( PreferenceGroup& group ){
+ PreferencesPage page( group.createPage( "Build", "Build Preferences" ) );
+ Build_constructPreferences( page );
+}
+void Build_registerPreferencesPage(){
+ PreferencesDialog_addSettingsPage( makeCallbackF(Build_constructPage) );
+}
+
+#include "preferencesystem.h"
+#include "stringio.h"
+
+void BuildMonitor_Construct(){
+ g_pWatchBSP = new CWatchBSP();
+
+ g_WatchBSP_Enabled = !string_equal( g_pGameDescription->getKeyValue( "no_bsp_monitor" ), "1" );
+
+ GlobalPreferenceSystem().registerPreference( "WatchBSP", make_property_string( g_WatchBSP_Enabled ) );
+ GlobalPreferenceSystem().registerPreference( "RunQuake2Run", make_property_string( g_WatchBSP_RunQuake ) );
+ GlobalPreferenceSystem().registerPreference( "LeakStop", make_property_string( g_WatchBSP_LeakStop ) );
+ GlobalPreferenceSystem().registerPreference( "SleepMode", make_property_string( g_WatchBSP_DoSleep ) );
+
+ Build_registerPreferencesPage();
+}
+
+void BuildMonitor_Destroy(){
+ delete g_pWatchBSP;
+}
+
+CWatchBSP *GetWatchBSP(){
+ return g_pWatchBSP;
+}
+
+void BuildMonitor_Run( GPtrArray* commands, const char* mapName ){
+ GetWatchBSP()->DoMonitoringLoop( commands, mapName );
+}
+
// Static functions for the SAX callbacks -------------------------------------------------------
// utility for saxStartElement below
static void abortStream( message_info_t *data ){
- g_pParentWnd->GetWatchBSP()->Reset();
+ GetWatchBSP()->EndMonitoringLoop();
// tell there has been an error
- if ( g_pParentWnd->GetWatchBSP()->HasBSPPlugin() ) {
+#if 0
+ if ( GetWatchBSP()->HasBSPPlugin() ) {
g_BSPFrontendTable.m_pfnEndListen( 2 );
}
+#endif
// yeah this doesn't look good.. but it's needed so that everything will be ignored until the stream goes out
data->ignore_depth = -1;
data->recurse++;
#include "stream_version.h"
static void saxStartElement( message_info_t *data, const xmlChar *name, const xmlChar **attrs ){
+#if 0
+ globalOutputStream() << "<" << name;
+ if ( attrs != 0 ) {
+ for ( const xmlChar** p = attrs; *p != 0; p += 2 )
+ {
+ globalOutputStream() << " " << p[0] << "=" << makeQuoted( p[1] );
+ }
+ }
+ globalOutputStream() << ">\n";
+#endif
+
if ( data->ignore_depth == 0 ) {
- if ( data->bGeometry ) {
+ if ( data->pGeometry != 0 ) {
// we have a handler
data->pGeometry->saxStartElement( data, name, attrs );
}
else
{
- if ( strcmp( (char *)name, "q3map_feedback" ) == 0 ) {
+ if ( strcmp( reinterpret_cast<const char*>( name ), "q3map_feedback" ) == 0 ) {
// check the correct version
// old q3map don't send a version attribute
// the ones we support .. send Q3MAP_STREAM_VERSION
- if ( !attrs[0] || !attrs[1] || ( strcmp( (char*)attrs[0],"version" ) != 0 ) ) {
- Sys_FPrintf( SYS_ERR, "No stream version given in the feedback stream, this is an old q3map version.\n"
- "Please turn off monitored compiling if you still wish to use this q3map executable\n" );
+ if ( !attrs[0] || !attrs[1] || ( strcmp( reinterpret_cast<const char*>( attrs[0] ), "version" ) != 0 ) ) {
+ message_flush( data );
+ globalErrorStream() << "No stream version given in the feedback stream, this is an old q3map version.\n"
+ "Please turn off monitored compiling if you still wish to use this q3map executable\n";
abortStream( data );
return;
}
- else if ( strcmp( (char*)attrs[1],Q3MAP_STREAM_VERSION ) != 0 ) {
- Sys_FPrintf( SYS_ERR,
- "This version of Radiant reads version %s debug streams, I got an incoming connection with version %s\n"
- "Please make sure your versions of Radiant and q3map are matching.\n", Q3MAP_STREAM_VERSION, (char*)attrs[1] );
+ else if ( strcmp( reinterpret_cast<const char*>( attrs[1] ), Q3MAP_STREAM_VERSION ) != 0 ) {
+ message_flush( data );
+ globalErrorStream() <<
+ "This version of " RADIANT_NAME " reads version " Q3MAP_STREAM_VERSION " debug streams, I got an incoming connection with version " << reinterpret_cast<const char*>( attrs[1] ) << "\n"
+ "Please make sure your versions of " RADIANT_NAME " and q3map are matching.\n";
abortStream( data );
return;
}
}
// we don't treat locally
- else if ( strcmp( (char *)name, "message" ) == 0 ) {
- data->msg_level = atoi( (char *)attrs[1] );
+ else if ( strcmp( reinterpret_cast<const char*>( name ), "message" ) == 0 ) {
+ int msg_level = atoi( reinterpret_cast<const char*>( attrs[1] ) );
+ if ( msg_level != data->msg_level ) {
+ message_flush( data );
+ data->msg_level = msg_level;
+ }
}
- else if ( strcmp( (char *)name, "polyline" ) == 0 ) {
+ else if ( strcmp( reinterpret_cast<const char*>( name ), "polyline" ) == 0 ) {
// polyline has a particular status .. right now we only use it for leakfile ..
- data->bGeometry = true;
+ data->geometry_depth = data->recurse;
data->pGeometry = &g_pointfile;
data->pGeometry->saxStartElement( data, name, attrs );
}
- else if ( strcmp( (char *)name, "select" ) == 0 ) {
+ else if ( strcmp( reinterpret_cast<const char*>( name ), "select" ) == 0 ) {
CSelectMsg *pSelect = new CSelectMsg();
- data->bGeometry = true;
+ data->geometry_depth = data->recurse;
data->pGeometry = pSelect;
data->pGeometry->saxStartElement( data, name, attrs );
}
- else if ( strcmp( (char *)name, "pointmsg" ) == 0 ) {
+ else if ( strcmp( reinterpret_cast<const char*>( name ), "pointmsg" ) == 0 ) {
CPointMsg *pPoint = new CPointMsg();
- data->bGeometry = true;
+ data->geometry_depth = data->recurse;
data->pGeometry = pPoint;
data->pGeometry->saxStartElement( data, name, attrs );
}
- else if ( strcmp( (char *)name, "windingmsg" ) == 0 ) {
+ else if ( strcmp( reinterpret_cast<const char*>( name ), "windingmsg" ) == 0 ) {
CWindingMsg *pWinding = new CWindingMsg();
- data->bGeometry = true;
+ data->geometry_depth = data->recurse;
data->pGeometry = pWinding;
data->pGeometry->saxStartElement( data, name, attrs );
}
else
{
- Sys_FPrintf( SYS_WRN, "WARNING: ignoring unrecognized node in XML stream (%s)\n", name );
+ globalErrorStream() << "Warning: ignoring unrecognized node in XML stream (" << reinterpret_cast<const char*>( name ) << ")\n";
// we don't recognize this node, jump over it
// (NOTE: the ignore mechanism is a bit screwed, only works when starting an ignore at the highest level)
data->ignore_depth = data->recurse;
data->recurse++;
}
-static void saxEndElement( message_info_t *data, const xmlChar *name ) {
+static void saxEndElement( message_info_t *data, const xmlChar *name ){
+#if 0
+ globalOutputStream() << "<" << name << "/>\n";
+#endif
+
data->recurse--;
// we are out of an ignored chunk
if ( data->recurse == data->ignore_depth ) {
data->ignore_depth = 0;
return;
}
- if ( data->bGeometry ) {
+ if ( data->pGeometry != 0 ) {
data->pGeometry->saxEndElement( data, name );
// we add the object to the debug window
- if ( !data->bGeometry ) {
+ if ( data->geometry_depth == data->recurse ) {
g_DbgDlg.Push( data->pGeometry );
+ data->pGeometry = 0;
}
}
if ( data->recurse == data->stop_depth ) {
-#ifdef _DEBUG
- Sys_Printf( "Received error msg .. shutting down..\n" );
+ message_flush( data );
+#if GDEF_DEBUG
+ globalOutputStream() << "Received error msg .. shutting down..\n";
#endif
- g_pParentWnd->GetWatchBSP()->Reset();
+ GetWatchBSP()->EndMonitoringLoop();
// tell there has been an error
- if ( g_pParentWnd->GetWatchBSP()->HasBSPPlugin() ) {
+#if 0
+ if ( GetWatchBSP()->HasBSPPlugin() ) {
g_BSPFrontendTable.m_pfnEndListen( 2 );
}
+#endif
return;
}
}
-static void saxCharacters( message_info_t *data, const xmlChar *ch, int len ){
- if ( data->bGeometry ) {
- data->pGeometry->saxCharacters( data, ch, len );
+class MessageOutputStream : public TextOutputStream
+{
+ message_info_t* m_data;
+public:
+ MessageOutputStream( message_info_t* data ) : m_data( data ){
}
- else
- {
- if ( data->ignore_depth != 0 ) {
- return;
+
+ std::size_t write( const char* buffer, std::size_t length ){
+ if ( m_data->pGeometry != 0 ) {
+ m_data->pGeometry->saxCharacters( m_data, reinterpret_cast<const xmlChar*>( buffer ), int(length) );
}
- // output the message using the level
- char buf[1024];
- memcpy( buf, ch, len );
- buf[len] = '\0';
- Sys_FPrintf( data->msg_level, "%s", buf );
- // if this message has error level flag, we mark the depth to stop the compilation when we get out
- // we don't set the msg level if we don't stop on leak
- if ( data->msg_level == 3 ) {
- data->stop_depth = data->recurse - 1;
+ else
+ {
+ if ( m_data->ignore_depth == 0 ) {
+ // output the message using the level
+ message_print( m_data, buffer, length );
+ // if this message has error level flag, we mark the depth to stop the compilation when we get out
+ // we don't set the msg level if we don't stop on leak
+ if ( m_data->msg_level == 3 ) {
+ m_data->stop_depth = m_data->recurse - 1;
+ }
+ }
}
+
+ return length;
}
+};
+
+template<typename T>
+inline MessageOutputStream& operator<<( MessageOutputStream& ostream, const T& t ){
+ return ostream_write( ostream, t );
+}
+
+static void saxCharacters( message_info_t *data, const xmlChar *ch, int len ){
+ MessageOutputStream ostream( data );
+ ostream << StringRange( reinterpret_cast<const char*>( ch ), reinterpret_cast<const char*>( ch + len ) );
}
static void saxComment( void *ctx, const xmlChar *msg ){
- Sys_Printf( "XML comment: %s\n", msg );
+ globalOutputStream() << "XML comment: " << reinterpret_cast<const char*>( msg ) << "\n";
}
static void saxWarning( void *ctx, const char *msg, ... ){
va_start( args, msg );
vsprintf( saxMsgBuffer, msg, args );
va_end( args );
- Sys_FPrintf( SYS_WRN, "XML warning: %s\n", saxMsgBuffer );
+ globalOutputStream() << "XML warning: " << saxMsgBuffer << "\n";
}
static void saxError( void *ctx, const char *msg, ... ){
va_start( args, msg );
vsprintf( saxMsgBuffer, msg, args );
va_end( args );
- Sys_FPrintf( SYS_ERR, "XML error: %s\n", saxMsgBuffer );
+ globalErrorStream() << "XML error: " << saxMsgBuffer << "\n";
}
static void saxFatal( void *ctx, const char *msg, ... ){
va_start( args, msg );
vsprintf( buffer, msg, args );
va_end( args );
- Sys_FPrintf( SYS_ERR, "XML fatal error: %s\n", buffer );
+ globalErrorStream() << "XML fatal error: " << buffer << "\n";
}
static xmlSAXHandler saxParser = {
(warningSAXFunc)saxWarning, /* warning */
(errorSAXFunc)saxError, /* error */
(fatalErrorSAXFunc)saxFatal, /* fatalError */
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
};
// ------------------------------------------------------------------------------------------------
-CWatchBSP::~CWatchBSP(){
- Reset();
- if ( m_sBSPName ) {
- delete[] m_sBSPName;
- m_sBSPName = NULL;
- }
- Net_Shutdown();
+
+guint s_routine_id;
+static gint watchbsp_routine( gpointer data ){
+ reinterpret_cast<CWatchBSP*>( data )->RoutineProcessing();
+ return TRUE;
}
void CWatchBSP::Reset(){
m_xmlInputBuffer = NULL;
}
m_eState = EIdle;
+ if ( s_routine_id ) {
+ g_source_remove( s_routine_id );
+ }
}
bool CWatchBSP::SetupListening(){
-#ifdef _DEBUG
+#if GDEF_DEBUG
if ( m_pListenSocket ) {
- Sys_Printf( "ERROR: m_pListenSocket != NULL in CWatchBSP::SetupListening\n" );
+ globalOutputStream() << "ERROR: m_pListenSocket != NULL in CWatchBSP::SetupListening\n";
return false;
}
#endif
- Sys_Printf( "Setting up\n" );
- if ( !Net_Setup() ) {
- return false;
- }
-
+ globalOutputStream() << "Setting up\n";
+ Net_Setup();
m_pListenSocket = Net_ListenSocket( 39000 );
if ( m_pListenSocket == NULL ) {
return false;
}
-
- Sys_Printf( "Listening...\n" );
+ globalOutputStream() << "Listening...\n";
return true;
}
-void CWatchBSP::DoEBeginStep() {
+void CWatchBSP::DoEBeginStep(){
Reset();
- if ( !SetupListening() ) {
- CString msg;
- msg = "Failed to get a listening socket on port 39000.\nTry running with BSP monitoring disabled if you can't fix this.\n";
- Sys_Printf( msg );
- gtk_MessageBox( g_pParentWnd->m_pWidget, msg, "BSP monitoring", MB_OK | MB_ICONERROR );
+ if ( SetupListening() == false ) {
+ const char* msg = "Failed to get a listening socket on port 39000.\nTry running with Build monitoring disabled if you can't fix this.\n";
+ globalOutputStream() << msg;
+ ui::alert( MainFrame_getWindow(), msg, "Build monitoring", ui::alert_type::OK, ui::alert_icon::Error );
return;
}
// set the timer for timeouts and step cancellation
g_timer_start( m_pTimer );
if ( !m_bBSPPlugin ) {
- Sys_Printf( "=== running BSP command ===\n%s\n", g_ptr_array_index( m_pCmd, m_iCurrentStep ) );
-
- if ( !Q_Exec( NULL, (char *)g_ptr_array_index( m_pCmd, m_iCurrentStep ), NULL, true ) ) {
- CString msg;
- msg = "Failed to execute the following command: ";
- msg += (char *)g_ptr_array_index( m_pCmd, m_iCurrentStep );
- msg += "\nCheck that the file exists and that you don't run out of system resources.\n";
- Sys_Printf( msg );
- gtk_MessageBox( g_pParentWnd->m_pWidget, msg, "BSP monitoring", MB_OK | MB_ICONERROR );
+ globalOutputStream() << "=== running build command ===\n"
+ << static_cast<const char*>( g_ptr_array_index( m_pCmd, m_iCurrentStep ) ) << "\n";
+
+ if ( !Q_Exec( NULL, (char *)g_ptr_array_index( m_pCmd, m_iCurrentStep ), NULL, true, false ) ) {
+ StringOutputStream msg( 256 );
+ msg << "Failed to execute the following command: ";
+ msg << reinterpret_cast<const char*>( g_ptr_array_index( m_pCmd, m_iCurrentStep ) );
+ msg << "\nCheck that the file exists and that you don't run out of system resources.\n";
+ globalOutputStream() << msg.c_str();
+ ui::alert( MainFrame_getWindow(), msg.c_str(), "Build monitoring", ui::alert_type::OK, ui::alert_icon::Error );
return;
}
// re-initialise the debug window
}
}
m_eState = EBeginStep;
+ s_routine_id = g_timeout_add( 25, watchbsp_routine, this );
}
-void CWatchBSP::RoutineProcessing(){
- // used for select()
-#ifdef _WIN32
- TIMEVAL tout = { 0, 0 };
-#endif
-#if defined ( __linux__ ) || defined ( __APPLE__ )
- timeval tout;
- tout.tv_sec = 0;
- tout.tv_usec = 0;
+
+#if GDEF_OS_WINDOWS
+const char *ENGINE_ATTRIBUTE = "engine_win32";
+const char *MP_ENGINE_ATTRIBUTE = "mp_engine_win32";
+#elif GDEF_OS_LINUX || GDEF_OS_BSD
+const char *ENGINE_ATTRIBUTE = "engine_linux";
+const char *MP_ENGINE_ATTRIBUTE = "mp_engine_linux";
+#elif GDEF_OS_MACOS
+const char *ENGINE_ATTRIBUTE = "engine_macos";
+const char *MP_ENGINE_ATTRIBUTE = "mp_engine_macos";
+#else
+#error "unsupported platform"
#endif
+class RunEngineConfiguration
+{
+public:
+ const char* executable;
+ const char* mp_executable;
+ bool do_sp_mp;
+
+ RunEngineConfiguration() :
+ executable( g_pGameDescription->getRequiredKeyValue( ENGINE_ATTRIBUTE ) ),
+ mp_executable( g_pGameDescription->getKeyValue( MP_ENGINE_ATTRIBUTE ) ){
+ do_sp_mp = !string_empty( mp_executable );
+ }
+};
+
+inline void GlobalGameDescription_string_write_mapparameter( StringOutputStream& string, const char* mapname ){
+ if ( g_pGameDescription->mGameType == "q2"
+ || g_pGameDescription->mGameType == "heretic2" ) {
+ string << ". +exec radiant.cfg +map " << mapname;
+ }
+ else
+ {
+ string << "+set sv_pure 0 ";
+ // TTimo: a check for vm_* but that's all fine
+ //cmdline = "+set sv_pure 0 +set vm_ui 0 +set vm_cgame 0 +set vm_game 0 ";
+ const char* fs_game = gamename_get();
+ if ( !string_equal( fs_game, basegame_get() ) ) {
+ string << "+set fs_game " << fs_game << " ";
+ }
+ if ( g_pGameDescription->mGameType == "wolf" ) {
+ //|| g_pGameDescription->mGameType == "et")
+ if ( string_equal( gamemode_get(), "mp" ) ) {
+ // MP
+ string << "+devmap " << mapname;
+ }
+ else
+ {
+ // SP
+ string << "+set nextmap \"spdevmap " << mapname << "\"";
+ }
+ }
+ else
+ {
+ string << "+devmap " << mapname;
+ }
+ }
+}
+
+
+void CWatchBSP::RoutineProcessing(){
switch ( m_eState )
{
case EBeginStep:
// timeout: if we don't get an incoming connection fast enough, go back to idle
- if ( g_timer_elapsed( m_pTimer, NULL ) > g_PrefsDlg.m_iTimeout ) {
- gtk_MessageBox( g_pParentWnd->m_pWidget, "The connection timed out, assuming the BSP process failed\nMake sure you are using a networked version of Q3Map?\nOtherwise you need to disable BSP Monitoring in prefs.", "BSP process monitoring", MB_OK );
- Reset();
+ if ( g_timer_elapsed( m_pTimer, NULL ) > g_WatchBSP_Timeout ) {
+ ui::alert( MainFrame_getWindow(), "The connection timed out, assuming the build process failed\nMake sure you are using a networked version of Q3Map?\nOtherwise you need to disable BSP Monitoring in prefs.", "BSP process monitoring", ui::alert_type::OK );
+ EndMonitoringLoop();
+#if 0
if ( m_bBSPPlugin ) {
// status == 1 : didn't get the connection
g_BSPFrontendTable.m_pfnEndListen( 1 );
}
+#endif
return;
}
-#ifdef _DEBUG
+#if GDEF_DEBUG
// some debug checks
if ( !m_pListenSocket ) {
- Sys_Printf( "ERROR: m_pListenSocket == NULL in CWatchBSP::RoutineProcessing EBeginStep state\n" );
+ globalErrorStream() << "ERROR: m_pListenSocket == NULL in CWatchBSP::RoutineProcessing EBeginStep state\n";
return;
}
#endif
// we are not connected yet, accept any incoming connection
m_pInSocket = Net_Accept( m_pListenSocket );
if ( m_pInSocket ) {
- Sys_Printf( "Connected.\n" );
+ globalOutputStream() << "Connected.\n";
// prepare the message info struct for diving in
- memset( &m_message_info, 0, sizeof( message_info_s ) );
+ memset( &m_message_info, 0, sizeof( message_info_t ) );
// a dumb flag to make sure we init the push parser context when first getting a msg
m_bNeedCtxtInit = true;
m_eState = EWatching;
}
break;
case EWatching:
-#ifdef _DEBUG
+ {
+#if GDEF_DEBUG
// some debug checks
if ( !m_pInSocket ) {
- Sys_Printf( "ERROR: m_pInSocket == NULL in CWatchBSP::RoutineProcessing EWatching state\n" );
+ globalErrorStream() << "ERROR: m_pInSocket == NULL in CWatchBSP::RoutineProcessing EWatching state\n";
return;
}
#endif
- // select() will identify if the socket needs an update
- // if the socket is identified that means there's either a message or the connection has been closed/reset/terminated
- fd_set readfds;
- int ret;
- FD_ZERO( &readfds );
- FD_SET( ( (unsigned int)m_pInSocket->socket ), &readfds );
- // from select man page:
- // n is the highest-numbered descriptor in any of the three sets, plus 1
- // (no use on windows)
- ret = select( m_pInSocket->socket + 1, &readfds, NULL, NULL, &tout );
- if ( ret == SOCKET_ERROR ) {
- Sys_Printf( "WARNING: SOCKET_ERROR in CWatchBSP::RoutineProcessing\n" );
- Sys_Printf( "Terminating the connection.\n" );
- Reset();
- return;
- }
-#ifdef _DEBUG
+
+ int ret = Net_Wait( m_pInSocket, 0, 0 );
if ( ret == -1 ) {
- // we are non-blocking?? we should never get timeout errors
- Sys_Printf( "WARNING: unexpected timeout expired in CWatchBSP::Processing\n" );
- Sys_Printf( "Terminating the connection.\n" );
- Reset();
+ globalOutputStream() << "WARNING: SOCKET_ERROR in CWatchBSP::RoutineProcessing\n";
+ globalOutputStream() << "Terminating the connection.\n";
+ EndMonitoringLoop();
return;
}
-#endif
+
if ( ret == 1 ) {
// the socket has been identified, there's something (message or disconnection)
// see if there's anything in input
strcpy( m_xmlBuf, NMSG_ReadString( &msg ) );
if ( m_bNeedCtxtInit ) {
m_xmlParserCtxt = NULL;
- m_xmlParserCtxt = xmlCreatePushParserCtxt( &saxParser, &m_message_info, m_xmlBuf, strlen( m_xmlBuf ), NULL );
+ m_xmlParserCtxt = xmlCreatePushParserCtxt( &saxParser, &m_message_info, m_xmlBuf, static_cast<int>( strlen( m_xmlBuf ) ), NULL );
+
if ( m_xmlParserCtxt == NULL ) {
- Sys_FPrintf( SYS_ERR, "Failed to create the XML parser (incoming stream began with: %s)\n", m_xmlBuf );
- Reset();
+ globalErrorStream() << "Failed to create the XML parser (incoming stream began with: " << m_xmlBuf << ")\n";
+ EndMonitoringLoop();
}
m_bNeedCtxtInit = false;
}
else
{
- xmlParseChunk( m_xmlParserCtxt, m_xmlBuf, strlen( m_xmlBuf ), 0 );
+ xmlParseChunk( m_xmlParserCtxt, m_xmlBuf, static_cast<int>( strlen( m_xmlBuf ) ), 0 );
}
}
else
{
+ message_flush( &m_message_info );
// error or connection closed/reset
// NOTE: if we get an error down the XML stream we don't reach here
Net_Disconnect( m_pInSocket );
m_pInSocket = NULL;
- Sys_Printf( "Connection closed.\n" );
+ globalOutputStream() << "Connection closed.\n";
+#if 0
if ( m_bBSPPlugin ) {
- Reset();
+ EndMonitoringLoop();
// let the BSP plugin know that the job is done
g_BSPFrontendTable.m_pfnEndListen( 0 );
return;
}
+#endif
// move to next step or finish
m_iCurrentStep++;
if ( m_iCurrentStep < m_pCmd->len ) {
}
else
{
- // release the GPtrArray and the strings
- if ( m_pCmd != NULL ) {
- for ( m_iCurrentStep = 0; m_iCurrentStep < m_pCmd->len; m_iCurrentStep++ )
- {
- delete[] (char *)g_ptr_array_index( m_pCmd, m_iCurrentStep );
- }
- g_ptr_array_free( m_pCmd, false );
- }
- m_pCmd = NULL;
// launch the engine .. OMG
- if ( g_PrefsDlg.m_bRunQuake ) {
+ if ( g_WatchBSP_RunQuake ) {
+#if 0
// do we enter sleep mode before?
- if ( g_PrefsDlg.m_bDoSleep ) {
- Sys_Printf( "Going into sleep mode..\n" );
+ if ( g_WatchBSP_DoSleep ) {
+ globalOutputStream() << "Going into sleep mode..\n";
g_pParentWnd->OnSleep();
}
- Sys_Printf( "Running engine...\n" );
- Str cmd;
+#endif
+ globalOutputStream() << "Running engine...\n";
+ StringOutputStream cmd( 256 );
// build the command line
- cmd = g_pGameDescription->mEnginePath.GetBuffer();
+ cmd << EnginePath_get();
// this is game dependant
- if ( !strcmp( ValueForKey( g_qeglobals.d_project_entity, "gamemode" ),"mp" ) ) {
- // MP
- cmd += g_pGameDescription->mMultiplayerEngine.GetBuffer();
- }
- else
- {
- // SP
- cmd += g_pGameDescription->mEngine.GetBuffer();
- }
-#ifdef _WIN32
- // NOTE: we are using unix pathnames and CreateProcess doesn't like / in the program path
- // FIXME: This isn't true anymore, doesn't it?
- FindReplace( cmd, "/", "\\" );
-#endif
- Str cmdline;
- if ( g_pGameDescription->quake2 ) {
- cmdline = ". +exec radiant.cfg +map ";
- cmdline += m_sBSPName;
- }
- else
- {
- cmdline = "+set sv_pure 0 ";
- // TTimo: a check for vm_* but that's all fine
- //cmdline = "+set sv_pure 0 +set vm_ui 0 +set vm_cgame 0 +set vm_game 0 ";
- if ( *ValueForKey( g_qeglobals.d_project_entity, "gamename" ) != '\0' ) {
- cmdline += "+set fs_game ";
- cmdline += ValueForKey( g_qeglobals.d_project_entity, "gamename" );
- cmdline += " ";
- }
- //!\todo Read the start-map args from a config file.
- if ( g_pGameDescription->mGameFile == "wolf.game" ) {
- if ( !strcmp( ValueForKey( g_qeglobals.d_project_entity, "gamemode" ),"mp" ) ) {
- // MP
- cmdline += "+devmap ";
- cmdline += m_sBSPName;
- }
- else
- {
- // SP
- cmdline += "+set nextmap \"spdevmap ";
- cmdline += m_sBSPName;
- cmdline += "\"";
- }
+
+ RunEngineConfiguration engineConfig;
+
+ if ( engineConfig.do_sp_mp ) {
+ if ( string_equal( gamemode_get(), "mp" ) ) {
+ cmd << engineConfig.mp_executable;
}
else
{
- cmdline += "+devmap ";
- cmdline += m_sBSPName;
+ cmd << engineConfig.executable;
}
}
+ else
+ {
+ cmd << engineConfig.executable;
+ }
+
+ StringOutputStream cmdline;
- Sys_Printf( "%s %s\n", cmd.GetBuffer(), cmdline.GetBuffer() );
+ GlobalGameDescription_string_write_mapparameter( cmdline, m_sBSPName );
+
+ globalOutputStream() << cmd.c_str() << " " << cmdline.c_str() << "\n";
// execute now
- if ( !Q_Exec( cmd.GetBuffer(), (char *)cmdline.GetBuffer(), g_pGameDescription->mEnginePath.GetBuffer(), false ) ) {
- CString msg;
- msg = "Failed to execute the following command: ";
- msg += cmd; msg += cmdline;
- Sys_Printf( msg );
- gtk_MessageBox( g_pParentWnd->m_pWidget, msg, "BSP monitoring", MB_OK | MB_ICONERROR );
+ if ( !Q_Exec( cmd.c_str(), (char *)cmdline.c_str(), EnginePath_get(), false, false ) ) {
+ StringOutputStream msg;
+ msg << "Failed to execute the following command: " << cmd.c_str() << cmdline.c_str();
+ globalOutputStream() << msg.c_str();
+ ui::alert( MainFrame_getWindow(), msg.c_str(), "Build monitoring", ui::alert_type::OK, ui::alert_icon::Error );
}
}
- Reset();
+ EndMonitoringLoop();
}
}
}
- break;
+ }
+ break;
default:
break;
}
}
-void CWatchBSP::DoMonitoringLoop( GPtrArray *pCmd, char *sBSPName ){
- if ( m_sBSPName ) {
- delete[] m_sBSPName;
+GPtrArray* str_ptr_array_clone( GPtrArray* array ){
+ GPtrArray* cloned = g_ptr_array_sized_new( array->len );
+ for ( guint i = 0; i < array->len; ++i )
+ {
+ g_ptr_array_add( cloned, g_strdup( (char*)g_ptr_array_index( array, i ) ) );
}
- m_sBSPName = sBSPName;
+ return cloned;
+}
+
+void CWatchBSP::DoMonitoringLoop( GPtrArray *pCmd, const char *sBSPName ){
+ m_sBSPName = string_clone( sBSPName );
if ( m_eState != EIdle ) {
- Sys_Printf( "WatchBSP got a monitoring request while not idling...\n" );
+ globalOutputStream() << "WatchBSP got a monitoring request while not idling...\n";
// prompt the user, should we cancel the current process and go ahead?
- if ( gtk_MessageBox( g_pParentWnd->m_pWidget, "I am already monitoring a BSP process.\nDo you want me to override and start a new compilation?",
- "BSP process monitoring", MB_YESNO ) == IDYES ) {
+ if ( ui::alert( MainFrame_getWindow(), "I am already monitoring a Build process.\nDo you want me to override and start a new compilation?",
+ "Build process monitoring", ui::alert_type::YESNO ) == ui::alert_response::YES ) {
// disconnect and set EIdle state
Reset();
}
}
- m_pCmd = pCmd;
+ m_pCmd = str_ptr_array_clone( pCmd );
m_iCurrentStep = 0;
DoEBeginStep();
}
// the part of the watchbsp interface we export to plugins
// NOTE: in the long run, the whole watchbsp.cpp interface needs to go out and be handled at the BSP plugin level
// for now we provide something really basic and limited, the essential is to have something that works fine and fast (for 1.1 final)
-void WINAPI QERApp_Listen(){
+void QERApp_Listen(){
// open the listening socket
- g_pParentWnd->GetWatchBSP()->ExternalListen();
+ GetWatchBSP()->ExternalListen();
}