]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/watchbsp.cpp
create a branch for AB sync
[xonotic/netradiant.git] / radiant / watchbsp.cpp
1 /*\r
2 Copyright (c) 2001, Loki software, inc.\r
3 All rights reserved.\r
4 \r
5 Redistribution and use in source and binary forms, with or without modification, \r
6 are permitted provided that the following conditions are met:\r
7 \r
8 Redistributions of source code must retain the above copyright notice, this list \r
9 of conditions and the following disclaimer.\r
10 \r
11 Redistributions in binary form must reproduce the above copyright notice, this\r
12 list of conditions and the following disclaimer in the documentation and/or\r
13 other materials provided with the distribution.\r
14 \r
15 Neither the name of Loki software nor the names of its contributors may be used \r
16 to endorse or promote products derived from this software without specific prior \r
17 written permission. \r
18 \r
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' \r
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE \r
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \r
22 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY \r
23 DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES \r
24 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \r
25 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON \r
26 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT \r
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS \r
28 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \r
29 */\r
30 \r
31 //-----------------------------------------------------------------------------\r
32 //\r
33 // DESCRIPTION:\r
34 // monitoring window for running BSP processes (and possibly various other stuff)\r
35 \r
36 #include "stdafx.h"\r
37 #include "watchbsp.h"\r
38 #include "feedback.h"\r
39 \r
40 #ifdef _WIN32\r
41 #include <winsock2.h>\r
42 #endif\r
43 \r
44 #if defined (__linux__) || defined (__APPLE__)\r
45 #include <sys/time.h>\r
46 #define SOCKET_ERROR -1\r
47 #endif\r
48 \r
49 #ifdef __APPLE__\r
50 #include <unistd.h>\r
51 #endif\r
52 \r
53 #include <assert.h>\r
54 \r
55 // Static functions for the SAX callbacks -------------------------------------------------------\r
56 \r
57 // utility for saxStartElement below\r
58 static void abortStream(message_info_t *data)\r
59 {\r
60   g_pParentWnd->GetWatchBSP()->Reset();\r
61   // tell there has been an error\r
62   if (g_pParentWnd->GetWatchBSP()->HasBSPPlugin ())\r
63     g_BSPFrontendTable.m_pfnEndListen(2);\r
64   // yeah this doesn't look good.. but it's needed so that everything will be ignored until the stream goes out\r
65   data->ignore_depth = -1;\r
66   data->recurse++;\r
67 }\r
68 \r
69 #include "stream_version.h"\r
70 \r
71 static void saxStartElement(message_info_t *data, const xmlChar *name, const xmlChar **attrs) \r
72 {\r
73   if (data->ignore_depth == 0)\r
74   {\r
75     if (data->bGeometry)\r
76       // we have a handler\r
77     {\r
78       data->pGeometry->saxStartElement (data, name, attrs);\r
79     }\r
80     else\r
81     {\r
82       if (strcmp ((char *)name, "q3map_feedback") == 0)\r
83       {\r
84         // check the correct version\r
85         // old q3map don't send a version attribute\r
86         // the ones we support .. send Q3MAP_STREAM_VERSION\r
87         if (!attrs[0] || !attrs[1] || (strcmp((char*)attrs[0],"version") != 0))\r
88         {\r
89           Sys_FPrintf(SYS_ERR, "No stream version given in the feedback stream, this is an old q3map version.\n"\r
90                       "Please turn off monitored compiling if you still wish to use this q3map executable\n");\r
91           abortStream(data);\r
92           return;\r
93         }\r
94         else if (strcmp((char*)attrs[1],Q3MAP_STREAM_VERSION) != 0)\r
95         {\r
96           Sys_FPrintf(SYS_ERR, \r
97             "This version of Radiant reads version %s debug streams, I got an incoming connection with version %s\n"\r
98             "Please make sure your versions of Radiant and q3map are matching.\n", Q3MAP_STREAM_VERSION, (char*)attrs[1]);\r
99           abortStream(data);\r
100           return;\r
101         }\r
102       }\r
103       // we don't treat locally\r
104       else if (strcmp ((char *)name, "message") == 0)\r
105       {\r
106         data->msg_level = atoi ((char *)attrs[1]);\r
107       }\r
108       else if (strcmp ((char *)name, "polyline") == 0) \r
109       // polyline has a particular status .. right now we only use it for leakfile ..\r
110       {\r
111         data->bGeometry = true;\r
112         data->pGeometry = &g_pointfile;\r
113         data->pGeometry->saxStartElement (data, name, attrs);  \r
114       }\r
115       else if (strcmp ((char *)name, "select") == 0)\r
116       {\r
117         CSelectMsg *pSelect = new CSelectMsg();\r
118         data->bGeometry = true;\r
119         data->pGeometry = pSelect;\r
120         data->pGeometry->saxStartElement (data, name, attrs);\r
121       }\r
122       else if (strcmp ((char *)name, "pointmsg") == 0)\r
123       {\r
124         CPointMsg *pPoint = new CPointMsg();\r
125         data->bGeometry = true;\r
126         data->pGeometry = pPoint;\r
127         data->pGeometry->saxStartElement (data, name, attrs);\r
128       }\r
129       else if (strcmp ((char *)name, "windingmsg") == 0)\r
130       {\r
131         CWindingMsg *pWinding = new CWindingMsg();\r
132         data->bGeometry = true;\r
133         data->pGeometry = pWinding;\r
134         data->pGeometry->saxStartElement (data, name, attrs);\r
135       }\r
136       else\r
137       {\r
138         Sys_FPrintf (SYS_WRN, "WARNING: ignoring unrecognized node in XML stream (%s)\n", name);\r
139         // we don't recognize this node, jump over it\r
140         // (NOTE: the ignore mechanism is a bit screwed, only works when starting an ignore at the highest level)\r
141         data->ignore_depth = data->recurse;\r
142       }\r
143     }\r
144   }\r
145   data->recurse++;\r
146 }\r
147 \r
148 static void saxEndElement(message_info_t *data, const xmlChar *name) \r
149 {\r
150   data->recurse--;\r
151   // we are out of an ignored chunk\r
152   if (data->recurse == data->ignore_depth)\r
153   {\r
154     data->ignore_depth = 0;\r
155     return;\r
156   }\r
157   if (data->bGeometry)\r
158   {\r
159     data->pGeometry->saxEndElement (data, name);\r
160     // we add the object to the debug window\r
161     if (!data->bGeometry)\r
162     {\r
163       g_DbgDlg.Push (data->pGeometry);\r
164     }\r
165   }\r
166   if (data->recurse == data->stop_depth)\r
167   {\r
168 #ifdef _DEBUG\r
169     Sys_Printf ("Received error msg .. shutting down..\n");\r
170 #endif\r
171     g_pParentWnd->GetWatchBSP()->Reset();\r
172     // tell there has been an error\r
173     if (g_pParentWnd->GetWatchBSP()->HasBSPPlugin ())\r
174       g_BSPFrontendTable.m_pfnEndListen(2);\r
175     return;\r
176   }\r
177 }\r
178 \r
179 static void saxCharacters(message_info_t *data, const xmlChar *ch, int len)\r
180 {\r
181   if (data->bGeometry)\r
182   {\r
183     data->pGeometry->saxCharacters (data, ch, len);\r
184   }\r
185   else\r
186   {\r
187     if (data->ignore_depth != 0)\r
188       return;\r
189     // output the message using the level\r
190     char buf[1024];\r
191     memcpy( buf, ch, len );\r
192     buf[len] = '\0';\r
193     Sys_FPrintf (data->msg_level, "%s", buf);\r
194     // if this message has error level flag, we mark the depth to stop the compilation when we get out\r
195     // we don't set the msg level if we don't stop on leak\r
196     if (data->msg_level == 3)\r
197     {\r
198       data->stop_depth = data->recurse-1;\r
199     }\r
200   }\r
201 }\r
202 \r
203 static void saxComment(void *ctx, const xmlChar *msg)\r
204 {\r
205   Sys_Printf("XML comment: %s\n", msg);\r
206 }\r
207 \r
208 static void saxWarning(void *ctx, const char *msg, ...)\r
209 {\r
210   char saxMsgBuffer[4096];\r
211   va_list args;\r
212  \r
213   va_start(args, msg);\r
214   vsprintf (saxMsgBuffer, msg, args);\r
215   va_end(args);\r
216   Sys_FPrintf(SYS_WRN, "XML warning: %s\n", saxMsgBuffer);\r
217 }\r
218 \r
219 static void saxError(void *ctx, const char *msg, ...)\r
220 {\r
221   char saxMsgBuffer[4096];\r
222   va_list args;\r
223  \r
224   va_start(args, msg);\r
225   vsprintf (saxMsgBuffer, msg, args);\r
226   va_end(args);\r
227   Sys_FPrintf(SYS_ERR, "XML error: %s\n", saxMsgBuffer);\r
228 }\r
229 \r
230 static void saxFatal(void *ctx, const char *msg, ...)\r
231 {\r
232   char buffer[4096];\r
233   \r
234   va_list args;\r
235  \r
236   va_start(args, msg);\r
237   vsprintf (buffer, msg, args);\r
238   va_end(args);\r
239   Sys_FPrintf(SYS_ERR, "XML fatal error: %s\n", buffer);\r
240 }\r
241 \r
242 static xmlSAXHandler saxParser = {\r
243     0, /* internalSubset */\r
244     0, /* isStandalone */\r
245     0, /* hasInternalSubset */\r
246     0, /* hasExternalSubset */\r
247     0, /* resolveEntity */\r
248     0, /* getEntity */\r
249     0, /* entityDecl */\r
250     0, /* notationDecl */\r
251     0, /* attributeDecl */\r
252     0, /* elementDecl */\r
253     0, /* unparsedEntityDecl */\r
254     0, /* setDocumentLocator */\r
255     0, /* startDocument */\r
256     0, /* endDocument */\r
257     (startElementSAXFunc)saxStartElement, /* startElement */\r
258     (endElementSAXFunc)saxEndElement, /* endElement */\r
259     0, /* reference */\r
260     (charactersSAXFunc)saxCharacters, /* characters */\r
261     0, /* ignorableWhitespace */\r
262     0, /* processingInstruction */\r
263     (commentSAXFunc)saxComment, /* comment */\r
264     (warningSAXFunc)saxWarning, /* warning */\r
265     (errorSAXFunc)saxError, /* error */\r
266     (fatalErrorSAXFunc)saxFatal, /* fatalError */\r
267 };\r
268 \r
269 // ------------------------------------------------------------------------------------------------\r
270 \r
271 CWatchBSP::~CWatchBSP()\r
272 {\r
273   Reset();\r
274   if (m_sBSPName)\r
275   {\r
276     delete[] m_sBSPName;\r
277     m_sBSPName = NULL;\r
278   }\r
279   Net_Shutdown();\r
280 }\r
281 \r
282 void CWatchBSP::Reset()\r
283 {\r
284   if (m_pInSocket)\r
285   {\r
286     Net_Disconnect(m_pInSocket);\r
287     m_pInSocket = NULL;\r
288   }\r
289   if (m_pListenSocket)\r
290   {\r
291     Net_Disconnect(m_pListenSocket);\r
292     m_pListenSocket = NULL;\r
293   }\r
294   if (m_xmlInputBuffer)\r
295   {\r
296     xmlFreeParserInputBuffer (m_xmlInputBuffer);\r
297     m_xmlInputBuffer = NULL;\r
298   }\r
299   m_eState = EIdle;\r
300 }\r
301 \r
302 bool CWatchBSP::SetupListening()\r
303 {\r
304 #ifdef _DEBUG\r
305   if (m_pListenSocket)\r
306   {\r
307     Sys_Printf("ERROR: m_pListenSocket != NULL in CWatchBSP::SetupListening\n");\r
308     return false;\r
309   }\r
310 #endif\r
311   Sys_Printf("Setting up\n");\r
312         Net_Setup();\r
313         m_pListenSocket = Net_ListenSocket(39000);\r
314   if (m_pListenSocket == NULL)\r
315     return false;\r
316   Sys_Printf("Listening...\n");\r
317   return true;\r
318 }\r
319 \r
320 void CWatchBSP::DoEBeginStep()\r
321 {\r
322   Reset();\r
323   if (SetupListening() == false)\r
324   {\r
325     CString msg;\r
326     msg = "Failed to get a listening socket on port 39000.\nTry running with BSP monitoring disabled if you can't fix this.\n";\r
327     Sys_Printf (msg);\r
328     gtk_MessageBox (g_pParentWnd->m_pWidget, msg, "BSP monitoring", MB_OK | MB_ICONERROR);\r
329     return;\r
330   }\r
331   // set the timer for timeouts and step cancellation\r
332   g_timer_reset( m_pTimer );\r
333   g_timer_start( m_pTimer );\r
334 \r
335   if (!m_bBSPPlugin)\r
336   {\r
337     Sys_Printf("=== running BSP command ===\n%s\n", g_ptr_array_index( m_pCmd, m_iCurrentStep ) );\r
338     \r
339     if (!Q_Exec(NULL, (char *)g_ptr_array_index( m_pCmd, m_iCurrentStep ), NULL, true ))\r
340     {\r
341       CString msg;\r
342       msg = "Failed to execute the following command: ";\r
343       msg += (char *)g_ptr_array_index( m_pCmd, m_iCurrentStep );\r
344       msg += "\nCheck that the file exists and that you don't run out of system resources.\n";\r
345       Sys_Printf(msg);\r
346       gtk_MessageBox(g_pParentWnd->m_pWidget,  msg, "BSP monitoring", MB_OK | MB_ICONERROR );\r
347       return;\r
348     }\r
349     // re-initialise the debug window\r
350     if (m_iCurrentStep == 0)\r
351       g_DbgDlg.Init();\r
352   }\r
353   m_eState = EBeginStep;\r
354 }\r
355 \r
356 void CWatchBSP::RoutineProcessing()\r
357 {\r
358   // used for select()\r
359 #ifdef _WIN32\r
360     TIMEVAL tout = { 0, 0 };\r
361 #endif\r
362 #if defined (__linux__) || defined (__APPLE__)\r
363                 timeval tout;\r
364                 tout.tv_sec = 0;\r
365                 tout.tv_usec = 0;\r
366 #endif\r
367 \r
368   switch (m_eState)\r
369   {\r
370   case EBeginStep:\r
371     // timeout: if we don't get an incoming connection fast enough, go back to idle\r
372     if ( g_timer_elapsed( m_pTimer, NULL ) > g_PrefsDlg.m_iTimeout )\r
373     {\r
374       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 );\r
375       Reset();\r
376       if (m_bBSPPlugin)\r
377       {\r
378         // status == 1 : didn't get the connection\r
379         g_BSPFrontendTable.m_pfnEndListen(1);\r
380       }\r
381       return;\r
382     }\r
383 #ifdef _DEBUG\r
384     // some debug checks\r
385     if (!m_pListenSocket)\r
386     {\r
387       Sys_Printf("ERROR: m_pListenSocket == NULL in CWatchBSP::RoutineProcessing EBeginStep state\n");\r
388       return;\r
389     }\r
390 #endif\r
391     // we are not connected yet, accept any incoming connection\r
392     m_pInSocket = Net_Accept(m_pListenSocket);\r
393     if (m_pInSocket)\r
394     {\r
395       Sys_Printf("Connected.\n");\r
396       // prepare the message info struct for diving in\r
397       memset (&m_message_info, 0, sizeof(message_info_s)); \r
398       // a dumb flag to make sure we init the push parser context when first getting a msg\r
399       m_bNeedCtxtInit = true;\r
400       m_eState = EWatching;\r
401     }\r
402     break;\r
403   case EWatching:\r
404 #ifdef _DEBUG\r
405     // some debug checks\r
406     if (!m_pInSocket)\r
407     {\r
408       Sys_Printf("ERROR: m_pInSocket == NULL in CWatchBSP::RoutineProcessing EWatching state\n");\r
409       return;\r
410     }\r
411 #endif\r
412     // select() will identify if the socket needs an update\r
413     // if the socket is identified that means there's either a message or the connection has been closed/reset/terminated\r
414     fd_set readfds;\r
415     int ret;\r
416     FD_ZERO(&readfds);\r
417     FD_SET(((unsigned int)m_pInSocket->socket), &readfds);\r
418                 // from select man page:\r
419                 // n is the highest-numbered descriptor in any of the three sets, plus 1\r
420                 // (no use on windows)\r
421     ret = select( m_pInSocket->socket + 1, &readfds, NULL, NULL, &tout );\r
422     if (ret == SOCKET_ERROR)\r
423     {\r
424       Sys_Printf("WARNING: SOCKET_ERROR in CWatchBSP::RoutineProcessing\n");\r
425       Sys_Printf("Terminating the connection.\n");\r
426       Reset();\r
427       return;\r
428     }\r
429 #ifdef _DEBUG\r
430     if (ret == -1)\r
431     {\r
432       // we are non-blocking?? we should never get timeout errors\r
433       Sys_Printf("WARNING: unexpected timeout expired in CWatchBSP::Processing\n");\r
434       Sys_Printf("Terminating the connection.\n");\r
435       Reset();\r
436       return;\r
437     }\r
438 #endif\r
439     if (ret == 1)\r
440     {\r
441       // the socket has been identified, there's something (message or disconnection)\r
442       // see if there's anything in input\r
443       ret = Net_Receive( m_pInSocket, &msg );\r
444       if (ret > 0)\r
445       {\r
446         //        unsigned int size = msg.size; //++timo just a check\r
447         strcpy (m_xmlBuf, NMSG_ReadString (&msg));\r
448         if (m_bNeedCtxtInit)\r
449         {\r
450           m_xmlParserCtxt = NULL;\r
451           m_xmlParserCtxt = xmlCreatePushParserCtxt (&saxParser, &m_message_info, m_xmlBuf, strlen(m_xmlBuf), NULL);\r
452           if (m_xmlParserCtxt == NULL)\r
453           {\r
454             Sys_FPrintf (SYS_ERR, "Failed to create the XML parser (incoming stream began with: %s)\n", m_xmlBuf);\r
455             Reset();\r
456           }\r
457           m_bNeedCtxtInit = false;\r
458         }\r
459         else\r
460         {\r
461           xmlParseChunk (m_xmlParserCtxt, m_xmlBuf, strlen(m_xmlBuf), 0);\r
462         }\r
463       }\r
464       else\r
465       {\r
466         // error or connection closed/reset\r
467         // NOTE: if we get an error down the XML stream we don't reach here\r
468         Net_Disconnect( m_pInSocket );\r
469         m_pInSocket = NULL;\r
470         Sys_Printf("Connection closed.\n");\r
471         if (m_bBSPPlugin)\r
472         {\r
473           Reset();\r
474           // let the BSP plugin know that the job is done\r
475           g_BSPFrontendTable.m_pfnEndListen(0);\r
476           return;\r
477         }\r
478         // move to next step or finish\r
479         m_iCurrentStep++;\r
480         if (m_iCurrentStep < m_pCmd->len )\r
481         {\r
482           DoEBeginStep();\r
483         }\r
484         else\r
485         {\r
486           // release the GPtrArray and the strings\r
487           if (m_pCmd != NULL)\r
488           {\r
489             for (m_iCurrentStep=0; m_iCurrentStep < m_pCmd->len; m_iCurrentStep++ )\r
490             {\r
491               delete[] (char *)g_ptr_array_index( m_pCmd, m_iCurrentStep );\r
492             }\r
493             g_ptr_array_free( m_pCmd, false );\r
494           }\r
495           m_pCmd = NULL;\r
496           // launch the engine .. OMG\r
497           if (g_PrefsDlg.m_bRunQuake)\r
498           {\r
499             // do we enter sleep mode before?\r
500             if (g_PrefsDlg.m_bDoSleep)\r
501             {\r
502               Sys_Printf("Going into sleep mode..\n");\r
503               g_pParentWnd->OnSleep();\r
504             }\r
505             Sys_Printf("Running engine...\n");\r
506             Str cmd;\r
507             // build the command line\r
508             cmd = g_pGameDescription->mEnginePath.GetBuffer();\r
509             // this is game dependant\r
510             //!\todo Read the engine binary name from a config file.\r
511             if (g_pGameDescription->mGameFile == "wolf.game")\r
512             {\r
513               if (!strcmp(ValueForKey(g_qeglobals.d_project_entity, "gamemode"),"mp"))\r
514               {\r
515                 // MP\r
516 #if defined(WIN32)\r
517                 cmd += "WolfMP.exe";\r
518 #elif defined(__linux__)\r
519                 cmd += "wolfmp";\r
520 #elif defined(__APPLE__)\r
521                 cmd += "wolfmp.app";\r
522 #else\r
523 #error "WTF are you compiling on"\r
524 #endif\r
525               }\r
526               else\r
527               {\r
528                 // SP\r
529 #if defined(WIN32)\r
530                 cmd += "WolfSP.exe";\r
531 #elif defined(__linux__)\r
532                 cmd += "wolfsp";\r
533 #elif defined(__APPLE__)\r
534                 cmd += "wolfsp.app";\r
535 #else\r
536 #error "WTF are you compiling on"\r
537 #endif\r
538               }\r
539             } else if (g_pGameDescription->mGameFile == "et.game")\r
540             {\r
541 #if defined(WIN32)\r
542               cmd += "et.exe";\r
543 #elif defined(__linux__)\r
544               cmd += "et";\r
545 #elif defined(__APPLE__)\r
546               cmd += "et.app";\r
547 #else\r
548 #error "WTF are you compiling on"\r
549 #endif\r
550             }\r
551             // RIANT\r
552             // JK2 HACK\r
553             else if (g_pGameDescription->mGameFile == "jk2.game")\r
554             {\r
555               if (!strcmp(ValueForKey(g_qeglobals.d_project_entity, "gamemode"),"mp"))\r
556               {\r
557                 // MP\r
558 #if defined(WIN32)\r
559                 cmd += "jk2MP.exe";\r
560 #elif defined(__linux__)\r
561                 cmd += "jk2mp";\r
562 #elif defined(__APPLE__)\r
563                 cmd += "jk2mp.app";\r
564 #else\r
565 #error "WTF are you compiling on"\r
566 #endif\r
567               }\r
568               else\r
569               {\r
570                 // SP\r
571 #if defined(WIN32)\r
572                 cmd += "jk2SP.exe";\r
573 #elif defined(__linux__)\r
574                 cmd += "jk2sp";\r
575 #elif defined(__APPLE__)\r
576                 cmd += "jk2sp.app";\r
577 #else\r
578 #error "WTF are you compiling on"\r
579 #endif\r
580               }\r
581             }\r
582             // TTimo\r
583             // JA HACK\r
584             else if (g_pGameDescription->mGameFile == "ja.game")\r
585             {\r
586               if (!strcmp(ValueForKey(g_qeglobals.d_project_entity, "gamemode"),"mp"))\r
587               {\r
588                 // MP\r
589 #if defined(WIN32)\r
590                 cmd += "jamp.exe";\r
591 #elif !defined(__linux__) && !defined(__APPLE__)\r
592 #error "WTF are you compiling on"\r
593 #endif\r
594               }\r
595               else\r
596               {\r
597                 // SP\r
598 #if defined(WIN32)\r
599                 cmd += "jasp.exe";\r
600 #elif !defined(__linux__) && !defined(__APPLE__)\r
601 #error "WTF are you compiling on"\r
602 #endif\r
603               }\r
604             }\r
605             // RIANT\r
606             // STVEF HACK\r
607             else if (g_pGameDescription->mGameFile == "stvef.game")\r
608             {\r
609               if (!strcmp(ValueForKey(g_qeglobals.d_project_entity, "gamemode"),"mp"))\r
610               {\r
611                 // MP\r
612 #if defined(WIN32)\r
613                 cmd += "stvoyHM.exe";\r
614 #elif defined(__linux__)\r
615                 cmd += "stvoyHM";\r
616 #elif defined(__APPLE__)\r
617                 cmd += "stvoyHM.app";\r
618 #else\r
619 #error "WTF are you compiling on"\r
620 #endif\r
621               }\r
622               else\r
623               {\r
624                 // SP\r
625 #if defined(WIN32)\r
626                 cmd += "stvoy.exe";\r
627 #elif defined(__linux__)\r
628                 cmd += "stvoy";\r
629 #elif defined(__APPLE__)\r
630                 cmd += "stvoy.app";\r
631 #else\r
632 #error "WTF are you compiling on"\r
633 #endif\r
634               }\r
635             }\r
636             // RIANT\r
637             // SOF2 HACK\r
638             else if (g_pGameDescription->mGameFile == "sof2.game")\r
639             {\r
640               if (!strcmp(ValueForKey(g_qeglobals.d_project_entity, "gamemode"),"mp"))\r
641               {\r
642                 // MP\r
643 #if defined(WIN32)\r
644                 cmd += "sof2MP.exe";\r
645 #elif defined(__linux__)\r
646                 cmd += "b00gus";\r
647 #elif defined(__APPLE__)\r
648                 cmd += "sof2MP.app";\r
649 #else\r
650 #error "WTF are you compiling on"\r
651 #endif\r
652               }\r
653               else\r
654               {\r
655                 // SP\r
656 #if defined(WIN32)\r
657                 cmd += "sof2.exe";\r
658 #elif defined(__linux__)\r
659                 cmd += "b00gus";\r
660 #elif defined(__APPLE__)\r
661                 cmd += "sof2.app";\r
662 #else\r
663 #error "WTF are you compiling on"\r
664 #endif\r
665               }\r
666             }\r
667             else\r
668             {\r
669               cmd += g_pGameDescription->mEngine.GetBuffer();\r
670             }\r
671 #ifdef _WIN32\r
672             // NOTE: we are using unix pathnames and CreateProcess doesn't like / in the program path\r
673             FindReplace( cmd, "/", "\\" );\r
674 #endif\r
675             Str cmdline;\r
676             if ( (g_pGameDescription->mGameFile == "q2.game") || (g_pGameDescription->mGameFile == "heretic2.game") )\r
677             {\r
678                 cmdline = ". +exec radiant.cfg +map ";\r
679                 cmdline += m_sBSPName;\r
680             }\r
681             else\r
682             {\r
683               cmdline = "+set sv_pure 0 ";\r
684               // TTimo: a check for vm_* but that's all fine\r
685               //cmdline = "+set sv_pure 0 +set vm_ui 0 +set vm_cgame 0 +set vm_game 0 ";\r
686               if (*ValueForKey(g_qeglobals.d_project_entity, "gamename") != '\0')\r
687               {\r
688                 cmdline += "+set fs_game ";\r
689                 cmdline += ValueForKey(g_qeglobals.d_project_entity, "gamename");\r
690                 cmdline += " ";\r
691               }\r
692               //!\todo Read the start-map args from a config file.\r
693               if (g_pGameDescription->mGameFile == "wolf.game")\r
694               {\r
695                 if (!strcmp(ValueForKey(g_qeglobals.d_project_entity, "gamemode"),"mp"))\r
696                 {\r
697                   // MP\r
698                   cmdline += "+devmap ";\r
699                   cmdline += m_sBSPName;\r
700                 }\r
701                 else\r
702                 {\r
703                   // SP                \r
704                   cmdline += "+set nextmap \"spdevmap ";\r
705                   cmdline += m_sBSPName;\r
706                   cmdline += "\"";\r
707                 }\r
708               }\r
709               else\r
710               {\r
711                 cmdline += "+devmap ";\r
712                 cmdline += m_sBSPName;\r
713               }\r
714             }\r
715 \r
716             Sys_Printf("%s %s\n", cmd.GetBuffer(), cmdline.GetBuffer());\r
717 \r
718             // execute now\r
719             if (!Q_Exec(cmd.GetBuffer(), (char *)cmdline.GetBuffer(), g_pGameDescription->mEnginePath.GetBuffer(), false))\r
720             {\r
721               CString msg;\r
722               msg = "Failed to execute the following command: ";\r
723               msg += cmd; msg += cmdline;\r
724               Sys_Printf(msg);\r
725               gtk_MessageBox(g_pParentWnd->m_pWidget,  msg, "BSP monitoring", MB_OK | MB_ICONERROR );\r
726             }\r
727           }\r
728           Reset();\r
729         }\r
730       }\r
731     }\r
732     break;\r
733   default:\r
734     break;\r
735   }\r
736 }\r
737 \r
738 void CWatchBSP::DoMonitoringLoop( GPtrArray *pCmd, char *sBSPName )\r
739 {\r
740   if (m_sBSPName)\r
741   {\r
742     delete[] m_sBSPName;\r
743   }\r
744   m_sBSPName = sBSPName;\r
745   if (m_eState != EIdle)\r
746   {\r
747     Sys_Printf("WatchBSP got a monitoring request while not idling...\n");\r
748     // prompt the user, should we cancel the current process and go ahead?\r
749     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?", \r
750       "BSP process monitoring", MB_YESNO ) == IDYES)\r
751     {\r
752       // disconnect and set EIdle state\r
753       Reset();\r
754     }\r
755   }\r
756   m_pCmd = pCmd;\r
757   m_iCurrentStep = 0;\r
758   DoEBeginStep();\r
759 }\r
760 \r
761 void CWatchBSP::ExternalListen()\r
762 {\r
763   m_bBSPPlugin = true;\r
764   DoEBeginStep ();\r
765 }\r
766 \r
767 // the part of the watchbsp interface we export to plugins\r
768 // NOTE: in the long run, the whole watchbsp.cpp interface needs to go out and be handled at the BSP plugin level\r
769 // for now we provide something really basic and limited, the essential is to have something that works fine and fast (for 1.1 final)\r
770 void WINAPI QERApp_Listen()\r
771 {\r
772   // open the listening socket\r
773   g_pParentWnd->GetWatchBSP()->ExternalListen();\r
774 }\r