2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 /** @file dedicated_v.cpp Dedicated server video 'driver'. */
10 #include "../stdafx.h"
12 #include "../gfx_func.h"
13 #include "../error_func.h"
14 #include "../network/network.h"
15 #include "../network/network_internal.h"
16 #include "../console_func.h"
17 #include "../genworld.h"
18 #include "../fileio_type.h"
20 #include "../blitter/factory.hpp"
21 #include "../company_func.h"
22 #include "../core/random_func.hpp"
23 #include "../saveload/saveload.h"
24 #include "../thread.h"
25 #include "../window_func.h"
27 #include "dedicated_v.h"
30 # include <sys/time.h> /* gettimeofday */
31 # include <sys/types.h>
34 # define STDIN 0 /* file descriptor for standard input */
37 static void DedicatedSignalHandler(int sig
)
39 if (_game_mode
== GM_NORMAL
&& _settings_client
.gui
.autosave_on_exit
) DoExitSave();
41 signal(sig
, DedicatedSignalHandler
);
46 # include <windows.h> /* GetTickCount */
50 # include "../os/windows/win32.h"
52 static HANDLE _hInputReady
, _hWaitForInputHandling
;
53 static HANDLE _hThread
; // Thread to close
54 static std::string _win_console_thread_buffer
;
56 /* Windows Console thread. Just loop and signal when input has been received */
57 static void WINAPI
CheckForConsoleInput()
59 SetCurrentThreadName("ottd:win-console");
62 std::getline(std::cin
, _win_console_thread_buffer
);
64 /* Signal input waiting that input is read and wait for it being handled. */
65 SignalObjectAndWait(_hInputReady
, _hWaitForInputHandling
, INFINITE
, FALSE
);
69 static void CreateWindowsConsoleThread()
72 /* Create event to signal when console input is ready */
73 _hInputReady
= CreateEvent(nullptr, false, false, nullptr);
74 _hWaitForInputHandling
= CreateEvent(nullptr, false, false, nullptr);
75 if (_hInputReady
== nullptr || _hWaitForInputHandling
== nullptr) UserError("Cannot create console event!");
77 _hThread
= CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE
)CheckForConsoleInput
, nullptr, 0, &dwThreadId
);
78 if (_hThread
== nullptr) UserError("Cannot create console thread!");
80 Debug(driver
, 2, "Windows console thread started");
83 static void CloseWindowsConsoleThread()
85 CloseHandle(_hThread
);
86 CloseHandle(_hInputReady
);
87 CloseHandle(_hWaitForInputHandling
);
88 Debug(driver
, 2, "Windows console thread shut down");
93 #include "../safeguards.h"
96 static void *_dedicated_video_mem
;
98 /* Whether a fork has been done. */
99 bool _dedicated_forks
;
101 extern bool SafeLoad(const std::string
&filename
, SaveLoadOperation fop
, DetailedFileType dft
, GameMode newgm
, Subdirectory subdir
, struct LoadFilter
*lf
= nullptr);
103 static FVideoDriver_Dedicated iFVideoDriver_Dedicated
;
106 std::optional
<std::string_view
> VideoDriver_Dedicated::Start(const StringList
&)
108 this->UpdateAutoResolution();
110 int bpp
= BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
111 _dedicated_video_mem
= (bpp
== 0) ? nullptr : MallocT
<uint8_t>(static_cast<size_t>(_cur_resolution
.width
) * _cur_resolution
.height
* (bpp
/ 8));
113 _screen
.width
= _screen
.pitch
= _cur_resolution
.width
;
114 _screen
.height
= _cur_resolution
.height
;
115 _screen
.dst_ptr
= _dedicated_video_mem
;
117 BlitterFactory::GetCurrentBlitter()->PostResize();
120 /* For win32 we need to allocate a console (debug mode does the same) */
122 CreateWindowsConsoleThread();
123 SetConsoleTitle(L
"OpenTTD Dedicated Server");
127 /* Disable the MSVC assertion message box. */
128 _set_error_mode(_OUT_TO_STDERR
);
129 _CrtSetReportMode(_CRT_ASSERT
, _CRTDBG_MODE_FILE
);
130 _CrtSetReportFile(_CRT_ASSERT
, _CRTDBG_FILE_STDERR
);
133 Debug(driver
, 1, "Loading dedicated server");
137 void VideoDriver_Dedicated::Stop()
140 CloseWindowsConsoleThread();
142 free(_dedicated_video_mem
);
145 void VideoDriver_Dedicated::MakeDirty(int, int, int, int) {}
146 bool VideoDriver_Dedicated::ChangeResolution(int, int) { return false; }
147 bool VideoDriver_Dedicated::ToggleFullscreen(bool) { return false; }
150 static bool InputWaiting()
159 FD_SET(STDIN
, &readfds
);
161 /* don't care about writefds and exceptfds: */
162 return select(STDIN
+ 1, &readfds
, nullptr, nullptr, &tv
) > 0;
167 static bool InputWaiting()
169 return WaitForSingleObject(_hInputReady
, 1) == WAIT_OBJECT_0
;
174 static void DedicatedHandleKeyInput()
176 if (!InputWaiting()) return;
178 if (_exit_game
) return;
180 std::string input_line
;
182 if (!std::getline(std::cin
, input_line
)) return;
184 /* Handle console input, and signal console thread, it can accept input again */
185 std::swap(input_line
, _win_console_thread_buffer
);
186 SetEvent(_hWaitForInputHandling
);
189 /* Remove any trailing \r or \n, and ensure the string is valid. */
190 auto p
= input_line
.find_last_not_of("\r\n");
191 if (p
!= std::string::npos
) p
++;
192 IConsoleCmdExec(StrMakeValid(input_line
.substr(0, p
)));
195 void VideoDriver_Dedicated::MainLoop()
197 /* Signal handlers */
199 signal(SIGTERM
, DedicatedSignalHandler
);
200 signal(SIGINT
, DedicatedSignalHandler
);
201 signal(SIGQUIT
, DedicatedSignalHandler
);
204 /* Load the dedicated server stuff */
205 _is_network_server
= true;
206 _network_dedicated
= true;
207 _current_company
= _local_company
= COMPANY_SPECTATOR
;
209 /* If SwitchMode is SM_LOAD_GAME / SM_START_HEIGHTMAP, it means that the user used the '-g' options */
210 if (_switch_mode
!= SM_LOAD_GAME
&& _switch_mode
!= SM_START_HEIGHTMAP
) {
211 StartNewGameWithoutGUI(GENERATE_NEW_SEED
);
214 this->is_game_threaded
= false;
216 /* Done loading, start game! */
218 while (!_exit_game
) {
219 if (!_dedicated_forks
) DedicatedHandleKeyInput();
220 this->DrainCommandQueue();
223 this->SleepTillNextTick();