]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - sys_shared.c
Implement Con_Error(f) and Con_Warn(f), error and warning, for prettier colors
[xonotic/darkplaces.git] / sys_shared.c
1 #ifdef WIN32
2 # ifndef DONT_USE_SETDLLDIRECTORY
3 #  define _WIN32_WINNT 0x0502
4 # endif
5 #endif
6
7 #include "quakedef.h"
8 #include "taskqueue.h"
9 #include "thread.h"
10
11 #define SUPPORTDLL
12
13 #ifdef WIN32
14 # include <windows.h>
15 # include <mmsystem.h> // timeGetTime
16 # include <time.h> // localtime
17 #ifdef _MSC_VER
18 #pragma comment(lib, "winmm.lib")
19 #endif
20 #else
21 # include <unistd.h>
22 # include <fcntl.h>
23 # include <sys/time.h>
24 # include <time.h>
25 # ifdef SUPPORTDLL
26 #  include <dlfcn.h>
27 # endif
28 #endif
29
30 static char sys_timestring[128];
31 char *Sys_TimeString(const char *timeformat)
32 {
33         time_t mytime = time(NULL);
34 #if _MSC_VER >= 1400
35         struct tm mytm;
36         localtime_s(&mytm, &mytime);
37         strftime(sys_timestring, sizeof(sys_timestring), timeformat, &mytm);
38 #else
39         strftime(sys_timestring, sizeof(sys_timestring), timeformat, localtime(&mytime));
40 #endif
41         return sys_timestring;
42 }
43
44
45 extern qboolean host_shuttingdown;
46 void Sys_Quit (int returnvalue)
47 {
48         // Unlock mutexes because the quit command may jump directly here, causing a deadlock
49         if (cmd_client.text_lock)
50                 Cbuf_Unlock(&cmd_client);
51         if (cmd_server.text_lock)
52                 Cbuf_Unlock(&cmd_server);
53         SV_UnlockThreadMutex();
54         TaskQueue_Frame(true);
55
56         if (COM_CheckParm("-profilegameonly"))
57                 Sys_AllowProfiling(false);
58         host_shuttingdown = true;
59         Host_Shutdown();
60         exit(returnvalue);
61 }
62
63 #ifdef __cplusplus
64 extern "C"
65 #endif
66 void Sys_AllowProfiling(qboolean enable)
67 {
68 #ifdef __ANDROID__
69 #ifdef USE_PROFILER
70         extern void monstartup(const char *libname);
71         extern void moncleanup(void);
72         if (enable)
73                 monstartup("libmain.so");
74         else
75                 moncleanup();
76 #endif
77 #elif defined(__linux__) || defined(__FreeBSD__)
78         extern int moncontrol(int);
79         moncontrol(enable);
80 #endif
81 }
82
83
84 /*
85 ===============================================================================
86
87 DLL MANAGEMENT
88
89 ===============================================================================
90 */
91
92 static qboolean Sys_LoadLibraryFunctions(dllhandle_t dllhandle, const dllfunction_t *fcts, qboolean complain, qboolean has_next)
93 {
94         const dllfunction_t *func;
95         if(dllhandle)
96         {
97                 for (func = fcts; func && func->name != NULL; func++)
98                         if (!(*func->funcvariable = (void *) Sys_GetProcAddress (dllhandle, func->name)))
99                         {
100                                 if(complain)
101                                 {
102                                         Con_DPrintf (" - missing function \"%s\" - broken library!", func->name);
103                                         if(has_next)
104                                                 Con_DPrintf("\nContinuing with");
105                                 }
106                                 goto notfound;
107                         }
108                 return true;
109
110         notfound:
111                 for (func = fcts; func && func->name != NULL; func++)
112                         *func->funcvariable = NULL;
113         }
114         return false;
115 }
116
117 qboolean Sys_LoadLibrary (const char** dllnames, dllhandle_t* handle, const dllfunction_t *fcts)
118 {
119 #ifdef SUPPORTDLL
120         const dllfunction_t *func;
121         dllhandle_t dllhandle = 0;
122         unsigned int i;
123
124         if (handle == NULL)
125                 return false;
126
127 #ifndef WIN32
128 #ifdef PREFER_PRELOAD
129         dllhandle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);
130         if(Sys_LoadLibraryFunctions(dllhandle, fcts, false, false))
131         {
132                 Con_DPrintf ("All of %s's functions were already linked in! Not loading dynamically...\n", dllnames[0]);
133                 *handle = dllhandle;
134                 return true;
135         }
136         else
137                 Sys_UnloadLibrary(&dllhandle);
138 notfound:
139 #endif
140 #endif
141
142         // Initializations
143         for (func = fcts; func && func->name != NULL; func++)
144                 *func->funcvariable = NULL;
145
146         // Try every possible name
147         Con_DPrintf ("Trying to load library...");
148         for (i = 0; dllnames[i] != NULL; i++)
149         {
150                 Con_DPrintf (" \"%s\"", dllnames[i]);
151 #ifdef WIN32
152 # ifndef DONT_USE_SETDLLDIRECTORY
153 #  ifdef _WIN64
154                 SetDllDirectory("bin64");
155 #  else
156                 SetDllDirectory("bin32");
157 #  endif
158 # endif
159                 dllhandle = LoadLibrary (dllnames[i]);
160                 // no need to unset this - we want ALL dlls to be loaded from there, anyway
161 #else
162                 dllhandle = dlopen (dllnames[i], RTLD_LAZY | RTLD_GLOBAL);
163 #endif
164                 if (Sys_LoadLibraryFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(com_argv[0], '/'))))
165                         break;
166                 else
167                         Sys_UnloadLibrary (&dllhandle);
168         }
169
170         // see if the names can be loaded relative to the executable path
171         // (this is for Mac OSX which does not check next to the executable)
172         if (!dllhandle && strrchr(com_argv[0], '/'))
173         {
174                 char path[MAX_OSPATH];
175                 strlcpy(path, com_argv[0], sizeof(path));
176                 strrchr(path, '/')[1] = 0;
177                 for (i = 0; dllnames[i] != NULL; i++)
178                 {
179                         char temp[MAX_OSPATH];
180                         strlcpy(temp, path, sizeof(temp));
181                         strlcat(temp, dllnames[i], sizeof(temp));
182                         Con_DPrintf (" \"%s\"", temp);
183 #ifdef WIN32
184                         dllhandle = LoadLibrary (temp);
185 #else
186                         dllhandle = dlopen (temp, RTLD_LAZY | RTLD_GLOBAL);
187 #endif
188                         if (Sys_LoadLibraryFunctions(dllhandle, fcts, true, dllnames[i+1] != NULL))
189                                 break;
190                         else
191                                 Sys_UnloadLibrary (&dllhandle);
192                 }
193         }
194
195         // No DLL found
196         if (! dllhandle)
197         {
198                 Con_DPrintf(" - failed.\n");
199                 return false;
200         }
201
202         Con_DPrintf(" - loaded.\n");
203
204         *handle = dllhandle;
205         return true;
206 #else
207         return false;
208 #endif
209 }
210
211 void Sys_UnloadLibrary (dllhandle_t* handle)
212 {
213 #ifdef SUPPORTDLL
214         if (handle == NULL || *handle == NULL)
215                 return;
216
217 #ifdef WIN32
218         FreeLibrary (*handle);
219 #else
220         dlclose (*handle);
221 #endif
222
223         *handle = NULL;
224 #endif
225 }
226
227 void* Sys_GetProcAddress (dllhandle_t handle, const char* name)
228 {
229 #ifdef SUPPORTDLL
230 #ifdef WIN32
231         return (void *)GetProcAddress (handle, name);
232 #else
233         return (void *)dlsym (handle, name);
234 #endif
235 #else
236         return NULL;
237 #endif
238 }
239
240 #ifdef WIN32
241 # define HAVE_TIMEGETTIME 1
242 # define HAVE_QUERYPERFORMANCECOUNTER 1
243 # define HAVE_Sleep 1
244 #endif
245
246 #ifndef WIN32
247 #if defined(CLOCK_MONOTONIC) || defined(CLOCK_HIRES)
248 # define HAVE_CLOCKGETTIME 1
249 #endif
250 // FIXME improve this check, manpage hints to DST_NONE
251 # define HAVE_GETTIMEOFDAY 1
252 #endif
253
254 #ifndef WIN32
255 // on Win32, select() cannot be used with all three FD list args being NULL according to MSDN
256 // (so much for POSIX...)
257 # ifdef FD_SET
258 #  define HAVE_SELECT 1
259 # endif
260 #endif
261
262 #ifndef WIN32
263 // FIXME improve this check
264 # define HAVE_USLEEP 1
265 #endif
266
267 // this one is referenced elsewhere
268 cvar_t sys_usenoclockbutbenchmark = {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sys_usenoclockbutbenchmark", "0", "don't use ANY real timing, and simulate a clock (for benchmarking); the game then runs as fast as possible. Run a QC mod with bots that does some stuff, then does a quit at the end, to benchmark a server. NEVER do this on a public server."};
269
270 // these are not
271 static cvar_t sys_debugsleep = {CVAR_CLIENT | CVAR_SERVER, "sys_debugsleep", "0", "write requested and attained sleep times to standard output, to be used with gnuplot"};
272 static cvar_t sys_usesdlgetticks = {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sys_usesdlgetticks", "0", "use SDL_GetTicks() timer (less accurate, for debugging)"};
273 static cvar_t sys_usesdldelay = {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sys_usesdldelay", "0", "use SDL_Delay() (less accurate, for debugging)"};
274 #if HAVE_QUERYPERFORMANCECOUNTER
275 static cvar_t sys_usequeryperformancecounter = {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sys_usequeryperformancecounter", "0", "use windows QueryPerformanceCounter timer (which has issues on multicore/multiprocessor machines and processors which are designed to conserve power) for timing rather than timeGetTime function (which has issues on some motherboards)"};
276 #endif
277 #if HAVE_CLOCKGETTIME
278 static cvar_t sys_useclockgettime = {CVAR_CLIENT | CVAR_SERVER | CVAR_SAVE, "sys_useclockgettime", "1", "use POSIX clock_gettime function (not adjusted by NTP on some older Linux kernels) for timing rather than gettimeofday (which has issues if the system time is stepped by ntpdate, or apparently on some Xen installations)"};
279 #endif
280
281 static double benchmark_time; // actually always contains an integer amount of milliseconds, will eventually "overflow"
282
283 void Sys_Init_Commands (void)
284 {
285         Cvar_RegisterVariable(&sys_debugsleep);
286         Cvar_RegisterVariable(&sys_usenoclockbutbenchmark);
287 #if HAVE_TIMEGETTIME || HAVE_QUERYPERFORMANCECOUNTER || HAVE_CLOCKGETTIME || HAVE_GETTIMEOFDAY
288         if(sys_supportsdlgetticks)
289         {
290                 Cvar_RegisterVariable(&sys_usesdlgetticks);
291                 Cvar_RegisterVariable(&sys_usesdldelay);
292         }
293 #endif
294 #if HAVE_QUERYPERFORMANCECOUNTER
295         Cvar_RegisterVariable(&sys_usequeryperformancecounter);
296 #endif
297 #if HAVE_CLOCKGETTIME
298         Cvar_RegisterVariable(&sys_useclockgettime);
299 #endif
300 }
301
302 double Sys_DirtyTime(void)
303 {
304         // first all the OPTIONAL timers
305
306         // benchmark timer (fake clock)
307         if(sys_usenoclockbutbenchmark.integer)
308         {
309                 double old_benchmark_time = benchmark_time;
310                 benchmark_time += 1;
311                 if(benchmark_time == old_benchmark_time)
312                         Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
313                 return benchmark_time * 0.000001;
314         }
315 #if HAVE_QUERYPERFORMANCECOUNTER
316         if (sys_usequeryperformancecounter.integer)
317         {
318                 // LadyHavoc: note to people modifying this code, DWORD is specifically defined as an unsigned 32bit number, therefore the 65536.0 * 65536.0 is fine.
319                 // QueryPerformanceCounter
320                 // platform:
321                 // Windows 95/98/ME/NT/2000/XP
322                 // features:
323                 // very accurate (CPU cycles)
324                 // known issues:
325                 // does not necessarily match realtime too well (tends to get faster and faster in win98)
326                 // wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
327                 double timescale;
328                 LARGE_INTEGER PerformanceFreq;
329                 LARGE_INTEGER PerformanceCount;
330
331                 if (QueryPerformanceFrequency (&PerformanceFreq))
332                 {
333                         QueryPerformanceCounter (&PerformanceCount);
334         
335                         #ifdef __BORLANDC__
336                         timescale = 1.0 / ((double) PerformanceFreq.u.LowPart + (double) PerformanceFreq.u.HighPart * 65536.0 * 65536.0);
337                         return ((double) PerformanceCount.u.LowPart + (double) PerformanceCount.u.HighPart * 65536.0 * 65536.0) * timescale;
338                         #else
339                         timescale = 1.0 / ((double) PerformanceFreq.LowPart + (double) PerformanceFreq.HighPart * 65536.0 * 65536.0);
340                         return ((double) PerformanceCount.LowPart + (double) PerformanceCount.HighPart * 65536.0 * 65536.0) * timescale;
341                         #endif
342                 }
343                 else
344                 {
345                         Con_Printf("No hardware timer available\n");
346                         // fall back to other clock sources
347                         Cvar_SetValueQuick(&sys_usequeryperformancecounter, false);
348                 }
349         }
350 #endif
351
352 #if HAVE_CLOCKGETTIME
353         if (sys_useclockgettime.integer)
354         {
355                 struct timespec ts;
356 #  ifdef CLOCK_MONOTONIC
357                 // linux
358                 clock_gettime(CLOCK_MONOTONIC, &ts);
359 #  else
360                 // sunos
361                 clock_gettime(CLOCK_HIGHRES, &ts);
362 #  endif
363                 return (double) ts.tv_sec + ts.tv_nsec / 1000000000.0;
364         }
365 #endif
366
367         // now all the FALLBACK timers
368         if(sys_supportsdlgetticks && sys_usesdlgetticks.integer)
369                 return (double) Sys_SDL_GetTicks() / 1000.0;
370 #if HAVE_GETTIMEOFDAY
371         {
372                 struct timeval tp;
373                 gettimeofday(&tp, NULL);
374                 return (double) tp.tv_sec + tp.tv_usec / 1000000.0;
375         }
376 #elif HAVE_TIMEGETTIME
377         {
378                 static int firsttimegettime = true;
379                 // timeGetTime
380                 // platform:
381                 // Windows 95/98/ME/NT/2000/XP
382                 // features:
383                 // reasonable accuracy (millisecond)
384                 // issues:
385                 // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
386
387                 // make sure the timer is high precision, otherwise different versions of windows have varying accuracy
388                 if (firsttimegettime)
389                 {
390                         timeBeginPeriod(1);
391                         firsttimegettime = false;
392                 }
393
394                 return (double) timeGetTime() / 1000.0;
395         }
396 #else
397         // fallback for using the SDL timer if no other timer is available
398         // this calls Sys_Error() if not linking against SDL
399         return (double) Sys_SDL_GetTicks() / 1000.0;
400 #endif
401 }
402
403 void Sys_Sleep(int microseconds)
404 {
405         double t = 0;
406         if(sys_usenoclockbutbenchmark.integer)
407         {
408                 if(microseconds)
409                 {
410                         double old_benchmark_time = benchmark_time;
411                         benchmark_time += microseconds;
412                         if(benchmark_time == old_benchmark_time)
413                                 Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
414                 }
415                 return;
416         }
417         if(sys_debugsleep.integer)
418         {
419                 t = Sys_DirtyTime();
420         }
421         if(sys_supportsdlgetticks && sys_usesdldelay.integer)
422         {
423                 Sys_SDL_Delay(microseconds / 1000);
424         }
425 #if HAVE_SELECT
426         else
427         {
428                 struct timeval tv;
429                 tv.tv_sec = microseconds / 1000000;
430                 tv.tv_usec = microseconds % 1000000;
431                 select(0, NULL, NULL, NULL, &tv);
432         }
433 #elif HAVE_USLEEP
434         else
435         {
436                 usleep(microseconds);
437         }
438 #elif HAVE_Sleep
439         else
440         {
441                 Sleep(microseconds / 1000);
442         }
443 #else
444         else
445         {
446                 Sys_SDL_Delay(microseconds / 1000);
447         }
448 #endif
449         if(sys_debugsleep.integer)
450         {
451                 t = Sys_DirtyTime() - t;
452                 Sys_PrintfToTerminal("%d %d # debugsleep\n", microseconds, (unsigned int)(t * 1000000));
453         }
454 }
455
456 void Sys_PrintfToTerminal(const char *fmt, ...)
457 {
458         va_list argptr;
459         char msg[MAX_INPUTLINE];
460
461         va_start(argptr,fmt);
462         dpvsnprintf(msg,sizeof(msg),fmt,argptr);
463         va_end(argptr);
464
465         Sys_PrintToTerminal(msg);
466 }
467
468 #ifndef WIN32
469 static const char *Sys_FindInPATH(const char *name, char namesep, const char *PATH, char pathsep, char *buf, size_t bufsize)
470 {
471         const char *p = PATH;
472         const char *q;
473         if(p && name)
474         {
475                 while((q = strchr(p, ':')))
476                 {
477                         dpsnprintf(buf, bufsize, "%.*s%c%s", (int)(q-p), p, namesep, name);
478                         if(FS_SysFileExists(buf))
479                                 return buf;
480                         p = q + 1;
481                 }
482                 if(!q) // none found - try the last item
483                 {
484                         dpsnprintf(buf, bufsize, "%s%c%s", p, namesep, name);
485                         if(FS_SysFileExists(buf))
486                                 return buf;
487                 }
488         }
489         return name;
490 }
491 #endif
492
493 static const char *Sys_FindExecutableName(void)
494 {
495 #if defined(WIN32)
496         return com_argv[0];
497 #else
498         static char exenamebuf[MAX_OSPATH+1];
499         ssize_t n = -1;
500 #if defined(__FreeBSD__)
501         n = readlink("/proc/curproc/file", exenamebuf, sizeof(exenamebuf)-1);
502 #elif defined(__linux__)
503         n = readlink("/proc/self/exe", exenamebuf, sizeof(exenamebuf)-1);
504 #endif
505         if(n > 0 && (size_t)(n) < sizeof(exenamebuf))
506         {
507                 exenamebuf[n] = 0;
508                 return exenamebuf;
509         }
510         if(strchr(com_argv[0], '/'))
511                 return com_argv[0]; // possibly a relative path
512         else
513                 return Sys_FindInPATH(com_argv[0], '/', getenv("PATH"), ':', exenamebuf, sizeof(exenamebuf));
514 #endif
515 }
516
517 void Sys_ProvideSelfFD(void)
518 {
519         if(com_selffd != -1)
520                 return;
521         com_selffd = FS_SysOpenFD(Sys_FindExecutableName(), "rb", false);
522 }
523
524 // for x86 cpus only...  (x64 has SSE2_PRESENT)
525 #if defined(SSE_POSSIBLE) && !defined(SSE2_PRESENT)
526 // code from SDL, shortened as we can expect CPUID to work
527 static int CPUID_Features(void)
528 {
529         int features = 0;
530 # if defined(__GNUC__) && defined(__i386__)
531         __asm__ (
532 "        movl    %%ebx,%%edi\n"
533 "        xorl    %%eax,%%eax                                           \n"
534 "        incl    %%eax                                                 \n"
535 "        cpuid                       # Get family/model/stepping/features\n"
536 "        movl    %%edx,%0                                              \n"
537 "        movl    %%edi,%%ebx\n"
538         : "=m" (features)
539         :
540         : "%eax", "%ecx", "%edx", "%edi"
541         );
542 # elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
543         __asm {
544         xor     eax, eax
545         inc     eax
546         cpuid                       ; Get family/model/stepping/features
547         mov     features, edx
548         }
549 # else
550 #  error SSE_POSSIBLE set but no CPUID implementation
551 # endif
552         return features;
553 }
554 #endif
555
556 #ifdef SSE_POSSIBLE
557 qboolean Sys_HaveSSE(void)
558 {
559         // COMMANDLINEOPTION: SSE: -nosse disables SSE support and detection
560         if(COM_CheckParm("-nosse"))
561                 return false;
562 #ifdef SSE_PRESENT
563         return true;
564 #else
565         // COMMANDLINEOPTION: SSE: -forcesse enables SSE support and disables detection
566         if(COM_CheckParm("-forcesse") || COM_CheckParm("-forcesse2"))
567                 return true;
568         if(CPUID_Features() & (1 << 25))
569                 return true;
570         return false;
571 #endif
572 }
573
574 qboolean Sys_HaveSSE2(void)
575 {
576         // COMMANDLINEOPTION: SSE2: -nosse2 disables SSE2 support and detection
577         if(COM_CheckParm("-nosse") || COM_CheckParm("-nosse2"))
578                 return false;
579 #ifdef SSE2_PRESENT
580         return true;
581 #else
582         // COMMANDLINEOPTION: SSE2: -forcesse2 enables SSE2 support and disables detection
583         if(COM_CheckParm("-forcesse2"))
584                 return true;
585         if((CPUID_Features() & (3 << 25)) == (3 << 25)) // SSE is 1<<25, SSE2 is 1<<26
586                 return true;
587         return false;
588 #endif
589 }
590 #endif
591
592 /// called to set process priority for dedicated servers
593 #if defined(__linux__)
594 #include <sys/resource.h>
595 #include <errno.h>
596 static int nicelevel;
597 static qboolean nicepossible;
598 static qboolean isnice;
599 void Sys_InitProcessNice (void)
600 {
601         struct rlimit lim;
602         nicepossible = false;
603         if(COM_CheckParm("-nonice"))
604                 return;
605         errno = 0;
606         nicelevel = getpriority(PRIO_PROCESS, 0);
607         if(errno)
608         {
609                 Con_Printf("Kernel does not support reading process priority - cannot use niceness\n");
610                 return;
611         }
612         if(getrlimit(RLIMIT_NICE, &lim))
613         {
614                 Con_Printf("Kernel does not support lowering nice level again - cannot use niceness\n");
615                 return;
616         }
617         if(lim.rlim_cur != RLIM_INFINITY && nicelevel < (int) (20 - lim.rlim_cur))
618         {
619                 Con_Printf("Current nice level is below the soft limit - cannot use niceness\n");
620                 return;
621         }
622         nicepossible = true;
623         isnice = false;
624 }
625 void Sys_MakeProcessNice (void)
626 {
627         if(!nicepossible)
628                 return;
629         if(isnice)
630                 return;
631         Con_DPrintf("Process is becoming 'nice'...\n");
632         if(setpriority(PRIO_PROCESS, 0, 19))
633                 Con_Errorf("Failed to raise nice level to %d\n", 19);
634         isnice = true;
635 }
636 void Sys_MakeProcessMean (void)
637 {
638         if(!nicepossible)
639                 return;
640         if(!isnice)
641                 return;
642         Con_DPrintf("Process is becoming 'mean'...\n");
643         if(setpriority(PRIO_PROCESS, 0, nicelevel))
644                 Con_Errorf("Failed to lower nice level to %d\n", nicelevel);
645         isnice = false;
646 }
647 #else
648 void Sys_InitProcessNice (void)
649 {
650 }
651 void Sys_MakeProcessNice (void)
652 {
653 }
654 void Sys_MakeProcessMean (void)
655 {
656 }
657 #endif