mirror of
https://github.com/TTimo/GtkRadiant.git
synced 2024-11-10 07:11:54 +00:00
Fix umpteen fails in the BSP monitoring state management. Probably introduce 1 or 2 new ones.
This commit is contained in:
parent
58c425637e
commit
a6934fb21b
2 changed files with 151 additions and 124 deletions
|
@ -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" ) ) {
|
||||
|
@ -526,7 +333,207 @@ void CWatchBSP::RoutineProcessing(){
|
|||
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 ) {
|
||||
printf("BEGIN AGAIN\n");
|
||||
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 +544,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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
Loading…
Reference in a new issue