Files @ r25014:e1f1bf3a062e
Branch filter:

Location: cpp/openttd-patchpack/source/src/video/dedicated_v.cpp - annotation

Patric Stout
Add: [Video] move GameLoop into its own thread

This allows drawing to happen while the GameLoop is doing an
iteration too.

Sadly, not much drawing currently can be done while the GameLoop
is running, as for example PollEvent() or UpdateWindows() can
influence the game-state. As such, they first need to acquire a
lock on the game-state before they can be called.

Currently, the main advantage is the time spend in Paint(), which
for non-OpenGL drivers can be a few milliseconds. For OpenGL this
is more like 0.05 milliseconds; in these instances this change
doesn't add any benefits for now.

This is an alternative to the former "draw-thread", which moved
the drawing in a thread for some OSes. It has similar performance
gain as this does, although this implementation allows for more
finer control over what suffers when the GameLoop takes too
long: drawing or the next GameLoop. For now they both suffer
equally.
r12768:980ae0491352
r12768:980ae0491352
r12768:980ae0491352
r12768:980ae0491352
r12768:980ae0491352
r12768:980ae0491352
r12768:980ae0491352
r9111:983de9c5a848
r9111:983de9c5a848
r5584:545d748cc681
r5584:545d748cc681
r8123:dde0a9a84019
r14248:a9050881acd7
r8276:7682b1a3406d
r9336:b3c601814650
r5584:545d748cc681
r10039:727fb45b0424
r8151:3983fe61f8f0
r6937:bea726150bf3
r10208:ef8fcc3dc4ca
r8433:ed199dffe99f
r16080:3d335f6288e5
r23589:b3521e885d81
r24833:e40e0f8b2230
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r15613:193c12018337
r15613:193c12018337
r6247:96e840dbefcc
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r11363:6906c490a00e
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r22834:7614d5fc156d
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r16080:3d335f6288e5
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r23088:6710c4c79ac6
r7408:a2596047ca4e
r22835:b0dc5b810969
r7408:a2596047ca4e
r7408:a2596047ca4e
r22466:254ae184fe62
r23793:62db3b15fbb2
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r6247:96e840dbefcc
r5584:545d748cc681
r23587:821735a25dec
r22466:254ae184fe62
r6265:05ea4f089b8d
r6265:05ea4f089b8d
r17388:4c1f53028687
r23607:36c15679007d
r21397:10b7bc8e92d3
r21397:10b7bc8e92d3
r21397:10b7bc8e92d3
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r6247:96e840dbefcc
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r23607:36c15679007d
r23607:36c15679007d
r23607:36c15679007d
r5584:545d748cc681
r23607:36c15679007d
r23607:36c15679007d
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r6247:96e840dbefcc
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r21383:942c32fb8b0e
r21383:942c32fb8b0e
r5584:545d748cc681
r6878:61440e99046f
r5584:545d748cc681
r15514:7e3c6c9e07c2
r15514:7e3c6c9e07c2
r15514:7e3c6c9e07c2
r24525:3150e1a8a4b3
r5584:545d748cc681
r7170:38b143754b40
r5584:545d748cc681
r7170:38b143754b40
r24218:c32caa9f014d
r5584:545d748cc681
r24665:20a8caea4459
r24665:20a8caea4459
r21124:6022cb131374
r23607:36c15679007d
r6878:61440e99046f
r9533:0cf5e972f19e
r9533:0cf5e972f19e
r13993:05fba9b1b249
r9001:2baba84422d8
r21124:6022cb131374
r5584:545d748cc681
r23088:6710c4c79ac6
r11363:6906c490a00e
r5584:545d748cc681
r5584:545d748cc681
r24928:f1d8013662df
r5584:545d748cc681
r5584:545d748cc681
r18273:a81ce039c1e9
r18273:a81ce039c1e9
r18273:a81ce039c1e9
r18273:a81ce039c1e9
r18273:a81ce039c1e9
r5584:545d748cc681
r11363:6906c490a00e
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r23607:36c15679007d
r5584:545d748cc681
r5584:545d748cc681
r7170:38b143754b40
r5584:545d748cc681
r23088:6710c4c79ac6
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r7170:38b143754b40
r7170:38b143754b40
r8171:ff11fcdf6589
r5584:545d748cc681
r22834:7614d5fc156d
r6247:96e840dbefcc
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r23607:36c15679007d
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r6247:96e840dbefcc
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r6247:96e840dbefcc
r5584:545d748cc681
r9905:a3518d11830b
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r22834:7614d5fc156d
r23607:36c15679007d
r5584:545d748cc681
r19944:25a78576fb5e
r24521:57ec498b9221
r11639:baf78c8eb5f4
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r21397:10b7bc8e92d3
r9848:478ccf636d4c
r9848:478ccf636d4c
r9848:478ccf636d4c
r9848:478ccf636d4c
r5584:545d748cc681
r5584:545d748cc681
r11272:411ffa5b815a
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r7170:38b143754b40
r5584:545d748cc681
r5584:545d748cc681
r22834:7614d5fc156d
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r11363:6906c490a00e
r5584:545d748cc681
r5584:545d748cc681
r15284:a975e22a01ee
r5584:545d748cc681
r17722:3fa2edce79df
r17722:3fa2edce79df
r5584:545d748cc681
r11223:d5864a79dfb6
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r22443:89afba6ce61a
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r17722:3fa2edce79df
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r25014:e1f1bf3a062e
r25014:e1f1bf3a062e
r11363:6906c490a00e
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r5584:545d748cc681
r15514:7e3c6c9e07c2
r5584:545d748cc681
r24970:1ff44d19fa4a
r24855:196c88971d0d
r24855:196c88971d0d
r5584:545d748cc681
r5584:545d748cc681
/*
 * This file is part of OpenTTD.
 * 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.
 * 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.
 * 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/>.
 */

/** @file dedicated_v.cpp Dedicated server video 'driver'. */

#include "../stdafx.h"

#include "../gfx_func.h"
#include "../network/network.h"
#include "../network/network_internal.h"
#include "../console_func.h"
#include "../genworld.h"
#include "../fileio_type.h"
#include "../fios.h"
#include "../blitter/factory.hpp"
#include "../company_func.h"
#include "../core/random_func.hpp"
#include "../saveload/saveload.h"
#include "../thread.h"
#include "../window_func.h"
#include "dedicated_v.h"

#ifdef __OS2__
#	include <sys/time.h> /* gettimeofday */
#	include <sys/types.h>
#	include <unistd.h>
#	include <conio.h>

#	define INCL_DOS
#	include <os2.h>

#	define STDIN 0  /* file descriptor for standard input */

/**
 * Switches OpenTTD to a console app at run-time, instead of a PM app
 * Necessary to see stdout, etc.
 */
static void OS2_SwitchToConsoleMode()
{
	PPIB pib;
	PTIB tib;

	DosGetInfoBlocks(&tib, &pib);

	/* Change flag from PM to VIO */
	pib->pib_ultype = 3;
}
#endif

#if defined(UNIX)
#	include <sys/time.h> /* gettimeofday */
#	include <sys/types.h>
#	include <unistd.h>
#	include <signal.h>
#	define STDIN 0  /* file descriptor for standard input */

/* Signal handlers */
static void DedicatedSignalHandler(int sig)
{
	if (_game_mode == GM_NORMAL && _settings_client.gui.autosave_on_exit) DoExitSave();
	_exit_game = true;
	signal(sig, DedicatedSignalHandler);
}
#endif

#if defined(_WIN32)
# include <windows.h> /* GetTickCount */
# include <conio.h>
# include <time.h>
# include <tchar.h>
# include "../os/windows/win32.h"

static HANDLE _hInputReady, _hWaitForInputHandling;
static HANDLE _hThread; // Thread to close
static char _win_console_thread_buffer[200];

/* Windows Console thread. Just loop and signal when input has been received */
static void WINAPI CheckForConsoleInput()
{
	SetCurrentThreadName("ottd:win-console");

	DWORD nb;
	HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
	for (;;) {
		ReadFile(hStdin, _win_console_thread_buffer, lengthof(_win_console_thread_buffer), &nb, nullptr);
		if (nb >= lengthof(_win_console_thread_buffer)) nb = lengthof(_win_console_thread_buffer) - 1;
		_win_console_thread_buffer[nb] = '\0';

		/* Signal input waiting that input is read and wait for it being handled
		 * SignalObjectAndWait() should be used here, but it's unsupported in Win98< */
		SetEvent(_hInputReady);
		WaitForSingleObject(_hWaitForInputHandling, INFINITE);
	}
}

static void CreateWindowsConsoleThread()
{
	DWORD dwThreadId;
	/* Create event to signal when console input is ready */
	_hInputReady = CreateEvent(nullptr, false, false, nullptr);
	_hWaitForInputHandling = CreateEvent(nullptr, false, false, nullptr);
	if (_hInputReady == nullptr || _hWaitForInputHandling == nullptr) usererror("Cannot create console event!");

	_hThread = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)CheckForConsoleInput, nullptr, 0, &dwThreadId);
	if (_hThread == nullptr) usererror("Cannot create console thread!");

	DEBUG(driver, 2, "Windows console thread started");
}

static void CloseWindowsConsoleThread()
{
	CloseHandle(_hThread);
	CloseHandle(_hInputReady);
	CloseHandle(_hWaitForInputHandling);
	DEBUG(driver, 2, "Windows console thread shut down");
}

#endif

#include "../safeguards.h"


static void *_dedicated_video_mem;

/* Whether a fork has been done. */
bool _dedicated_forks;

extern bool SafeLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileType dft, GameMode newgm, Subdirectory subdir, struct LoadFilter *lf = nullptr);

static FVideoDriver_Dedicated iFVideoDriver_Dedicated;


const char *VideoDriver_Dedicated::Start(const StringList &parm)
{
	this->UpdateAutoResolution();

	int bpp = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
	_dedicated_video_mem = (bpp == 0) ? nullptr : MallocT<byte>(_cur_resolution.width * _cur_resolution.height * (bpp / 8));

	_screen.width  = _screen.pitch = _cur_resolution.width;
	_screen.height = _cur_resolution.height;
	_screen.dst_ptr = _dedicated_video_mem;
	ScreenSizeChanged();
	BlitterFactory::GetCurrentBlitter()->PostResize();

#if defined(_WIN32)
	/* For win32 we need to allocate a console (debug mode does the same) */
	CreateConsole();
	CreateWindowsConsoleThread();
	SetConsoleTitle(L"OpenTTD Dedicated Server");
#endif

#ifdef _MSC_VER
	/* Disable the MSVC assertion message box. */
	_set_error_mode(_OUT_TO_STDERR);
#endif

#ifdef __OS2__
	/* For OS/2 we also need to switch to console mode instead of PM mode */
	OS2_SwitchToConsoleMode();
#endif

	DEBUG(driver, 1, "Loading dedicated server");
	return nullptr;
}

void VideoDriver_Dedicated::Stop()
{
#ifdef _WIN32
	CloseWindowsConsoleThread();
#endif
	free(_dedicated_video_mem);
}

void VideoDriver_Dedicated::MakeDirty(int left, int top, int width, int height) {}
bool VideoDriver_Dedicated::ChangeResolution(int w, int h) { return false; }
bool VideoDriver_Dedicated::ToggleFullscreen(bool fs) { return false; }

#if defined(UNIX) || defined(__OS2__)
static bool InputWaiting()
{
	struct timeval tv;
	fd_set readfds;

	tv.tv_sec = 0;
	tv.tv_usec = 1;

	FD_ZERO(&readfds);
	FD_SET(STDIN, &readfds);

	/* don't care about writefds and exceptfds: */
	return select(STDIN + 1, &readfds, nullptr, nullptr, &tv) > 0;
}

#else

static bool InputWaiting()
{
	return WaitForSingleObject(_hInputReady, 1) == WAIT_OBJECT_0;
}

#endif

static void DedicatedHandleKeyInput()
{
	static char input_line[1024] = "";

	if (!InputWaiting()) return;

	if (_exit_game) return;

#if defined(UNIX) || defined(__OS2__)
	if (fgets(input_line, lengthof(input_line), stdin) == nullptr) return;
#else
	/* Handle console input, and signal console thread, it can accept input again */
	static_assert(lengthof(_win_console_thread_buffer) <= lengthof(input_line));
	strecpy(input_line, _win_console_thread_buffer, lastof(input_line));
	SetEvent(_hWaitForInputHandling);
#endif

	/* Remove trailing \r or \n */
	for (char *c = input_line; *c != '\0'; c++) {
		if (*c == '\n' || *c == '\r' || c == lastof(input_line)) {
			*c = '\0';
			break;
		}
	}
	str_validate(input_line, lastof(input_line));

	IConsoleCmdExec(input_line); // execute command
}

void VideoDriver_Dedicated::MainLoop()
{
	/* Signal handlers */
#if defined(UNIX)
	signal(SIGTERM, DedicatedSignalHandler);
	signal(SIGINT, DedicatedSignalHandler);
	signal(SIGQUIT, DedicatedSignalHandler);
#endif

	/* Load the dedicated server stuff */
	_is_network_server = true;
	_network_dedicated = true;
	_current_company = _local_company = COMPANY_SPECTATOR;

	/* If SwitchMode is SM_LOAD_GAME, it means that the user used the '-g' options */
	if (_switch_mode != SM_LOAD_GAME) {
		StartNewGameWithoutGUI(GENERATE_NEW_SEED);
		SwitchToMode(_switch_mode);
		_switch_mode = SM_NONE;
	} else {
		_switch_mode = SM_NONE;
		/* First we need to test if the savegame can be loaded, else we will end up playing the
		 *  intro game... */
		if (!SafeLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.detail_ftype, GM_NORMAL, BASE_DIR)) {
			/* Loading failed, pop out.. */
			DEBUG(net, 0, "Loading requested map failed, aborting");
			_networking = false;
		} else {
			/* We can load this game, so go ahead */
			SwitchToMode(SM_LOAD_GAME);
		}
	}

	this->is_game_threaded = false;

	/* Done loading, start game! */

	if (!_networking) {
		DEBUG(net, 0, "Dedicated server could not be started, aborting");
		return;
	}

	while (!_exit_game) {
		if (!_dedicated_forks) DedicatedHandleKeyInput();

		ChangeGameSpeed(_ddc_fastforward);
		this->Tick();
		this->SleepTillNextTick();
	}
}