script, ui: added "sv_min_startmap_health" cvar
[k8vavoom.git] / source / sys_ded.cpp
blobb4bc1367361b4746342656a77d3d688ef300e044
1 //**************************************************************************
2 //**
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
9 //**
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
12 //**
13 //** This program is free software: you can redistribute it and/or modify
14 //** it under the terms of the GNU General Public License as published by
15 //** the Free Software Foundation, version 3 of the License ONLY.
16 //**
17 //** This program is distributed in the hope that it will be useful,
18 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 //** GNU General Public License for more details.
21 //**
22 //** You should have received a copy of the GNU General Public License
23 //** along with this program. If not, see <http://www.gnu.org/licenses/>.
24 //**
25 //**************************************************************************
26 //**
27 //** System driver for DOS, LINUX and UNIX dedicated servers.
28 //**
29 //**************************************************************************
30 #if defined(USE_FPU_MATH)
31 # define VAVOOM_ALLOW_FPU_DEBUG
32 #elif defined(__linux__)
33 # if defined(__x86_64__) || defined(__i386__)
34 # define VAVOOM_ALLOW_FPU_DEBUG
35 # endif
36 #endif
38 #if !defined(VAVOOM_K8_DEVELOPER) && defined(VAVOOM_ALLOW_FPU_DEBUG)
39 # undef VAVOOM_ALLOW_FPU_DEBUG
40 #endif
42 #ifdef VAVOOM_ALLOW_FPU_DEBUG
43 # ifndef _GNU_SOURCE
44 # define _GNU_SOURCE
45 # endif
46 # include <fenv.h>
47 #endif
49 #include "gamedefs.h"
50 #include "host.h"
51 #include "filesys/files.h"
53 #include <signal.h>
54 #include <fcntl.h>
55 #include <unistd.h>
56 #ifdef _WIN32
57 # define ftime fucked_ftime
58 # include <io.h>
59 # undef ftime
60 # include <conio.h>
61 # include <sys/timeb.h>
62 # include <windows.h>
63 #endif
64 #include <dirent.h>
65 #include <sys/stat.h>
66 #include <sys/time.h>
67 #include <time.h>
70 //==========================================================================
72 // Sys_Quit
74 // Shuts down net game, saves defaults, prints the exit text message,
75 // goes to text mode, and exits.
77 //==========================================================================
78 void Sys_Quit (const char *msg) {
79 //if (ttyIsGood()) ttySetRawMode(false);
80 Host_Shutdown();
81 Z_Exit(0);
85 //==========================================================================
87 // Sys_Shutdown
89 //==========================================================================
90 void Sys_Shutdown () {
92 if (ttyIsGood()) {
93 ttySetRawMode(false);
94 ttyRawWrite("\r\x1b[0m\x1b[K");
100 extern bool ttyRefreshInputLine;
101 extern bool ttyExtraDisabled;
102 extern bool dedEnableTTYLog;
104 static char text[8192];
105 #ifndef _WIN32
106 static char text2[8192];
107 #endif
108 static int textpos = 0;
111 #ifndef _WIN32
112 //==========================================================================
114 // onShowCompletionMatchCB
116 //==========================================================================
117 static void onShowCompletionMatchCB (bool isheader, VStr s) {
118 const bool olddis = dedEnableTTYLog;
119 dedEnableTTYLog = true;
120 if (isheader) {
121 GCon->Logf("\034K%s", *s);
122 } else {
123 GCon->Logf("\034D %s", *s);
125 dedEnableTTYLog = olddis;
127 #endif
130 //==========================================================================
132 // UpdateTTYPrompt
134 //==========================================================================
135 void UpdateTTYPrompt () {
136 if (!ttyRefreshInputLine) return;
137 ttyRefreshInputLine = false;
138 vassert(textpos < (int)ARRAY_COUNT(text));
139 text[textpos] = 0;
140 int wdt = ttyGetWidth();
141 if (wdt < 3) return; // just in case
142 char ssr[64];
143 snprintf(ssr, sizeof(ssr), "\x1b[%d;1H\x1b[44m\x1b[37;1m>", ttyGetHeight());
144 int stpos = textpos-(wdt-2);
145 if (stpos < 0) stpos = 0;
146 ttyRawWrite(ssr);
147 ttyRawWrite(text+stpos);
148 ttyRawWrite("\x1b[K"); // clear line
152 #ifndef _WIN32
153 //==========================================================================
155 // PutToTTYText
157 //==========================================================================
158 static void PutToTTYText (const char *s) {
159 if (!s || !s[0]) return;
160 ttyRefreshInputLine = true;
161 while (*s) {
162 if (textpos >= (int)ARRAY_COUNT(text)-3) {
163 ttyBeep();
164 return;
166 text[textpos++] = *s++;
169 #endif
172 //==========================================================================
174 // Sys_ConsoleInput
176 //==========================================================================
177 char *Sys_ConsoleInput () {
178 #ifdef _WIN32
179 return nullptr;
180 #else
181 if (!ttyIsAvailable()) return nullptr;
182 if (ttyExtraDisabled || !ttyIsGood()) {
183 int len;
184 fd_set fdset;
185 struct timeval timeout;
187 FD_ZERO(&fdset);
188 FD_SET(0, &fdset); // stdin
189 timeout.tv_sec = 0;
190 timeout.tv_usec = 0;
191 if (select(1, &fdset, nullptr, nullptr, &timeout) == -1 || !FD_ISSET(0, &fdset)) return nullptr;
193 len = read(0, text, sizeof(text));
194 if (len < 1) return nullptr;
195 text[len-1] = 0; // rip off the /n and terminate
197 return text;
200 UpdateTTYPrompt();
202 TTYEvent evt = ttyReadKey(0);
203 if (evt.type <= TTYEvent::Type::Unknown) return nullptr;
204 // C-smth
205 if (evt.type == TTYEvent::Type::ModChar) {
206 if (evt.ch == 'C') Sys_Quit("*** ABORTED ***");
207 if (evt.ch == 'Y') { textpos = 0; ttyRefreshInputLine = true; UpdateTTYPrompt(); return nullptr; }
208 } else if (evt.type == TTYEvent::Type::Enter) {
209 if (textpos == 0) return nullptr;
210 strcpy(text2, text);
211 textpos = 0;
212 text[0] = 0;
213 ttyRefreshInputLine = true;
214 UpdateTTYPrompt();
215 GCon->Logf(">%s", text2);
216 return text2;
219 if (evt.type == TTYEvent::Type::Backspace) {
220 if (textpos == 0) return nullptr;
221 text[--textpos] = 0;
222 ttyRefreshInputLine = true;
223 } else if (evt.type == TTYEvent::Type::Tab) {
224 // autocompletion
225 if (textpos == 0) return nullptr;
226 //TODO: autocompletion with moved cursor
227 const int curpos = textpos;
228 VStr clineRest(text); // after cursor
229 VStr cline = clineRest.left(curpos);
230 clineRest.chopLeft(curpos);
231 if (cline.length() && clineRest.length() && clineRest[0] == '"') {
232 cline += clineRest[0];
233 clineRest.chopLeft(1);
235 // find last command
236 int cmdstart = cline.findNextCommand();
237 for (;;) {
238 const int cmdstnext = cline.findNextCommand(cmdstart);
239 if (cmdstnext == cmdstart) break;
240 cmdstart = cmdstnext;
242 VStr oldpfx = cline;
243 oldpfx.chopLeft(cmdstart); // remove completed commands
244 VStr newpfx = VCommand::GetAutoComplete(oldpfx);
245 if (oldpfx != newpfx) {
246 textpos = 0;
247 PutToTTYText(*cline.left(cmdstart));
248 PutToTTYText(*newpfx);
249 // append rest of cline
250 if (clineRest.length()) {
251 //int cpos = textpos;
252 PutToTTYText(*clineRest);
253 //c_iline.setCurPos(cpos);
256 } else if (evt.type == TTYEvent::Type::Char) {
257 if (evt.ch >= ' ' && evt.ch < 127) {
258 char tmp[2];
259 tmp[0] = (char)evt.ch;
260 tmp[1] = 0;
261 PutToTTYText(tmp);
265 UpdateTTYPrompt();
266 return nullptr;
267 #endif
271 //==========================================================================
273 // signal_handler
275 // Shuts down system, on error signal
277 //==========================================================================
278 static volatile int sigReceived = 0;
280 static void signal_handler (int s) {
281 sigReceived = 1;
282 VObject::vmAbortBySignal += 1;
286 #ifndef _WIN32
287 extern "C" {
288 static void restoreTTYOnExit (void) {
289 if (ttyIsGood()) {
290 ttySetRawMode(false);
291 //ttyRawWrite("\x1b[9999F\x1b[0m\x1b[K");
292 ttyRawWrite("\r\x1b[0m\x1b[K");
296 #endif
299 //==========================================================================
301 // main
303 // Main program
305 //==========================================================================
306 int main (int argc, char **argv) {
307 try {
308 //printf("k8vavoom dedicated server " VERSION_TEXT "\n");
310 bool logEnabled = true; // postpone this
311 dedEnableTTYLog = true;
313 host_gdb_mode = false;
314 for (int f = 1; f < argc; ++f) {
315 if (strcmp(argv[f], "-gdb") == 0) {
316 host_gdb_mode = true;
317 ttyExtraDisabled = true;
318 for (int c = f+1; c < argc; ++c) argv[c-1] = argv[c];
319 --argc;
320 --f;
321 } else if (strcmp(argv[f], "-conlog") == 0) {
322 logEnabled = true;
323 for (int c = f+1; c < argc; ++c) argv[c-1] = argv[c];
324 --argc;
325 --f;
326 } else if (strcmp(argv[f], "-nonetlog") == 0 || strcmp(argv[f], "-nottylog") == 0) {
327 logEnabled = false;
328 for (int c = f+1; c < argc; ++c) argv[c-1] = argv[c];
329 --argc;
330 --f;
334 Sys_PinThread(); // pin main thread to one CPU
336 Host_InitStreamCallbacks();
338 VObject::StaticInitOptions(GParsedArgs);
339 FL_InitOptions();
340 GArgs.Init(argc, argv, "-file");
341 FL_CollectPreinits();
342 GParsedArgs.parse(GArgs);
344 #ifndef _WIN32
345 if (!ttyExtraDisabled && ttyIsGood()) {
346 ttySetRawMode(true);
347 atexit(&restoreTTYOnExit);
348 VCommand::onShowCompletionMatch = &onShowCompletionMatchCB;
349 //ttyRawWrite("\x1b[0m;\x1b[2J\x1b[9999F"); // clear, move cursor down
350 UpdateTTYPrompt();
352 #endif
354 #ifndef _WIN32
355 // install basic signal handlers
356 signal(SIGTERM, signal_handler);
357 signal(SIGINT, signal_handler);
358 signal(SIGQUIT, signal_handler);
359 #else
360 signal(SIGINT, signal_handler);
361 signal(SIGTERM, signal_handler);
362 signal(SIGBREAK,signal_handler);
363 signal(SIGABRT, signal_handler);
364 #endif
366 #ifdef VAVOOM_ALLOW_FPU_DEBUG
367 if (GArgs.CheckParm("-dev-fpu-alltraps") || GArgs.CheckParm("-dev-fpu-all-traps")) {
368 feenableexcept(FE_DIVBYZERO|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW);
369 } else if (GArgs.CheckParm("-dev-fpu-traps")) {
370 feenableexcept(FE_DIVBYZERO|FE_INVALID);
371 } else {
372 //GCon->Logf("ROUND: %d (%d); EXCEPT: %d", fegetround(), FLT_ROUNDS, fegetexcept());
373 feclearexcept(FE_ALL_EXCEPT);
375 // sse math can only round towards zero, so force it for FPU
376 if (fesetround(0) != 0) GCon->Logf(NAME_Warning, "Cannot set float rounding mode (this is not fatal)");
377 #endif
379 // initialise
380 Host_Init();
382 if (!logEnabled) {
383 GCon->Logf(NAME_Warning, "disabling TTY logs to avoid random slowdowns and disconnects.");
384 dedEnableTTYLog = false;
387 // play game
388 for (;;) {
389 Host_Frame();
390 if (sigReceived) {
391 GCon->Logf("*** SIGNAL RECEIVED ***");
392 Host_Shutdown();
393 fprintf(stderr, "*** TERMINATED BY SIGNAL ***\n");
394 break;
397 } catch (VavoomError &e) {
398 dedEnableTTYLog = true;
399 GCon->Logf(NAME_Error, "ERROR: %s", e.message);
400 Host_Shutdown();
401 //fprintf(stderr, "\nERROR: %s\n", e.message);
402 Z_Exit(1);
403 } catch (...) {
404 dedEnableTTYLog = true;
405 GCon->Log(NAME_Error, "Exiting due to external exception");
406 Host_Shutdown();
407 //fprintf(stderr, "\nExiting due to external exception\n");
408 throw;
411 Z_ShuttingDown();
412 return 0;