1 //**************************************************************************
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
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.
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.
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/>.
25 //**************************************************************************
27 //** System driver for DOS, LINUX and UNIX dedicated servers.
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
38 #if !defined(VAVOOM_K8_DEVELOPER) && defined(VAVOOM_ALLOW_FPU_DEBUG)
39 # undef VAVOOM_ALLOW_FPU_DEBUG
42 #ifdef VAVOOM_ALLOW_FPU_DEBUG
51 #include "filesys/files.h"
57 # define ftime fucked_ftime
61 # include <sys/timeb.h>
70 //==========================================================================
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);
85 //==========================================================================
89 //==========================================================================
90 void Sys_Shutdown () {
94 ttyRawWrite("\r\x1b[0m\x1b[K");
100 extern bool ttyRefreshInputLine
;
101 extern bool ttyExtraDisabled
;
102 extern bool dedEnableTTYLog
;
104 static char text
[8192];
106 static char text2
[8192];
108 static int textpos
= 0;
112 //==========================================================================
114 // onShowCompletionMatchCB
116 //==========================================================================
117 static void onShowCompletionMatchCB (bool isheader
, VStr s
) {
118 const bool olddis
= dedEnableTTYLog
;
119 dedEnableTTYLog
= true;
121 GCon
->Logf("\034K%s", *s
);
123 GCon
->Logf("\034D %s", *s
);
125 dedEnableTTYLog
= olddis
;
130 //==========================================================================
134 //==========================================================================
135 void UpdateTTYPrompt () {
136 if (!ttyRefreshInputLine
) return;
137 ttyRefreshInputLine
= false;
138 vassert(textpos
< (int)ARRAY_COUNT(text
));
140 int wdt
= ttyGetWidth();
141 if (wdt
< 3) return; // just in case
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;
147 ttyRawWrite(text
+stpos
);
148 ttyRawWrite("\x1b[K"); // clear line
153 //==========================================================================
157 //==========================================================================
158 static void PutToTTYText (const char *s
) {
159 if (!s
|| !s
[0]) return;
160 ttyRefreshInputLine
= true;
162 if (textpos
>= (int)ARRAY_COUNT(text
)-3) {
166 text
[textpos
++] = *s
++;
172 //==========================================================================
176 //==========================================================================
177 char *Sys_ConsoleInput () {
181 if (!ttyIsAvailable()) return nullptr;
182 if (ttyExtraDisabled
|| !ttyIsGood()) {
185 struct timeval timeout
;
188 FD_SET(0, &fdset
); // stdin
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
202 TTYEvent evt
= ttyReadKey(0);
203 if (evt
.type
<= TTYEvent::Type::Unknown
) return nullptr;
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;
213 ttyRefreshInputLine
= true;
215 GCon
->Logf(">%s", text2
);
219 if (evt
.type
== TTYEvent::Type::Backspace
) {
220 if (textpos
== 0) return nullptr;
222 ttyRefreshInputLine
= true;
223 } else if (evt
.type
== TTYEvent::Type::Tab
) {
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);
236 int cmdstart
= cline
.findNextCommand();
238 const int cmdstnext
= cline
.findNextCommand(cmdstart
);
239 if (cmdstnext
== cmdstart
) break;
240 cmdstart
= cmdstnext
;
243 oldpfx
.chopLeft(cmdstart
); // remove completed commands
244 VStr newpfx
= VCommand::GetAutoComplete(oldpfx
);
245 if (oldpfx
!= newpfx
) {
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) {
259 tmp
[0] = (char)evt
.ch
;
271 //==========================================================================
275 // Shuts down system, on error signal
277 //==========================================================================
278 static volatile int sigReceived
= 0;
280 static void signal_handler (int s
) {
282 VObject::vmAbortBySignal
+= 1;
288 static void restoreTTYOnExit (void) {
290 ttySetRawMode(false);
291 //ttyRawWrite("\x1b[9999F\x1b[0m\x1b[K");
292 ttyRawWrite("\r\x1b[0m\x1b[K");
299 //==========================================================================
305 //==========================================================================
306 int main (int argc
, char **argv
) {
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
];
321 } else if (strcmp(argv
[f
], "-conlog") == 0) {
323 for (int c
= f
+1; c
< argc
; ++c
) argv
[c
-1] = argv
[c
];
326 } else if (strcmp(argv
[f
], "-nonetlog") == 0 || strcmp(argv
[f
], "-nottylog") == 0) {
328 for (int c
= f
+1; c
< argc
; ++c
) argv
[c
-1] = argv
[c
];
334 Sys_PinThread(); // pin main thread to one CPU
336 Host_InitStreamCallbacks();
338 VObject::StaticInitOptions(GParsedArgs
);
340 GArgs
.Init(argc
, argv
, "-file");
341 FL_CollectPreinits();
342 GParsedArgs
.parse(GArgs
);
345 if (!ttyExtraDisabled
&& ttyIsGood()) {
347 atexit(&restoreTTYOnExit
);
348 VCommand::onShowCompletionMatch
= &onShowCompletionMatchCB
;
349 //ttyRawWrite("\x1b[0m;\x1b[2J\x1b[9999F"); // clear, move cursor down
355 // install basic signal handlers
356 signal(SIGTERM
, signal_handler
);
357 signal(SIGINT
, signal_handler
);
358 signal(SIGQUIT
, signal_handler
);
360 signal(SIGINT
, signal_handler
);
361 signal(SIGTERM
, signal_handler
);
362 signal(SIGBREAK
,signal_handler
);
363 signal(SIGABRT
, signal_handler
);
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
);
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)");
383 GCon
->Logf(NAME_Warning
, "disabling TTY logs to avoid random slowdowns and disconnects.");
384 dedEnableTTYLog
= false;
391 GCon
->Logf("*** SIGNAL RECEIVED ***");
393 fprintf(stderr
, "*** TERMINATED BY SIGNAL ***\n");
397 } catch (VavoomError
&e
) {
398 dedEnableTTYLog
= true;
399 GCon
->Logf(NAME_Error
, "ERROR: %s", e
.message
);
401 //fprintf(stderr, "\nERROR: %s\n", e.message);
404 dedEnableTTYLog
= true;
405 GCon
->Log(NAME_Error
, "Exiting due to external exception");
407 //fprintf(stderr, "\nExiting due to external exception\n");