tron@2186: /* $Id$ */ tron@2186: tron@2189: #include "../stdafx.h" truelight@543: truelight@543: #ifdef ENABLE_NETWORK truelight@543: tron@2195: #include "../openttd.h" tron@2189: #include "../debug.h" tron@2189: #include "../functions.h" tron@2189: #include "../gfx.h" rubidium@5720: #include "../network/network.h" tron@2189: #include "../window.h" tron@2189: #include "../console.h" tron@2189: #include "../variables.h" truelight@4300: #include "../genworld.h" richk@6720: #include "../fileio.h" richk@6720: #include "../blitter/factory.hpp" tron@2195: #include "dedicated_v.h" truelight@781: bjarni@2497: #ifdef BEOS_NET_SERVER bjarni@2497: #include bjarni@2497: #endif bjarni@2497: truelight@781: #ifdef __OS2__ truelight@781: # include /* gettimeofday */ truelight@781: # include truelight@781: # include truelight@781: # include orudge@2181: orudge@2181: # define INCL_DOS orudge@2181: # include orudge@2181: truelight@781: # define STDIN 0 /* file descriptor for standard input */ truelight@810: ludde@2261: /** tron@2180: * Switches OpenTTD to a console app at run-time, instead of a PM app tron@2180: * Necessary to see stdout, etc. */ rubidium@6573: static void OS2_SwitchToConsoleMode() tron@2180: { tron@2180: PPIB pib; tron@2180: PTIB tib; tron@2180: tron@2180: DosGetInfoBlocks(&tib, &pib); tron@2180: tron@2180: // Change flag from PM to VIO tron@2180: pib->pib_ultype = 3; tron@2180: } truelight@781: #endif truelight@781: truelight@6359: #if defined(UNIX) || defined(PSP) truelight@543: # include /* gettimeofday */ truelight@543: # include truelight@543: # include truelight@702: # include truelight@543: # define STDIN 0 /* file descriptor for standard input */ truelight@6359: # if defined(PSP) truelight@6359: # include truelight@6359: # include truelight@6359: # endif /* PSP */ truelight@543: truelight@702: /* Signal handlers */ truelight@704: static void DedicatedSignalHandler(int sig) truelight@702: { truelight@736: _exit_game = true; truelight@736: signal(sig, DedicatedSignalHandler); truelight@702: } truelight@702: #endif truelight@702: rubidium@6800: #if defined(WIN32) rubidium@6800: # include /* GetTickCount */ rubidium@6800: # if !defined(WINCE) rubidium@6800: # include rubidium@6800: # endif rubidium@6800: # include rubidium@6800: # include Darkvater@4599: static HANDLE _hInputReady, _hWaitForInputHandling; Darkvater@4599: static HANDLE _hThread; // Thread to close darkvater@1046: static char _win_console_thread_buffer[200]; darkvater@1046: darkvater@1046: /* Windows Console thread. Just loop and signal when input has been received */ rubidium@6573: static void WINAPI CheckForConsoleInput() darkvater@1046: { rubidium@6800: #if defined(WINCE) rubidium@6800: /* WinCE doesn't support console stuff */ rubidium@6800: return; rubidium@6800: #else glx@6591: DWORD nb; glx@6591: HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); darkvater@1046: while (true) { glx@6591: ReadFile(hStdin, _win_console_thread_buffer, lengthof(_win_console_thread_buffer), &nb, NULL); Darkvater@4599: /* Signal input waiting that input is read and wait for it being handled Darkvater@4599: * SignalObjectAndWait() should be used here, but it's unsupported in Win98< */ Darkvater@4599: SetEvent(_hInputReady); Darkvater@4599: WaitForSingleObject(_hWaitForInputHandling, INFINITE); darkvater@1046: } rubidium@6800: #endif darkvater@1046: } darkvater@1046: rubidium@6573: static void CreateWindowsConsoleThread() darkvater@1046: { Darkvater@1762: DWORD dwThreadId; darkvater@1046: /* Create event to signal when console input is ready */ Darkvater@4599: _hInputReady = CreateEvent(NULL, false, false, NULL); Darkvater@4599: _hWaitForInputHandling = CreateEvent(NULL, false, false, NULL); Darkvater@4599: if (_hInputReady == NULL || _hWaitForInputHandling == NULL) error("Cannot create console event!"); darkvater@1046: Darkvater@4599: _hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CheckForConsoleInput, NULL, 0, &dwThreadId); Darkvater@4599: if (_hThread == NULL) error("Cannot create console thread!"); darkvater@1046: Darkvater@5568: DEBUG(driver, 2, "Windows console thread started"); darkvater@1046: } darkvater@1046: rubidium@6573: static void CloseWindowsConsoleThread() darkvater@1046: { Darkvater@4599: CloseHandle(_hThread); Darkvater@4599: CloseHandle(_hInputReady); Darkvater@4599: CloseHandle(_hWaitForInputHandling); Darkvater@5568: DEBUG(driver, 2, "Windows console thread shut down"); darkvater@1046: } darkvater@1046: darkvater@1046: #endif darkvater@1046: tron@2180: richk@6720: static void *_dedicated_video_mem; tron@2180: richk@6720: extern bool SafeSaveOrLoad(const char *filename, int mode, int newgm, Subdirectory subdir); tron@2180: extern void SwitchMode(int new_mode); tron@2180: richk@6720: static FVideoDriver_Dedicated iFVideoDriver_Dedicated; tron@2180: richk@6720: richk@6720: const char *VideoDriver_Dedicated::Start(const char * const *parm) tron@1301: { richk@6720: int bpp = BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth(); richk@6720: if (bpp == 0) _dedicated_video_mem = NULL; richk@6720: else _dedicated_video_mem = malloc(_cur_resolution[0] * _cur_resolution[1] * (bpp / 8)); richk@6720: truelight@543: _screen.width = _screen.pitch = _cur_resolution[0]; truelight@543: _screen.height = _cur_resolution[1]; truelight@543: Darkvater@5657: SetDebugString("net=6"); truelight@543: rubidium@6800: #if defined(WINCE) rubidium@6800: /* WinCE doesn't support console stuff */ rubidium@6800: #elif defined(WIN32) Darkvater@5168: // For win32 we need to allocate a console (debug mode does the same) truelight@626: CreateConsole(); darkvater@1046: CreateWindowsConsoleThread(); Darkvater@5168: SetConsoleTitle(_T("OpenTTD Dedicated Server")); truelight@626: #endif truelight@626: truelight@810: #ifdef __OS2__ truelight@810: // For OS/2 we also need to switch to console mode instead of PM mode truelight@810: OS2_SwitchToConsoleMode(); truelight@810: #endif truelight@810: Darkvater@5568: DEBUG(driver, 1, "Loading dedicated server"); truelight@543: return NULL; truelight@543: } darkvater@1046: richk@6720: void VideoDriver_Dedicated::Stop() darkvater@1046: { darkvater@1046: #ifdef WIN32 darkvater@1046: CloseWindowsConsoleThread(); darkvater@1046: #endif tron@1109: free(_dedicated_video_mem); darkvater@1046: } darkvater@1046: richk@6720: void VideoDriver_Dedicated::MakeDirty(int left, int top, int width, int height) {} richk@6720: bool VideoDriver_Dedicated::ChangeResolution(int w, int h) { return false; } richk@6720: void VideoDriver_Dedicated::ToggleFullscreen(bool fs) {} truelight@543: truelight@6359: #if defined(UNIX) || defined(__OS2__) || defined(PSP) rubidium@6573: static bool InputWaiting() truelight@543: { truelight@543: struct timeval tv; truelight@543: fd_set readfds; truelight@543: truelight@543: tv.tv_sec = 0; truelight@543: tv.tv_usec = 1; truelight@543: truelight@543: FD_ZERO(&readfds); truelight@543: FD_SET(STDIN, &readfds); truelight@543: truelight@543: /* don't care about writefds and exceptfds: */ tron@2180: return select(STDIN + 1, &readfds, NULL, NULL, &tv) > 0; tron@2180: } truelight@543: rubidium@6573: static uint32 GetTime() tron@2180: { tron@2180: struct timeval tim; darkvater@1046: tron@2180: gettimeofday(&tim, NULL); tron@2180: return tim.tv_usec / 1000 + tim.tv_sec * 1000; truelight@543: } tron@2180: truelight@543: #else tron@2180: rubidium@6573: static bool InputWaiting() truelight@543: { Darkvater@4599: return WaitForSingleObject(_hInputReady, 1) == WAIT_OBJECT_0; tron@2180: } tron@1109: rubidium@6573: static uint32 GetTime() tron@2180: { tron@2180: return GetTickCount(); truelight@543: } tron@2180: truelight@543: #endif truelight@543: rubidium@6573: static void DedicatedHandleKeyInput() truelight@704: { truelight@721: static char input_line[200] = ""; truelight@704: Darkvater@4599: if (!InputWaiting()) return; darkvater@1046: Darkvater@4599: if (_exit_game) return; darkvater@1046: truelight@6359: #if defined(UNIX) || defined(__OS2__) || defined(PSP) peter1138@4891: if (fgets(input_line, lengthof(input_line), stdin) == NULL) return; darkvater@1046: #else Darkvater@4599: /* Handle console input, and singal console thread, it can accept input again */ Darkvater@4599: strncpy(input_line, _win_console_thread_buffer, lengthof(input_line)); Darkvater@4599: SetEvent(_hWaitForInputHandling); darkvater@1046: #endif truelight@736: darkvater@1046: /* XXX - strtok() does not 'forget' \n\r if it is the first character! */ darkvater@1046: strtok(input_line, "\r\n"); // Forget about the final \n (or \r) darkvater@1046: { /* Remove any special control characters */ darkvater@1046: uint i; darkvater@1046: for (i = 0; i < lengthof(input_line); i++) { darkvater@1046: if (input_line[i] == '\n' || input_line[i] == '\r') // cut missed beginning '\0' darkvater@1046: input_line[i] = '\0'; darkvater@1046: darkvater@1046: if (input_line[i] == '\0') darkvater@1046: break; darkvater@1046: rubidium@6871: if (!IsInsideMM(input_line[i], ' ', 256)) darkvater@1046: input_line[i] = ' '; truelight@704: } truelight@704: } darkvater@1046: darkvater@1046: IConsoleCmdExec(input_line); // execute command truelight@704: } truelight@704: richk@6720: void VideoDriver_Dedicated::MainLoop() truelight@704: { rubidium@5832: uint32 cur_ticks = GetTime(); rubidium@5832: uint32 next_tick = cur_ticks + 30; truelight@543: bjarni@770: /* Signal handlers */ truelight@6359: #if defined(UNIX) || defined(PSP) truelight@702: signal(SIGTERM, DedicatedSignalHandler); truelight@702: signal(SIGINT, DedicatedSignalHandler); truelight@736: signal(SIGQUIT, DedicatedSignalHandler); truelight@702: #endif truelight@702: truelight@543: // Load the dedicated server stuff truelight@543: _is_network_server = true; truelight@543: _network_dedicated = true; Darkvater@4848: _network_playas = PLAYER_SPECTATOR; Darkvater@4848: _local_player = PLAYER_SPECTATOR; truelight@1414: truelight@1414: /* If SwitchMode is SM_LOAD, it means that the user used the '-g' options */ truelight@1414: if (_switch_mode != SM_LOAD) { truelight@4300: StartNewGameWithoutGUI(GENERATE_NEW_SEED); truelight@4300: SwitchMode(_switch_mode); truelight@1414: _switch_mode = SM_NONE; truelight@1414: } else { truelight@1414: _switch_mode = SM_NONE; truelight@1414: /* First we need to test if the savegame can be loaded, else we will end up playing the truelight@1414: * intro game... */ richk@6720: if (!SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_NORMAL, BASE_DIR)) { truelight@1414: /* Loading failed, pop out.. */ Darkvater@5568: DEBUG(net, 0, "Loading requested map failed, aborting"); truelight@1414: _networking = false; truelight@1414: } else { truelight@1414: /* We can load this game, so go ahead */ truelight@1414: SwitchMode(SM_LOAD); truelight@1414: } truelight@1414: } truelight@1414: truelight@543: // Done loading, start game! truelight@543: truelight@543: if (!_networking) { Darkvater@5568: DEBUG(net, 0, "Dedicated server could not be started, aborting"); tron@2228: return; truelight@543: } truelight@543: tron@2228: while (!_exit_game) { rubidium@5832: uint32 prev_cur_ticks = cur_ticks; // to check for wrapping truelight@543: InteractiveRandom(); // randomness truelight@543: truelight@704: if (!_dedicated_forks) truelight@704: DedicatedHandleKeyInput(); truelight@543: tron@2180: cur_ticks = GetTime(); rubidium@5832: if (cur_ticks >= next_tick || cur_ticks < prev_cur_ticks) { rubidium@5832: next_tick = cur_ticks + 30; truelight@543: truelight@543: GameLoop(); richk@6719: _screen.dst_ptr = _dedicated_video_mem; truelight@543: UpdateWindows(); truelight@543: } truelight@543: CSleep(1); truelight@543: } truelight@543: } truelight@543: tron@2195: #endif /* ENABLE_NETWORK */