Merge pull request #245 from jdolan/master

Stabilize BSP monitoring code
This commit is contained in:
Timothee "TTimo" Besset 2013-11-19 19:53:47 -08:00
commit 26d8547ea0
3 changed files with 151 additions and 125 deletions

View file

@ -671,7 +671,7 @@ GtkWidget* create_SurfaceInspector( void ){
gtk_spin_button_set_update_policy( GTK_SPIN_BUTTON( vscale_value_spinbutton ), GTK_UPDATE_IF_VALID );
adjustment = gtk_adjustment_new( 0.0, -360.0, 360.0, 1.0, 10.0, 0.0 );
rotate_value_spinbutton = gtk_spin_button_new( GTK_ADJUSTMENT( adjustment ), 1, 0 );
rotate_value_spinbutton = gtk_spin_button_new( GTK_ADJUSTMENT( adjustment ), 1, 2 );
gtk_widget_show( rotate_value_spinbutton );
gtk_table_attach( GTK_TABLE( table1 ), rotate_value_spinbutton, 1, 2, 5, 6,
(GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),

View file

@ -152,7 +152,6 @@ static void saxEndElement( message_info_t *data, const xmlChar *name ) {
#ifdef _DEBUG
Sys_Printf( "Received error msg .. shutting down..\n" );
#endif
g_pParentWnd->GetWatchBSP()->Reset();
// tell there has been an error
if ( g_pParentWnd->GetWatchBSP()->HasBSPPlugin() ) {
g_BSPFrontendTable.m_pfnEndListen( 2 );
@ -248,219 +247,27 @@ static xmlSAXHandler saxParser = {
// ------------------------------------------------------------------------------------------------
CWatchBSP::~CWatchBSP(){
Reset();
if ( m_pCmd ) {
g_ptr_array_free( m_pCmd, true );
m_pCmd = NULL;
}
if ( m_sBSPName ) {
delete[] m_sBSPName;
g_free( m_sBSPName );
m_sBSPName = NULL;
}
Net_Shutdown();
}
void CWatchBSP::Reset(){
if ( m_pInSocket ) {
Net_Disconnect( m_pInSocket );
m_pInSocket = NULL;
}
if ( m_pListenSocket ) {
Net_Disconnect( m_pListenSocket );
m_pListenSocket = NULL;
}
if ( m_xmlInputBuffer ) {
xmlFreeParserInputBuffer( m_xmlInputBuffer );
m_xmlInputBuffer = NULL;
}
m_eState = EIdle;
}
void CWatchBSP::RunQuake() {
bool CWatchBSP::SetupListening(){
#ifdef _DEBUG
if ( m_pListenSocket ) {
Sys_Printf( "ERROR: m_pListenSocket != NULL in CWatchBSP::SetupListening\n" );
return false;
}
#endif
Sys_Printf( "Setting up\n" );
if ( !Net_Setup() ) {
return false;
}
m_pListenSocket = Net_ListenSocket( 39000 );
if ( m_pListenSocket == NULL ) {
return false;
}
Sys_Printf( "Listening...\n" );
return true;
}
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 );
return;
}
// set the timer for timeouts and step cancellation
g_timer_reset( m_pTimer );
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 );
return;
}
// re-initialise the debug window
if ( m_iCurrentStep == 0 ) {
g_DbgDlg.Init();
}
}
m_eState = EBeginStep;
}
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;
#endif
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 ( m_bBSPPlugin ) {
// status == 1 : didn't get the connection
g_BSPFrontendTable.m_pfnEndListen( 1 );
}
return;
}
#ifdef _DEBUG
// some debug checks
if ( !m_pListenSocket ) {
Sys_Printf( "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" );
// prepare the message info struct for diving in
memset( &m_message_info, 0, sizeof( message_info_s ) );
// 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
// some debug checks
if ( !m_pInSocket ) {
Sys_Printf( "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
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();
return;
}
#endif
if ( ret == 1 ) {
// the socket has been identified, there's something (message or disconnection)
// see if there's anything in input
ret = Net_Receive( m_pInSocket, &msg );
if ( ret > 0 ) {
// unsigned int size = msg.size; //++timo just a check
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 );
if ( m_xmlParserCtxt == NULL ) {
Sys_FPrintf( SYS_ERR, "Failed to create the XML parser (incoming stream began with: %s)\n", m_xmlBuf );
Reset();
}
m_bNeedCtxtInit = false;
}
else
{
xmlParseChunk( m_xmlParserCtxt, m_xmlBuf, strlen( m_xmlBuf ), 0 );
}
}
else
{
// 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" );
if ( m_bBSPPlugin ) {
Reset();
// let the BSP plugin know that the job is done
g_BSPFrontendTable.m_pfnEndListen( 0 );
return;
}
// move to next step or finish
m_iCurrentStep++;
if ( m_iCurrentStep < m_pCmd->len ) {
DoEBeginStep();
}
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 ) {
// do we enter sleep mode before?
if ( g_PrefsDlg.m_bDoSleep ) {
Sys_Printf( "Going into sleep mode..\n" );
g_pParentWnd->OnSleep();
}
Sys_Printf( "Running engine...\n" );
Str cmd;
// build the command line
Str cmd;
cmd = g_pGameDescription->mExecutablesPath.GetBuffer();
// this is game dependant
if ( !strcmp( ValueForKey( g_qeglobals.d_project_entity, "gamemode" ),"mp" ) ) {
@ -525,8 +332,207 @@ void CWatchBSP::RoutineProcessing(){
Sys_Printf( msg );
gtk_MessageBox( g_pParentWnd->m_pWidget, msg, "BSP monitoring", MB_OK | MB_ICONERROR );
}
}
void CWatchBSP::Reset(){
if ( m_pInSocket ) {
Net_Disconnect( m_pInSocket );
m_pInSocket = NULL;
}
if ( m_pListenSocket ) {
Net_Disconnect( m_pListenSocket );
m_pListenSocket = NULL;
}
if ( m_xmlInputBuffer ) {
xmlFreeParserInputBuffer( m_xmlInputBuffer );
m_xmlInputBuffer = NULL;
}
if ( m_xmlParserCtxt ) {
xmlFreeParserCtxt( m_xmlParserCtxt );
m_xmlParserCtxt = NULL;
}
m_eState = EIdle;
}
bool CWatchBSP::SetupListening(){
#ifdef _DEBUG
if ( m_pListenSocket ) {
Sys_Printf( "ERROR: m_pListenSocket != NULL in CWatchBSP::SetupListening\n" );
return false;
}
#endif
Sys_Printf( "Setting up\n" );
if ( !Net_Setup() ) {
return false;
}
m_pListenSocket = Net_ListenSocket( 39000 );
if ( m_pListenSocket == NULL ) {
return false;
}
Sys_Printf( "Listening...\n" );
return true;
}
void CWatchBSP::DoEBeginStep() {
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 );
Reset();
return;
}
// re-initialise the debug window
if ( m_iCurrentStep == 0 ) {
g_DbgDlg.Init();
}
// set the timer for timeouts and step cancellation
g_timer_reset( m_pTimer );
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 );
Reset();
return;
}
}
m_eState = EBeginStep;
}
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;
#endif
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 ( m_bBSPPlugin ) {
// status == 1 : didn't get the connection
g_BSPFrontendTable.m_pfnEndListen( 1 );
}
break;
}
#ifdef _DEBUG
// some debug checks
if ( !m_pListenSocket ) {
Sys_Printf( "ERROR: m_pListenSocket == NULL in CWatchBSP::RoutineProcessing EBeginStep state\n" );
Reset();
break;
}
#endif
// we are not connected yet, accept any incoming connection
m_pInSocket = Net_Accept( m_pListenSocket );
if ( m_pInSocket ) {
Sys_Printf( "Connected.\n" );
// prepare the message info struct for diving in
memset( &m_message_info, 0, sizeof( message_info_s ) );
// a dumb flag to make sure we init the push parser context when first getting a msg
m_eState = EWatching;
}
break;
case EWatching:
#ifdef _DEBUG
// some debug checks
if ( !m_pInSocket ) {
Sys_Printf( "ERROR: m_pInSocket == NULL in CWatchBSP::RoutineProcessing EWatching state\n" );
Reset();
break;
}
#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();
break;
}
if ( ret == 1 ) {
// the socket has been identified, there's something (message or disconnection)
// see if there's anything in input
ret = Net_Receive( m_pInSocket, &msg );
if ( ret > 0 ) {
// unsigned int size = msg.size; //++timo just a check
g_strlcpy( m_xmlBuf, NMSG_ReadString( &msg ), sizeof( m_xmlBuf) );
if ( m_xmlParserCtxt == NULL ) {
m_xmlParserCtxt = xmlCreatePushParserCtxt( &saxParser, &m_message_info, m_xmlBuf, 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();
break;
}
}
else
{
xmlParseChunk( m_xmlParserCtxt, m_xmlBuf, strlen( m_xmlBuf ), 0 );
}
}
else
{
// 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" );
if ( m_bBSPPlugin ) {
// let the BSP plugin know that the job is done
g_BSPFrontendTable.m_pfnEndListen( 0 );
}
Reset();
// move to next step or finish
m_iCurrentStep++;
if ( m_iCurrentStep < m_pCmd->len ) {
DoEBeginStep();
break;
}
// launch the engine .. OMG
if ( g_PrefsDlg.m_bRunQuake ) {
// do we enter sleep mode before?
if ( g_PrefsDlg.m_bDoSleep ) {
Sys_Printf( "Going into sleep mode..\n" );
g_pParentWnd->OnSleep();
}
Sys_Printf( "Running engine...\n" );
RunQuake();
}
}
}
@ -537,21 +543,39 @@ void CWatchBSP::RoutineProcessing(){
}
void CWatchBSP::DoMonitoringLoop( GPtrArray *pCmd, char *sBSPName ){
if ( m_sBSPName ) {
delete[] m_sBSPName;
}
m_sBSPName = sBSPName;
guint i;
if ( m_eState != EIdle ) {
Sys_Printf( "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 ) {
// disconnect and set EIdle state
"BSP process monitoring", MB_YESNO ) == IDNO ) {
return;
}
}
Reset();
if ( m_pCmd ) {
g_ptr_array_free( m_pCmd, true );
m_pCmd = NULL;
}
if ( m_sBSPName ) {
g_free( m_sBSPName );
m_sBSPName = NULL;
}
m_pCmd = pCmd;
m_pCmd = g_ptr_array_new_full( pCmd->len, g_free ) ;
for ( i = 0; i < pCmd->len; i++ ) {
g_ptr_array_add( m_pCmd, g_strdup( (char *) pCmd->pdata[i] ) );
}
m_iCurrentStep = 0;
m_sBSPName = g_strdup(sBSPName);
DoEBeginStep();
}

View file

@ -61,6 +61,8 @@ char *m_sBSPName;
xmlParserInputBufferPtr m_xmlInputBuffer;
xmlParserInputPtr m_xmlInput;
xmlParserCtxtPtr m_xmlParserCtxt;
// run the game engine after successful compile
void RunQuake();
// call this to switch the set listening mode
bool SetupListening();
// start a new EBeginStep
@ -71,7 +73,7 @@ bool m_bNeedCtxtInit;
message_info_s m_message_info;
public:
CWatchBSP() { 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; }
CWatchBSP() { m_bBSPPlugin = false; m_pListenSocket = NULL; m_pInSocket = NULL; m_eState = EIdle; m_pTimer = g_timer_new(); m_sBSPName = NULL; m_pCmd = NULL; m_iCurrentStep = 0; m_xmlInputBuffer = NULL; m_xmlParserCtxt = NULL; }
virtual ~CWatchBSP();
bool HasBSPPlugin() const
{ return m_bBSPPlugin; }