Merge branch 'jgrpp' into jgrpp-nrt
# Conflicts: # .ottdrev-vc # README.md # jgrpp-changelog.md # src/ship_cmd.cpp
This commit is contained in:
@@ -246,6 +246,8 @@ See [jgrpp-changelog.md](jgrpp-changelog.md) for changelog.
|
||||
* Add news/advice setting to warn if no depot order in vehicle schedule. (added in v0.31.1).
|
||||
* Enable vehicle list buttons in station window when the list would be non-empty. (added in v0.31.1).
|
||||
* Enable vehicle group management actions on other companies' stations. (added in v0.31.1).
|
||||
* Add a password mechanism to change network game settings from a network client. (added in v0.31.4).
|
||||
* Change network protocol to send server/join and rcon passwords in hashed form instead of in clear text. (added in v0.31.4).
|
||||
* Various minor fixes, see changelog.
|
||||
* [NewGRF specification additions](docs/newgrf-additions.html) ([online copy](https://htmlpreview.github.io/?https://github.com/JGRennison/OpenTTD-patches/blob/jgrpp/docs/newgrf-additions.html)).
|
||||
* [Low-level code/performance changes](docs/jgrpp-low-level-changes.md).
|
||||
|
@@ -6,6 +6,15 @@
|
||||
* Include NotRoadTypes (NRT).
|
||||
* Bump trunk base from commit 21edf67f89c60351d5a0d84625455aa296b6b950 to commit a52bbb72a8a2cbcbefb0ff91b559f33c34094239.
|
||||
|
||||
### v0.31.4 (2019-08-24)
|
||||
* Fix crash when removing signals from tunnel/bridge with trainless reservation.
|
||||
* Fix various cases where reversing a train inside a signalled tunnel/bridge handled PBS reservations incorrectly.
|
||||
* Fix error windows being closed when returning to the main menu.
|
||||
* Add a password mechanism to change network game settings from a network client.
|
||||
* Change station tile coverage highlight colour to light blue.
|
||||
* Change network protocol to send server/join and rcon passwords in hashed form instead of in clear text.
|
||||
* Fix various possible sources of non-determinism which could potentially cause multiplayer desyncs.
|
||||
|
||||
### v0.31.3 (2019-07-13)
|
||||
* Fix the target order number of conditional order jumps being loaded incorrectly from SpringPP savegames.
|
||||
* Fix order backups not being restored when using buy and refit.
|
||||
|
@@ -460,6 +460,7 @@ core/alloc_type.hpp
|
||||
core/backup_type.hpp
|
||||
core/bitmath_func.cpp
|
||||
core/bitmath_func.hpp
|
||||
core/checksum_func.hpp
|
||||
core/container_func.hpp
|
||||
core/dyn_arena_alloc.hpp
|
||||
core/endian_func.hpp
|
||||
|
@@ -40,6 +40,7 @@
|
||||
#include "disaster_vehicle.h"
|
||||
#include "newgrf_airporttiles.h"
|
||||
#include "framerate_type.h"
|
||||
#include "core/checksum_func.hpp"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
@@ -2127,6 +2128,7 @@ static bool AircraftEventHandler(Aircraft *v, int loop)
|
||||
|
||||
bool Aircraft::Tick()
|
||||
{
|
||||
UpdateStateChecksum((((uint64) this->x_pos) << 32) | this->y_pos);
|
||||
if (!this->IsNormalAircraft()) return true;
|
||||
|
||||
this->tick_counter++;
|
||||
|
@@ -1082,9 +1082,6 @@ struct BuildVehicleWindow : Window {
|
||||
this->GetWidget<NWidgetStacked>(WID_BV_BUILD_SEL)->SetDisplayedPlane(SZSP_NONE);
|
||||
}
|
||||
|
||||
/* disable renaming engines in network games if you are not the server */
|
||||
this->SetWidgetDisabledState(WID_BV_RENAME, _networking && !_network_server);
|
||||
|
||||
NWidgetCore *widget = this->GetWidget<NWidgetCore>(WID_BV_LIST);
|
||||
widget->tool_tip = STR_BUY_VEHICLE_TRAIN_LIST_TOOLTIP + type;
|
||||
|
||||
@@ -1589,6 +1586,9 @@ struct BuildVehicleWindow : Window {
|
||||
|
||||
this->SetWidgetsDisabledState(this->sel_engine == INVALID_ENGINE, WID_BV_SHOW_HIDE, WID_BV_BUILD, WID_BV_RENAME, WIDGET_LIST_END);
|
||||
|
||||
/* disable renaming engines in network games if you are not the server */
|
||||
this->SetWidgetDisabledState(WID_BV_RENAME, _networking && !(_network_server || _network_settings_access));
|
||||
|
||||
this->DrawWidgets();
|
||||
|
||||
if (!this->IsShaded()) {
|
||||
|
@@ -556,7 +556,7 @@ char *DumpCommandLog(char *buffer, const char *last)
|
||||
buffer += seprintf(buffer, last, "%c%c%c%c%c%c%c%c | ",
|
||||
fc(CLEF_SCRIPT, 'a'), fc(CLEF_BINARY, 'b'), fc(CLEF_MY_CMD, 'm'), fc(CLEF_ONLY_SENDING, 's'),
|
||||
fc(CLEF_ESTIMATE_ONLY, 'e'), fc(CLEF_TEXT, 't'), fc(CLEF_GENERATING_WORLD, 'g'), fc(CLEF_CMD_FAILED, 'f'));
|
||||
buffer += seprintf(buffer, last, " %7d x %7d, p1: 0x%08X, p2: 0x%08X, cc: %2u, lc: %2u, cmd: 0x%08X (%s)\n",
|
||||
buffer += seprintf(buffer, last, " %7d x %7d, p1: 0x%08X, p2: 0x%08X, cc: %3u, lc: %3u, cmd: 0x%08X (%s)\n",
|
||||
TileX(entry.tile), TileY(entry.tile), entry.p1, entry.p2, (uint) entry.current_company, (uint) entry.local_company, entry.cmd, GetCommandName(entry.cmd));
|
||||
}
|
||||
return buffer;
|
||||
|
@@ -703,6 +703,21 @@ DEF_CONSOLE_CMD(ConRcon)
|
||||
return true;
|
||||
}
|
||||
|
||||
DEF_CONSOLE_CMD(ConSettingsAccess)
|
||||
{
|
||||
if (argc == 0) {
|
||||
IConsoleHelp("Enable changing game settings from this client. Usage: 'settings_access <password>'");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argc < 2) return false;
|
||||
|
||||
if (!_network_server) {
|
||||
NetworkClientSendSettingsPassword(argv[1]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
DEF_CONSOLE_CMD(ConStatus)
|
||||
{
|
||||
if (argc == 0) {
|
||||
@@ -2356,6 +2371,7 @@ void IConsoleStdLibRegister()
|
||||
IConsoleAliasRegister("info", "server_info");
|
||||
IConsoleCmdRegister("reconnect", ConNetworkReconnect, ConHookClientOnly);
|
||||
IConsoleCmdRegister("rcon", ConRcon, ConHookNeedNetwork);
|
||||
IConsoleCmdRegister("settings_access", ConSettingsAccess, ConHookNeedNetwork);
|
||||
|
||||
IConsoleCmdRegister("join", ConJoinCompany, ConHookNeedNetwork);
|
||||
IConsoleAliasRegister("spectate", "join 255");
|
||||
@@ -2380,6 +2396,8 @@ void IConsoleStdLibRegister()
|
||||
IConsoleAliasRegister("server_password", "setting server_password %+");
|
||||
IConsoleAliasRegister("rcon_pw", "setting rcon_password %+");
|
||||
IConsoleAliasRegister("rcon_password", "setting rcon_password %+");
|
||||
IConsoleAliasRegister("settings_pw", "setting settings_password %+");
|
||||
IConsoleAliasRegister("settings_password", "setting settings_password %+");
|
||||
IConsoleAliasRegister("name", "setting client_name %+");
|
||||
IConsoleAliasRegister("server_name", "setting server_name %+");
|
||||
IConsoleAliasRegister("server_port", "setting server_port %+");
|
||||
|
@@ -55,6 +55,13 @@ uint8 FindFirstBit(uint32 x)
|
||||
return pos;
|
||||
}
|
||||
|
||||
uint8 FindFirstBit64(uint64 x)
|
||||
{
|
||||
if (x == 0) return 0;
|
||||
if ((x & 0x00000000ffffffffULL) != 0) return FindFirstBit(x);
|
||||
return FindFirstBit(x >> 32) + 32;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
@@ -198,6 +198,13 @@ inline uint8 FindFirstBit(uint32 x)
|
||||
return __builtin_ctz(x);
|
||||
}
|
||||
|
||||
inline uint8 FindFirstBit64(uint64 x)
|
||||
{
|
||||
if (x == 0) return 0;
|
||||
|
||||
return __builtin_ctzll(x);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/** Lookup table to check which bit is set in a 6 bit variable */
|
||||
@@ -216,6 +223,7 @@ extern const uint8 _ffb_64[64];
|
||||
#define FIND_FIRST_BIT(x) _ffb_64[(x)]
|
||||
|
||||
uint8 FindFirstBit(uint32 x);
|
||||
uint8 FindFirstBit64(uint64 x);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -395,14 +403,14 @@ static inline T ROR(const T x, const uint8 n)
|
||||
* (since it will use hardware swapping if available).
|
||||
* Even though they should return uint16 and uint32, we get
|
||||
* warnings if we don't cast those (why?) */
|
||||
#define BSWAP64(x) ((uint64)CFSwapInt64(x))
|
||||
#define BSWAP32(x) ((uint32)CFSwapInt32(x))
|
||||
#define BSWAP16(x) ((uint16)CFSwapInt16(x))
|
||||
#define BSWAP64(x) ((uint64)CFSwapInt64((uint64)x))
|
||||
#define BSWAP32(x) ((uint32)CFSwapInt32((uint32)x))
|
||||
#define BSWAP16(x) ((uint16)CFSwapInt16((uint16)x))
|
||||
#elif defined(_MSC_VER)
|
||||
/* MSVC has intrinsics for swapping, resulting in faster code */
|
||||
#define BSWAP64(x) (_byteswap_uint64(x))
|
||||
#define BSWAP32(x) (_byteswap_ulong(x))
|
||||
#define BSWAP16(x) (_byteswap_ushort(x))
|
||||
#define BSWAP64(x) ((uint64)_byteswap_uint64((uint64)x))
|
||||
#define BSWAP32(x) ((uint32)_byteswap_ulong((uint32)x))
|
||||
#define BSWAP16(x) ((uint16)_byteswap_ushort((uint16)x))
|
||||
#else
|
||||
/**
|
||||
* Perform a 64 bits endianness bitswap on x.
|
||||
@@ -430,7 +438,7 @@ static inline T ROR(const T x, const uint8 n)
|
||||
{
|
||||
#if !defined(__ICC) && (defined(__GNUC__) || defined(__clang__))
|
||||
/* GCC >= 4.3 provides a builtin, resulting in faster code */
|
||||
return (uint32)__builtin_bswap32((int32)x);
|
||||
return (uint32)__builtin_bswap32((uint32)x);
|
||||
#else
|
||||
return ((x >> 24) & 0xFF) | ((x >> 8) & 0xFF00) | ((x << 8) & 0xFF0000) | ((x << 24) & 0xFF000000);
|
||||
#endif /* __GNUC__ || __clang__ */
|
||||
@@ -443,7 +451,12 @@ static inline T ROR(const T x, const uint8 n)
|
||||
*/
|
||||
static inline uint16 BSWAP16(uint16 x)
|
||||
{
|
||||
#if !defined(__ICC) && (defined(__GNUC__) || defined(__clang__))
|
||||
/* GCC >= 4.3 provides a builtin, resulting in faster code */
|
||||
return (uint16)__builtin_bswap16((uint16)x);
|
||||
#else
|
||||
return (x >> 8) | (x << 8);
|
||||
#endif /* __GNUC__ || __clang__ */
|
||||
}
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
|
33
src/core/checksum_func.hpp
Normal file
33
src/core/checksum_func.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* 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 checksum_func.hpp Checksum utility functions. */
|
||||
|
||||
#ifndef CHECKSUM_FUNC_HPP
|
||||
#define CHECKSUM_FUNC_HPP
|
||||
|
||||
#include "bitmath_func.hpp"
|
||||
|
||||
struct SimpleChecksum64 {
|
||||
uint64 state = 0;
|
||||
|
||||
void Update(uint64 input)
|
||||
{
|
||||
this->state = ROL(this->state, 1) ^ input ^ 0x123456789ABCDEF7ULL;
|
||||
}
|
||||
};
|
||||
|
||||
extern SimpleChecksum64 _state_checksum;
|
||||
|
||||
inline void UpdateStateChecksum(uint64 input)
|
||||
{
|
||||
_state_checksum.Update(input);
|
||||
}
|
||||
|
||||
#endif /* CHECKSUM_FUNC_HPP */
|
@@ -12,6 +12,8 @@
|
||||
#ifndef RANDOM_FUNC_HPP
|
||||
#define RANDOM_FUNC_HPP
|
||||
|
||||
#include "bitmath_func.hpp"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
/* Apple already has Random declared */
|
||||
#define Random OTTD_Random
|
||||
|
@@ -179,6 +179,13 @@ char *CrashLog::LogOpenTTDVersion(char *buffer, const char *last) const
|
||||
*/
|
||||
char *CrashLog::LogConfiguration(char *buffer, const char *last) const
|
||||
{
|
||||
auto pathfinder_name = [](uint8 pf) -> const char * {
|
||||
switch (pf) {
|
||||
case VPF_NPF: return "NPF";
|
||||
case VPF_YAPF: return "YAPF";
|
||||
default: return "-";
|
||||
};
|
||||
};
|
||||
buffer += seprintf(buffer, last,
|
||||
"Configuration:\n"
|
||||
" Blitter: %s\n"
|
||||
@@ -189,7 +196,8 @@ char *CrashLog::LogConfiguration(char *buffer, const char *last) const
|
||||
" Network: %s\n"
|
||||
" Sound driver: %s\n"
|
||||
" Sound set: %s (%u)\n"
|
||||
" Video driver: %s\n\n",
|
||||
" Video driver: %s\n"
|
||||
" Pathfinder: %s %s %s\n\n",
|
||||
BlitterFactory::GetCurrentBlitter() == nullptr ? "none" : BlitterFactory::GetCurrentBlitter()->GetName(),
|
||||
BaseGraphics::GetUsedSet() == nullptr ? "none" : BaseGraphics::GetUsedSet()->name,
|
||||
BaseGraphics::GetUsedSet() == nullptr ? UINT32_MAX : BaseGraphics::GetUsedSet()->version,
|
||||
@@ -201,7 +209,8 @@ char *CrashLog::LogConfiguration(char *buffer, const char *last) const
|
||||
SoundDriver::GetInstance() == nullptr ? "none" : SoundDriver::GetInstance()->GetName(),
|
||||
BaseSounds::GetUsedSet() == nullptr ? "none" : BaseSounds::GetUsedSet()->name,
|
||||
BaseSounds::GetUsedSet() == nullptr ? UINT32_MAX : BaseSounds::GetUsedSet()->version,
|
||||
VideoDriver::GetInstance() == nullptr ? "none" : VideoDriver::GetInstance()->GetName()
|
||||
VideoDriver::GetInstance() == nullptr ? "none" : VideoDriver::GetInstance()->GetName(),
|
||||
pathfinder_name(_settings_game.pf.pathfinder_for_trains), pathfinder_name(_settings_game.pf.pathfinder_for_roadvehs), pathfinder_name(_settings_game.pf.pathfinder_for_ships)
|
||||
);
|
||||
|
||||
buffer += seprintf(buffer, last,
|
||||
@@ -396,7 +405,7 @@ char *CrashLog::FillCrashLog(char *buffer, const char *last) const
|
||||
|
||||
YearMonthDay ymd;
|
||||
ConvertDateToYMD(_date, &ymd);
|
||||
buffer += seprintf(buffer, last, "In game date: %i-%02i-%02i (%i, %i)\n", _cur_date_ymd.year, _cur_date_ymd.month + 1, _cur_date_ymd.day, _date_fract, _tick_skip_counter);
|
||||
buffer += seprintf(buffer, last, "In game date: %i-%02i-%02i (%i, %i) (DL: %u)\n", _cur_date_ymd.year, _cur_date_ymd.month + 1, _cur_date_ymd.day, _date_fract, _tick_skip_counter, _settings_game.economy.day_length_factor);
|
||||
if (_game_load_time != 0) {
|
||||
buffer += seprintf(buffer, last, "Game loaded at: %i-%02i-%02i (%i, %i), %s",
|
||||
_game_load_cur_date_ymd.year, _game_load_cur_date_ymd.month + 1, _game_load_cur_date_ymd.day, _game_load_date_fract, _game_load_tick_skip_counter, asctime(gmtime(&_game_load_time)));
|
||||
@@ -440,16 +449,26 @@ char *CrashLog::FillCrashLog(char *buffer, const char *last) const
|
||||
* @param last The last position in the buffer to write to.
|
||||
* @return the position of the \c '\0' character after the buffer.
|
||||
*/
|
||||
char *CrashLog::FillDesyncCrashLog(char *buffer, const char *last) const
|
||||
char *CrashLog::FillDesyncCrashLog(char *buffer, const char *last, const DesyncExtraInfo &info) const
|
||||
{
|
||||
time_t cur_time = time(nullptr);
|
||||
buffer += seprintf(buffer, last, "*** OpenTTD Multiplayer %s Desync Report ***\n\n", _network_server ? "Server" : "Client");
|
||||
|
||||
buffer += seprintf(buffer, last, "Desync at: %s", asctime(gmtime(&cur_time)));
|
||||
if (!_network_server && info.flags) {
|
||||
auto flag_check = [&](DesyncExtraInfo::Flags flag, const char *str) {
|
||||
return info.flags & flag ? str : "";
|
||||
};
|
||||
buffer += seprintf(buffer, last, "Flags: %s%s%s%s\n",
|
||||
flag_check(DesyncExtraInfo::DEIF_RAND1, "R"),
|
||||
flag_check(DesyncExtraInfo::DEIF_RAND2, "Z"),
|
||||
flag_check(DesyncExtraInfo::DEIF_STATE, "S"),
|
||||
flag_check(DesyncExtraInfo::DEIF_DBL_RAND, "D"));
|
||||
}
|
||||
|
||||
YearMonthDay ymd;
|
||||
ConvertDateToYMD(_date, &ymd);
|
||||
buffer += seprintf(buffer, last, "In game date: %i-%02i-%02i (%i, %i)\n", _cur_date_ymd.year, _cur_date_ymd.month + 1, _cur_date_ymd.day, _date_fract, _tick_skip_counter);
|
||||
buffer += seprintf(buffer, last, "In game date: %i-%02i-%02i (%i, %i) (DL: %u)\n", _cur_date_ymd.year, _cur_date_ymd.month + 1, _cur_date_ymd.day, _date_fract, _tick_skip_counter, _settings_game.economy.day_length_factor);
|
||||
if (_game_load_time != 0) {
|
||||
buffer += seprintf(buffer, last, "Game loaded at: %i-%02i-%02i (%i, %i), %s",
|
||||
_game_load_cur_date_ymd.year, _game_load_cur_date_ymd.month + 1, _game_load_cur_date_ymd.day, _game_load_date_fract, _game_load_tick_skip_counter, asctime(gmtime(&_game_load_time)));
|
||||
@@ -490,7 +509,7 @@ char *CrashLog::FillDesyncCrashLog(char *buffer, const char *last) const
|
||||
* @param filename_last The last position in the filename buffer.
|
||||
* @return true when the crash log was successfully written.
|
||||
*/
|
||||
bool CrashLog::WriteCrashLog(const char *buffer, char *filename, const char *filename_last, const char *name) const
|
||||
bool CrashLog::WriteCrashLog(const char *buffer, char *filename, const char *filename_last, const char *name, FILE **crashlog_file) const
|
||||
{
|
||||
seprintf(filename, filename_last, "%s%s.log", _personal_dir, name);
|
||||
|
||||
@@ -500,7 +519,11 @@ bool CrashLog::WriteCrashLog(const char *buffer, char *filename, const char *fil
|
||||
size_t len = strlen(buffer);
|
||||
size_t written = fwrite(buffer, 1, len, file);
|
||||
|
||||
FioFCloseFile(file);
|
||||
if (crashlog_file) {
|
||||
*crashlog_file = file;
|
||||
} else {
|
||||
FioFCloseFile(file);
|
||||
}
|
||||
return len == written;
|
||||
}
|
||||
|
||||
@@ -618,7 +641,7 @@ bool CrashLog::MakeCrashLog() const
|
||||
* information like paths to the console.
|
||||
* @return true when everything is made successfully.
|
||||
*/
|
||||
bool CrashLog::MakeDesyncCrashLog(const std::string *log_in, std::string *log_out) const
|
||||
bool CrashLog::MakeDesyncCrashLog(const std::string *log_in, std::string *log_out, const DesyncExtraInfo &info) const
|
||||
{
|
||||
char filename[MAX_PATH];
|
||||
char buffer[65536 * 2];
|
||||
@@ -632,16 +655,16 @@ bool CrashLog::MakeDesyncCrashLog(const std::string *log_in, std::string *log_ou
|
||||
strftime(name_buffer_date, lastof(name_buffer) - name_buffer_date, "%Y%m%dT%H%M%SZ", gmtime(&cur_time));
|
||||
|
||||
printf("Desync encountered (%s), generating desync log...\n", mode);
|
||||
char *b = this->FillDesyncCrashLog(buffer, lastof(buffer));
|
||||
char *b = this->FillDesyncCrashLog(buffer, lastof(buffer), info);
|
||||
|
||||
if (log_out) log_out->assign(buffer);
|
||||
|
||||
if (log_in && !log_in->empty()) {
|
||||
b = strecpy(b, "\n", lastof(buffer), true);
|
||||
b = strecpy(b, log_in->c_str(), lastof(buffer), true);
|
||||
}
|
||||
|
||||
if (log_out) log_out->assign(buffer);
|
||||
|
||||
bool bret = this->WriteCrashLog(buffer, filename, lastof(filename), name_buffer);
|
||||
bool bret = this->WriteCrashLog(buffer, filename, lastof(filename), name_buffer, info.log_file);
|
||||
if (bret) {
|
||||
printf("Desync log written to %s. Please add this file to any bug reports.\n\n", filename);
|
||||
} else {
|
||||
|
@@ -12,8 +12,23 @@
|
||||
#ifndef CRASHLOG_H
|
||||
#define CRASHLOG_H
|
||||
|
||||
#include "core/enum_type.hpp"
|
||||
#include <string>
|
||||
|
||||
struct DesyncExtraInfo {
|
||||
enum Flags {
|
||||
DEIF_NONE = 0, ///< no flags
|
||||
DEIF_RAND1 = 1 << 0, ///< random 1 mismatch
|
||||
DEIF_RAND2 = 1 << 1, ///< random 2 mismatch
|
||||
DEIF_STATE = 1 << 2, ///< state mismatch
|
||||
DEIF_DBL_RAND = 1 << 3, ///< double-seed sent
|
||||
};
|
||||
|
||||
Flags flags = DEIF_NONE;
|
||||
FILE **log_file = nullptr; ///< save unclosed log file handle here
|
||||
};
|
||||
DECLARE_ENUM_AS_BIT_SET(DesyncExtraInfo::Flags)
|
||||
|
||||
/**
|
||||
* Helper class for creating crash logs.
|
||||
*/
|
||||
@@ -113,8 +128,8 @@ public:
|
||||
virtual ~CrashLog() {}
|
||||
|
||||
char *FillCrashLog(char *buffer, const char *last) const;
|
||||
char *FillDesyncCrashLog(char *buffer, const char *last) const;
|
||||
bool WriteCrashLog(const char *buffer, char *filename, const char *filename_last, const char *name = "crash") const;
|
||||
char *FillDesyncCrashLog(char *buffer, const char *last, const DesyncExtraInfo &info) const;
|
||||
bool WriteCrashLog(const char *buffer, char *filename, const char *filename_last, const char *name = "crash", FILE **crashlog_file = nullptr) const;
|
||||
|
||||
/**
|
||||
* Write the (crash) dump to a file.
|
||||
@@ -130,7 +145,7 @@ public:
|
||||
bool WriteScreenshot(char *filename, const char *filename_last, const char *name = "crash") const;
|
||||
|
||||
bool MakeCrashLog() const;
|
||||
bool MakeDesyncCrashLog(const std::string *log_in, std::string *log_out) const;
|
||||
bool MakeDesyncCrashLog(const std::string *log_in, std::string *log_out, const DesyncExtraInfo &info) const;
|
||||
bool MakeCrashSavegameAndScreenshot() const;
|
||||
|
||||
/**
|
||||
@@ -140,7 +155,7 @@ public:
|
||||
*/
|
||||
static void InitialiseCrashLog();
|
||||
|
||||
static void DesyncCrashLog(const std::string *log_in, std::string *log_out);
|
||||
static void DesyncCrashLog(const std::string *log_in, std::string *log_out, const DesyncExtraInfo &info);
|
||||
|
||||
static void SetErrorMessage(const char *message);
|
||||
static void AfterCrashLogCleanup();
|
||||
|
@@ -47,6 +47,7 @@
|
||||
#include "company_base.h"
|
||||
#include "core/random_func.hpp"
|
||||
#include "core/backup_type.hpp"
|
||||
#include "core/checksum_func.hpp"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
@@ -707,6 +708,7 @@ static DisasterVehicleTickProc * const _disastervehicle_tick_procs[] = {
|
||||
|
||||
bool DisasterVehicle::Tick()
|
||||
{
|
||||
UpdateStateChecksum((((uint64) this->x_pos) << 32) | this->y_pos);
|
||||
return _disastervehicle_tick_procs[this->subtype](this);
|
||||
}
|
||||
|
||||
|
@@ -18,6 +18,7 @@
|
||||
#include "animated_tile_func.h"
|
||||
#include "effectvehicle_func.h"
|
||||
#include "effectvehicle_base.h"
|
||||
#include "core/checksum_func.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -670,6 +671,7 @@ EffectVehicle *CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, Eff
|
||||
|
||||
bool EffectVehicle::Tick()
|
||||
{
|
||||
UpdateStateChecksum((((uint64) this->x_pos) << 32) | this->y_pos);
|
||||
return _effect_tick_procs[this->subtype](this);
|
||||
}
|
||||
|
||||
|
@@ -41,6 +41,8 @@ NetworkGameSocketHandler::NetworkGameSocketHandler(SOCKET s) : info(nullptr), cl
|
||||
*/
|
||||
NetworkRecvStatus NetworkGameSocketHandler::CloseConnection(bool error)
|
||||
{
|
||||
if (this->ignore_close) return NETWORK_RECV_STATUS_CONN_LOST;
|
||||
|
||||
/* Clients drop back to the main menu */
|
||||
if (!_network_server && _networking) {
|
||||
extern void ClientNetworkEmergencySave(); // from network_client.cpp
|
||||
@@ -79,6 +81,8 @@ NetworkRecvStatus NetworkGameSocketHandler::HandlePacket(Packet *p)
|
||||
case PACKET_SERVER_NEED_COMPANY_PASSWORD: return this->Receive_SERVER_NEED_COMPANY_PASSWORD(p);
|
||||
case PACKET_CLIENT_GAME_PASSWORD: return this->Receive_CLIENT_GAME_PASSWORD(p);
|
||||
case PACKET_CLIENT_COMPANY_PASSWORD: return this->Receive_CLIENT_COMPANY_PASSWORD(p);
|
||||
case PACKET_CLIENT_SETTINGS_PASSWORD: return this->Receive_CLIENT_SETTINGS_PASSWORD(p);
|
||||
case PACKET_SERVER_SETTINGS_ACCESS: return this->Receive_SERVER_SETTINGS_ACCESS(p);
|
||||
case PACKET_SERVER_WELCOME: return this->Receive_SERVER_WELCOME(p);
|
||||
case PACKET_CLIENT_GETMAP: return this->Receive_CLIENT_GETMAP(p);
|
||||
case PACKET_SERVER_WAIT: return this->Receive_SERVER_WAIT(p);
|
||||
@@ -100,6 +104,7 @@ NetworkRecvStatus NetworkGameSocketHandler::HandlePacket(Packet *p)
|
||||
case PACKET_CLIENT_QUIT: return this->Receive_CLIENT_QUIT(p);
|
||||
case PACKET_CLIENT_ERROR: return this->Receive_CLIENT_ERROR(p);
|
||||
case PACKET_CLIENT_DESYNC_LOG: return this->Receive_CLIENT_DESYNC_LOG(p);
|
||||
case PACKET_SERVER_DESYNC_LOG: return this->Receive_SERVER_DESYNC_LOG(p);
|
||||
case PACKET_SERVER_QUIT: return this->Receive_SERVER_QUIT(p);
|
||||
case PACKET_SERVER_ERROR_QUIT: return this->Receive_SERVER_ERROR_QUIT(p);
|
||||
case PACKET_SERVER_SHUTDOWN: return this->Receive_SERVER_SHUTDOWN(p);
|
||||
@@ -166,6 +171,8 @@ NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_NEED_GAME_PASSWORD(Pa
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_NEED_COMPANY_PASSWORD(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_NEED_COMPANY_PASSWORD); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_GAME_PASSWORD(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_GAME_PASSWORD); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_COMPANY_PASSWORD(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_COMPANY_PASSWORD); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_SETTINGS_PASSWORD(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_SETTINGS_PASSWORD); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_SETTINGS_ACCESS(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_SETTINGS_ACCESS); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_WELCOME(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_WELCOME); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_GETMAP(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_GETMAP); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_WAIT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_WAIT); }
|
||||
@@ -187,6 +194,7 @@ NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_SET_NAME(Packet *p) {
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_QUIT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_QUIT); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_ERROR(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_ERROR); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_DESYNC_LOG(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_DESYNC_LOG); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_DESYNC_LOG(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_DESYNC_LOG); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_QUIT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_QUIT); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_ERROR_QUIT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_ERROR_QUIT); }
|
||||
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_SHUTDOWN(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_SHUTDOWN); }
|
||||
|
@@ -63,6 +63,8 @@ enum PacketGameType {
|
||||
PACKET_CLIENT_GAME_PASSWORD, ///< Clients sends the (hashed) game password.
|
||||
PACKET_SERVER_NEED_COMPANY_PASSWORD, ///< Server requests the (hashed) company password.
|
||||
PACKET_CLIENT_COMPANY_PASSWORD, ///< Client sends the (hashed) company password.
|
||||
PACKET_CLIENT_SETTINGS_PASSWORD, ///< Client sends the (hashed) settings password.
|
||||
PACKET_SERVER_SETTINGS_ACCESS, ///< Server sends the settings access state.
|
||||
|
||||
/* The server welcomes the authenticated client and sends information of other clients. */
|
||||
PACKET_SERVER_WELCOME, ///< Server welcomes you and gives you your #ClientID.
|
||||
@@ -122,6 +124,7 @@ enum PacketGameType {
|
||||
PACKET_CLIENT_ERROR, ///< A client reports an error to the server.
|
||||
PACKET_SERVER_ERROR_QUIT, ///< A server tells that a client has hit an error and did quit.
|
||||
PACKET_CLIENT_DESYNC_LOG, ///< A client reports a desync log
|
||||
PACKET_SERVER_DESYNC_LOG, ///< A server reports a desync log
|
||||
|
||||
PACKET_END, ///< Must ALWAYS be on the end of this list!! (period)
|
||||
};
|
||||
@@ -158,6 +161,7 @@ private:
|
||||
NetworkClientInfo *info; ///< Client info related to this socket
|
||||
|
||||
protected:
|
||||
bool ignore_close = false;
|
||||
NetworkRecvStatus ReceiveInvalidPacket(PacketGameType type);
|
||||
|
||||
/**
|
||||
@@ -262,6 +266,21 @@ protected:
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_CLIENT_COMPANY_PASSWORD(Packet *p);
|
||||
|
||||
/**
|
||||
* Send a password to the server to authorize
|
||||
* uint8 Password type (see NetworkPasswordType).
|
||||
* string The password.
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_CLIENT_SETTINGS_PASSWORD(Packet *p);
|
||||
|
||||
/**
|
||||
* Indication to the client that the setting access state has changed
|
||||
* bool setting access state
|
||||
* @param p The packet that was just received.
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_SERVER_SETTINGS_ACCESS(Packet *p);
|
||||
|
||||
/**
|
||||
* The client is joined and ready to receive his map:
|
||||
* uint32 Own client ID.
|
||||
@@ -428,6 +447,7 @@ protected:
|
||||
*/
|
||||
virtual NetworkRecvStatus Receive_CLIENT_ERROR(Packet *p);
|
||||
virtual NetworkRecvStatus Receive_CLIENT_DESYNC_LOG(Packet *p);
|
||||
virtual NetworkRecvStatus Receive_SERVER_DESYNC_LOG(Packet *p);
|
||||
|
||||
/**
|
||||
* Notification that a client left the game:
|
||||
|
@@ -35,6 +35,7 @@
|
||||
#include "../core/pool_func.hpp"
|
||||
#include "../gfx_func.h"
|
||||
#include "../error.h"
|
||||
#include "../core/checksum_func.hpp"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
@@ -56,6 +57,7 @@ bool _network_server; ///< network-server is active
|
||||
bool _network_available; ///< is network mode available?
|
||||
bool _network_dedicated; ///< are we a dedicated server?
|
||||
bool _is_network_server; ///< Does this client wants to be a network-server?
|
||||
bool _network_settings_access; ///< Can this client change server settings?
|
||||
NetworkServerGameInfo _network_game_info; ///< Information about our game.
|
||||
NetworkCompanyState *_network_company_states = nullptr; ///< Statistics about some companies.
|
||||
ClientID _network_own_client_id; ///< Our client identifier.
|
||||
@@ -74,6 +76,7 @@ uint32 _sync_seed_1; ///< Seed to compare during sync checks.
|
||||
#ifdef NETWORK_SEND_DOUBLE_SEED
|
||||
uint32 _sync_seed_2; ///< Second part of the seed.
|
||||
#endif
|
||||
uint64 _sync_state_checksum; ///< State checksum to compare during sync checks.
|
||||
uint32 _sync_frame; ///< The frame to perform the sync check.
|
||||
bool _network_first_time; ///< Whether we have finished joining or not.
|
||||
bool _network_udp_server; ///< Is the UDP server started?
|
||||
@@ -893,7 +896,7 @@ void NetworkGameLoop()
|
||||
/* We don't want to log multiple times if paused. */
|
||||
static Date last_log;
|
||||
if (last_log != _date) {
|
||||
DEBUG(desync, 1, "sync: date{%08x; %02x; %02x}; %08x; %08x", _date, _date_fract, _tick_skip_counter, _random.state[0], _random.state[1]);
|
||||
DEBUG(desync, 2, "sync: date{%08x; %02x; %02x}; %08x; %08x", _date, _date_fract, _tick_skip_counter, _random.state[0], _random.state[1]);
|
||||
last_log = _date;
|
||||
}
|
||||
}
|
||||
@@ -1028,6 +1031,7 @@ void NetworkGameLoop()
|
||||
#ifdef NETWORK_SEND_DOUBLE_SEED
|
||||
_sync_seed_2 = _random.state[1];
|
||||
#endif
|
||||
_sync_state_checksum = _state_checksum.state;
|
||||
|
||||
NetworkServer_Tick(send_frame);
|
||||
} else {
|
||||
|
@@ -22,5 +22,6 @@ extern bool _network_server; ///< network-server is active
|
||||
extern bool _network_available; ///< is network mode available?
|
||||
extern bool _network_dedicated; ///< are we a dedicated server?
|
||||
extern bool _is_network_server; ///< Does this client wants to be a network-server?
|
||||
extern bool _network_settings_access; ///< Can this client change server settings?
|
||||
|
||||
#endif /* NETWORK_H */
|
||||
|
@@ -32,6 +32,8 @@
|
||||
#include "../core/backup_type.hpp"
|
||||
#include "../thread.h"
|
||||
#include "../crashlog.h"
|
||||
#include "../core/checksum_func.hpp"
|
||||
#include "../fileio_func.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
@@ -134,6 +136,7 @@ void ClientNetworkEmergencySave()
|
||||
{
|
||||
if (!_settings_client.gui.autosave_on_network_disconnect) return;
|
||||
if (!_networking) return;
|
||||
if (!ClientNetworkGameSocketHandler::EmergencySavePossible()) return;
|
||||
|
||||
const char *filename = "netsave.sav";
|
||||
DEBUG(net, 0, "Client: Performing emergency save (%s)", filename);
|
||||
@@ -158,8 +161,18 @@ ClientNetworkGameSocketHandler::~ClientNetworkGameSocketHandler()
|
||||
{
|
||||
assert(ClientNetworkGameSocketHandler::my_client == this);
|
||||
ClientNetworkGameSocketHandler::my_client = nullptr;
|
||||
_network_settings_access = false;
|
||||
|
||||
delete this->savegame;
|
||||
|
||||
if (this->desync_log_file) {
|
||||
if (!this->server_desync_log.empty()) {
|
||||
fwrite("\n", 1, 1, this->desync_log_file);
|
||||
fwrite(this->server_desync_log.data(), 1, this->server_desync_log.size(), this->desync_log_file);
|
||||
}
|
||||
FioFCloseFile(this->desync_log_file);
|
||||
this->desync_log_file = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NetworkRecvStatus ClientNetworkGameSocketHandler::CloseConnection(NetworkRecvStatus status)
|
||||
@@ -173,6 +186,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::CloseConnection(NetworkRecvSta
|
||||
* that code any more complex or more aware of the validity of the socket.
|
||||
*/
|
||||
if (this->sock == INVALID_SOCKET) return status;
|
||||
if (this->status == STATUS_CLOSING) return status;
|
||||
|
||||
DEBUG(net, 1, "Shutting down client connection %d", this->client_id);
|
||||
|
||||
@@ -190,6 +204,12 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::CloseConnection(NetworkRecvSta
|
||||
|
||||
DEBUG(net, 1, "Shutdown client connection %d", this->client_id);
|
||||
|
||||
if (status == NETWORK_RECV_STATUS_DESYNC) {
|
||||
this->status = STATUS_CLOSING;
|
||||
this->ignore_close = true;
|
||||
this->ReceivePackets();
|
||||
}
|
||||
|
||||
delete this->GetInfo();
|
||||
delete this;
|
||||
|
||||
@@ -282,16 +302,26 @@ void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res)
|
||||
if (_sync_frame != 0) {
|
||||
if (_sync_frame == _frame_counter) {
|
||||
#ifdef NETWORK_SEND_DOUBLE_SEED
|
||||
if (_sync_seed_1 != _random.state[0] || _sync_seed_2 != _random.state[1]) {
|
||||
if (_sync_seed_1 != _random.state[0] || _sync_seed_2 != _random.state[1] || _sync_state_checksum != _state_checksum.state) {
|
||||
#else
|
||||
if (_sync_seed_1 != _random.state[0]) {
|
||||
if (_sync_seed_1 != _random.state[0] || _sync_state_checksum != _state_checksum.state) {
|
||||
#endif
|
||||
DesyncExtraInfo info;
|
||||
if (_sync_seed_1 != _random.state[0]) info.flags |= DesyncExtraInfo::DEIF_RAND1;
|
||||
#ifdef NETWORK_SEND_DOUBLE_SEED
|
||||
if (_sync_seed_2 != _random.state[1]) info.flags |= DesyncExtraInfo::DEIF_RAND2;
|
||||
info.flags |= DesyncExtraInfo::DEIF_DBL_RAND;
|
||||
#endif
|
||||
if (_sync_state_checksum != _state_checksum.state) info.flags |= DesyncExtraInfo::DEIF_STATE;
|
||||
|
||||
NetworkError(STR_NETWORK_ERROR_DESYNC);
|
||||
DEBUG(desync, 1, "sync_err: date{%08x; %02x; %02x}", _date, _date_fract, _tick_skip_counter);
|
||||
DEBUG(desync, 1, "sync_err: date{%08x; %02x; %02x} {%x, " OTTD_PRINTFHEX64 "} != {%x, " OTTD_PRINTFHEX64 "}"
|
||||
, _date, _date_fract, _tick_skip_counter, _sync_seed_1, _sync_state_checksum, _random.state[0], _state_checksum.state);
|
||||
DEBUG(net, 0, "Sync error detected!");
|
||||
|
||||
std::string desync_log;
|
||||
CrashLog::DesyncCrashLog(nullptr, &desync_log);
|
||||
info.log_file = &(my_client->desync_log_file);
|
||||
CrashLog::DesyncCrashLog(nullptr, &desync_log, info);
|
||||
my_client->SendDesyncLog(desync_log);
|
||||
my_client->ClientError(NETWORK_RECV_STATUS_DESYNC);
|
||||
return false;
|
||||
@@ -315,6 +345,14 @@ void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool ClientNetworkGameSocketHandler::EmergencySavePossible()
|
||||
{
|
||||
if (!my_client) return false;
|
||||
if (my_client->emergency_save_done) return false;
|
||||
my_client->emergency_save_done = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** Our client's connection. */
|
||||
ClientNetworkGameSocketHandler * ClientNetworkGameSocketHandler::my_client = nullptr;
|
||||
@@ -323,8 +361,14 @@ ClientNetworkGameSocketHandler * ClientNetworkGameSocketHandler::my_client = nul
|
||||
static uint32 last_ack_frame;
|
||||
|
||||
/** One bit of 'entropy' used to generate a salt for the company passwords. */
|
||||
static uint32 _password_game_seed;
|
||||
/** The other bit of 'entropy' used to generate a salt for the company passwords. */
|
||||
static uint32 _company_password_game_seed;
|
||||
/** One bit of 'entropy' used to generate a salt for the server passwords. */
|
||||
static uint32 _server_password_game_seed;
|
||||
/** One bit of 'entropy' used to generate a salt for the rcon passwords. */
|
||||
static uint32 _rcon_password_game_seed;
|
||||
/** One bit of 'entropy' used to generate a salt for the settings passwords. */
|
||||
static uint32 _settings_password_game_seed;
|
||||
/** The other bit of 'entropy' used to generate a salt for the company, server, rcon, and settings passwords. */
|
||||
static char _password_server_id[NETWORK_SERVER_ID_LENGTH];
|
||||
|
||||
/** Maximum number of companies of the currently joined server. */
|
||||
@@ -392,7 +436,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendNewGRFsOk()
|
||||
NetworkRecvStatus ClientNetworkGameSocketHandler::SendGamePassword(const char *password)
|
||||
{
|
||||
Packet *p = new Packet(PACKET_CLIENT_GAME_PASSWORD);
|
||||
p->Send_string(password);
|
||||
p->Send_string(GenerateCompanyPasswordHash(password, _password_server_id, _server_password_game_seed));
|
||||
my_client->SendPacket(p);
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
@@ -404,7 +448,19 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendGamePassword(const char *p
|
||||
NetworkRecvStatus ClientNetworkGameSocketHandler::SendCompanyPassword(const char *password)
|
||||
{
|
||||
Packet *p = new Packet(PACKET_CLIENT_COMPANY_PASSWORD);
|
||||
p->Send_string(GenerateCompanyPasswordHash(password, _password_server_id, _password_game_seed));
|
||||
p->Send_string(GenerateCompanyPasswordHash(password, _password_server_id, _company_password_game_seed));
|
||||
my_client->SendPacket(p);
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the game password as requested.
|
||||
* @param password The game password.
|
||||
*/
|
||||
NetworkRecvStatus ClientNetworkGameSocketHandler::SendSettingsPassword(const char *password)
|
||||
{
|
||||
Packet *p = new Packet(PACKET_CLIENT_SETTINGS_PASSWORD);
|
||||
p->Send_string(GenerateCompanyPasswordHash(password, _password_server_id, _settings_password_game_seed));
|
||||
my_client->SendPacket(p);
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
@@ -502,7 +558,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendSetPassword(const char *pa
|
||||
{
|
||||
Packet *p = new Packet(PACKET_CLIENT_SET_PASSWORD);
|
||||
|
||||
p->Send_string(GenerateCompanyPasswordHash(password, _password_server_id, _password_game_seed));
|
||||
p->Send_string(GenerateCompanyPasswordHash(password, _password_server_id, _company_password_game_seed));
|
||||
my_client->SendPacket(p);
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
@@ -539,7 +595,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendQuit()
|
||||
NetworkRecvStatus ClientNetworkGameSocketHandler::SendRCon(const char *pass, const char *command)
|
||||
{
|
||||
Packet *p = new Packet(PACKET_CLIENT_RCON);
|
||||
p->Send_string(pass);
|
||||
p->Send_string(GenerateCompanyPasswordHash(pass, _password_server_id, _rcon_password_game_seed));
|
||||
p->Send_string(command);
|
||||
my_client->SendPacket(p);
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
@@ -554,7 +610,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendMove(CompanyID company, co
|
||||
{
|
||||
Packet *p = new Packet(PACKET_CLIENT_MOVE);
|
||||
p->Send_uint8(company);
|
||||
p->Send_string(GenerateCompanyPasswordHash(password, _password_server_id, _password_game_seed));
|
||||
p->Send_string(GenerateCompanyPasswordHash(password, _password_server_id, _company_password_game_seed));
|
||||
my_client->SendPacket(p);
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
@@ -771,6 +827,10 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_NEED_GAME_PASSW
|
||||
if (this->status < STATUS_JOIN || this->status >= STATUS_AUTH_GAME) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
|
||||
this->status = STATUS_AUTH_GAME;
|
||||
|
||||
_server_password_game_seed = p->Recv_uint32();
|
||||
p->Recv_string(_password_server_id, sizeof(_password_server_id));
|
||||
if (this->HasClientQuit()) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
|
||||
|
||||
const char *password = _network_join_server_password;
|
||||
if (!StrEmpty(password)) {
|
||||
return SendGamePassword(password);
|
||||
@@ -786,7 +846,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_NEED_COMPANY_PA
|
||||
if (this->status < STATUS_JOIN || this->status >= STATUS_AUTH_COMPANY) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
|
||||
this->status = STATUS_AUTH_COMPANY;
|
||||
|
||||
_password_game_seed = p->Recv_uint32();
|
||||
_company_password_game_seed = p->Recv_uint32();
|
||||
p->Recv_string(_password_server_id, sizeof(_password_server_id));
|
||||
if (this->HasClientQuit()) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
|
||||
|
||||
@@ -808,7 +868,10 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_WELCOME(Packet
|
||||
_network_own_client_id = (ClientID)p->Recv_uint32();
|
||||
|
||||
/* Initialize the password hash salting variables, even if they were previously. */
|
||||
_password_game_seed = p->Recv_uint32();
|
||||
_company_password_game_seed = p->Recv_uint32();
|
||||
_server_password_game_seed = p->Recv_uint32();
|
||||
_rcon_password_game_seed = p->Recv_uint32();
|
||||
_settings_password_game_seed = p->Recv_uint32();
|
||||
p->Recv_string(_password_server_id, sizeof(_password_server_id));
|
||||
|
||||
/* Start receiving the map */
|
||||
@@ -932,6 +995,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet
|
||||
|
||||
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_FRAME(Packet *p)
|
||||
{
|
||||
if (this->status == STATUS_CLOSING) return NETWORK_RECV_STATUS_OKAY;
|
||||
if (this->status != STATUS_ACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
|
||||
|
||||
_frame_counter_server = p->Recv_uint32();
|
||||
@@ -945,6 +1009,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_FRAME(Packet *p
|
||||
#ifdef NETWORK_SEND_DOUBLE_SEED
|
||||
_sync_seed_2 = p->Recv_uint32();
|
||||
#endif
|
||||
_sync_state_checksum = p->Recv_uint64();
|
||||
}
|
||||
#endif
|
||||
/* Receive the token. */
|
||||
@@ -965,6 +1030,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_FRAME(Packet *p
|
||||
|
||||
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_SYNC(Packet *p)
|
||||
{
|
||||
if (this->status == STATUS_CLOSING) return NETWORK_RECV_STATUS_OKAY;
|
||||
if (this->status != STATUS_ACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
|
||||
|
||||
_sync_frame = p->Recv_uint32();
|
||||
@@ -972,12 +1038,14 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_SYNC(Packet *p)
|
||||
#ifdef NETWORK_SEND_DOUBLE_SEED
|
||||
_sync_seed_2 = p->Recv_uint32();
|
||||
#endif
|
||||
_sync_state_checksum = p->Recv_uint64();
|
||||
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_COMMAND(Packet *p)
|
||||
{
|
||||
if (this->status == STATUS_CLOSING) return NETWORK_RECV_STATUS_OKAY;
|
||||
if (this->status != STATUS_ACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
|
||||
|
||||
CommandPacket cp;
|
||||
@@ -997,6 +1065,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_COMMAND(Packet
|
||||
|
||||
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CHAT(Packet *p)
|
||||
{
|
||||
if (this->status == STATUS_CLOSING) return NETWORK_RECV_STATUS_OKAY;
|
||||
if (this->status != STATUS_ACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
|
||||
|
||||
char name[NETWORK_NAME_LENGTH], msg[NETWORK_CHAT_LENGTH];
|
||||
@@ -1054,6 +1123,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR_QUIT(Pack
|
||||
if (this->status < STATUS_AUTHORIZED) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
|
||||
|
||||
ClientID client_id = (ClientID)p->Recv_uint32();
|
||||
if (client_id == _network_own_client_id) return NETWORK_RECV_STATUS_OKAY; // do not try to clear our own client info
|
||||
|
||||
NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(client_id);
|
||||
if (ci != nullptr) {
|
||||
@@ -1066,6 +1136,15 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR_QUIT(Pack
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_DESYNC_LOG(Packet *p)
|
||||
{
|
||||
uint size = p->Recv_uint16();
|
||||
this->server_desync_log.resize(this->server_desync_log.size() + size);
|
||||
p->Recv_binary(const_cast<char *>(this->server_desync_log.data() + this->server_desync_log.size() - size), size);
|
||||
DEBUG(net, 2, "Received %u bytes of server desync log", size);
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_QUIT(Packet *p)
|
||||
{
|
||||
if (this->status < STATUS_AUTHORIZED) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
|
||||
@@ -1195,6 +1274,17 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_COMPANY_UPDATE(
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_SETTINGS_ACCESS(Packet *p)
|
||||
{
|
||||
if (this->status < STATUS_ACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
|
||||
|
||||
_network_settings_access = p->Recv_bool();
|
||||
|
||||
ReInitAllWindows();
|
||||
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the connection's state, i.e. is the connection still up?
|
||||
*/
|
||||
@@ -1249,6 +1339,16 @@ void NetworkClientSendRcon(const char *password, const char *command)
|
||||
MyClient::SendRCon(password, command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send settings password.
|
||||
* @param password The password.
|
||||
* @param command The command to execute.
|
||||
*/
|
||||
void NetworkClientSendSettingsPassword(const char *password)
|
||||
{
|
||||
MyClient::SendSettingsPassword(password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the server of this client wanting to be moved to another company.
|
||||
* @param company_id id of the company the client wishes to be moved to.
|
||||
|
@@ -32,11 +32,16 @@ private:
|
||||
STATUS_MAP_WAIT, ///< The client is waiting as someone else is downloading the map.
|
||||
STATUS_MAP, ///< The client is downloading the map.
|
||||
STATUS_ACTIVE, ///< The client is active within in the game.
|
||||
STATUS_CLOSING, ///< The client connection is in the process of being closed.
|
||||
STATUS_END, ///< Must ALWAYS be on the end of this list!! (period)
|
||||
};
|
||||
|
||||
ServerStatus status; ///< Status of the connection with the server.
|
||||
|
||||
FILE *desync_log_file = nullptr;
|
||||
std::string server_desync_log;
|
||||
bool emergency_save_done = false;
|
||||
|
||||
protected:
|
||||
friend void NetworkExecuteLocalCommandQueue();
|
||||
friend void NetworkClose(bool close_admins);
|
||||
@@ -49,6 +54,7 @@ protected:
|
||||
NetworkRecvStatus Receive_SERVER_CLIENT_INFO(Packet *p) override;
|
||||
NetworkRecvStatus Receive_SERVER_NEED_GAME_PASSWORD(Packet *p) override;
|
||||
NetworkRecvStatus Receive_SERVER_NEED_COMPANY_PASSWORD(Packet *p) override;
|
||||
NetworkRecvStatus Receive_SERVER_SETTINGS_ACCESS(Packet *p) override;
|
||||
NetworkRecvStatus Receive_SERVER_WELCOME(Packet *p) override;
|
||||
NetworkRecvStatus Receive_SERVER_WAIT(Packet *p) override;
|
||||
NetworkRecvStatus Receive_SERVER_MAP_BEGIN(Packet *p) override;
|
||||
@@ -62,6 +68,7 @@ protected:
|
||||
NetworkRecvStatus Receive_SERVER_CHAT(Packet *p) override;
|
||||
NetworkRecvStatus Receive_SERVER_QUIT(Packet *p) override;
|
||||
NetworkRecvStatus Receive_SERVER_ERROR_QUIT(Packet *p) override;
|
||||
NetworkRecvStatus Receive_SERVER_DESYNC_LOG(Packet *p) override;
|
||||
NetworkRecvStatus Receive_SERVER_SHUTDOWN(Packet *p) override;
|
||||
NetworkRecvStatus Receive_SERVER_NEWGAME(Packet *p) override;
|
||||
NetworkRecvStatus Receive_SERVER_RCON(Packet *p) override;
|
||||
@@ -92,6 +99,7 @@ public:
|
||||
|
||||
static NetworkRecvStatus SendGamePassword(const char *password);
|
||||
static NetworkRecvStatus SendCompanyPassword(const char *password);
|
||||
static NetworkRecvStatus SendSettingsPassword(const char *password);
|
||||
|
||||
static NetworkRecvStatus SendChat(NetworkAction action, DestType type, int dest, const char *msg, NetworkTextMessageData data);
|
||||
static NetworkRecvStatus SendSetPassword(const char *password);
|
||||
@@ -104,6 +112,8 @@ public:
|
||||
static void Send();
|
||||
static bool Receive();
|
||||
static bool GameLoop();
|
||||
|
||||
static bool EmergencySavePossible();
|
||||
};
|
||||
|
||||
/** Helper to make the code look somewhat nicer. */
|
||||
|
@@ -54,6 +54,7 @@ void NetworkClientsToSpectators(CompanyID cid);
|
||||
void NetworkClientConnectGame(NetworkAddress address, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr);
|
||||
void NetworkClientRequestMove(CompanyID company, const char *pass = "");
|
||||
void NetworkClientSendRcon(const char *password, const char *command);
|
||||
void NetworkClientSendSettingsPassword(const char *password);
|
||||
void NetworkClientSendChat(NetworkAction action, DestType type, int dest, const char *msg, NetworkTextMessageData data = NetworkTextMessageData());
|
||||
bool NetworkClientPreferTeamChat(const NetworkClientInfo *cio);
|
||||
bool NetworkCompanyIsPassworded(CompanyID company_id);
|
||||
|
@@ -118,6 +118,7 @@ extern uint32 _sync_seed_1;
|
||||
#ifdef NETWORK_SEND_DOUBLE_SEED
|
||||
extern uint32 _sync_seed_2;
|
||||
#endif
|
||||
extern uint64 _sync_state_checksum;
|
||||
extern uint32 _sync_frame;
|
||||
extern bool _network_first_time;
|
||||
/* Vars needed for the join-GUI */
|
||||
|
@@ -216,6 +216,9 @@ ServerNetworkGameSocketHandler::ServerNetworkGameSocketHandler(SOCKET s) : Netwo
|
||||
this->status = STATUS_INACTIVE;
|
||||
this->client_id = _network_client_id++;
|
||||
this->receive_limit = _settings_client.network.bytes_per_frame_burst;
|
||||
this->server_hash_bits = InteractiveRandom();
|
||||
this->rcon_hash_bits = InteractiveRandom();
|
||||
this->settings_hash_bits = InteractiveRandom();
|
||||
|
||||
/* The Socket and Info pools need to be the same in size. After all,
|
||||
* each Socket will be associated with at most one Info object. As
|
||||
@@ -320,7 +323,14 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::CloseConnection(NetworkRecvSta
|
||||
NetworkClientSocket *cs;
|
||||
FOR_ALL_CLIENT_SOCKETS(cs) {
|
||||
if (cs->writable) {
|
||||
if (cs->SendPackets() != SPS_CLOSED && cs->status == STATUS_MAP) {
|
||||
if (cs->status == STATUS_CLOSE_PENDING) {
|
||||
SendPacketsState send_state = cs->SendPackets(true);
|
||||
if (send_state == SPS_CLOSED) {
|
||||
cs->CloseConnection(NETWORK_RECV_STATUS_CONN_LOST);
|
||||
} else if (send_state != SPS_PARTLY_SENT && send_state != SPS_NONE_SENT) {
|
||||
ShutdownSocket(cs->sock, true, false, 2);
|
||||
}
|
||||
} else if (cs->SendPackets() != SPS_CLOSED && cs->status == STATUS_MAP) {
|
||||
/* This client is in the middle of a map-send, call the function for that */
|
||||
cs->SendMap();
|
||||
}
|
||||
@@ -461,6 +471,20 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendError(NetworkErrorCode err
|
||||
return this->CloseConnection(NETWORK_RECV_STATUS_SERVER_ERROR);
|
||||
}
|
||||
|
||||
NetworkRecvStatus ServerNetworkGameSocketHandler::SendDesyncLog(const std::string &log)
|
||||
{
|
||||
for (size_t offset = 0; offset < log.size();) {
|
||||
Packet *p = new Packet(PACKET_SERVER_DESYNC_LOG);
|
||||
size_t size = min<size_t>(log.size() - offset, SHRT_MAX - 2 - p->size);
|
||||
p->Send_uint16(size);
|
||||
p->Send_binary(log.data() + offset, size);
|
||||
this->SendPacket(p);
|
||||
|
||||
offset += size;
|
||||
}
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
/** Send the check for the NewGRFs. */
|
||||
NetworkRecvStatus ServerNetworkGameSocketHandler::SendNewGRFCheck()
|
||||
{
|
||||
@@ -492,6 +516,8 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendNeedGamePassword()
|
||||
this->last_frame = this->last_frame_server = _frame_counter;
|
||||
|
||||
Packet *p = new Packet(PACKET_SERVER_NEED_GAME_PASSWORD);
|
||||
p->Send_uint32(_settings_game.game_creation.generation_seed ^ this->server_hash_bits);
|
||||
p->Send_string(_settings_client.network.network_id);
|
||||
this->SendPacket(p);
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
@@ -531,6 +557,9 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendWelcome()
|
||||
p = new Packet(PACKET_SERVER_WELCOME);
|
||||
p->Send_uint32(this->client_id);
|
||||
p->Send_uint32(_settings_game.game_creation.generation_seed);
|
||||
p->Send_uint32(_settings_game.game_creation.generation_seed ^ this->server_hash_bits);
|
||||
p->Send_uint32(_settings_game.game_creation.generation_seed ^ this->rcon_hash_bits);
|
||||
p->Send_uint32(_settings_game.game_creation.generation_seed ^ this->settings_hash_bits);
|
||||
p->Send_string(_settings_client.network.network_id);
|
||||
this->SendPacket(p);
|
||||
|
||||
@@ -689,6 +718,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendFrame()
|
||||
#ifdef NETWORK_SEND_DOUBLE_SEED
|
||||
p->Send_uint32(_sync_seed_2);
|
||||
#endif
|
||||
p->Send_uint64(_sync_state_checksum);
|
||||
#endif
|
||||
|
||||
/* If token equals 0, we need to make a new token and send that. */
|
||||
@@ -711,6 +741,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendSync()
|
||||
#ifdef NETWORK_SEND_DOUBLE_SEED
|
||||
p->Send_uint32(_sync_seed_2);
|
||||
#endif
|
||||
p->Send_uint64(_sync_state_checksum);
|
||||
this->SendPacket(p);
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
@@ -852,6 +883,15 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendConfigUpdate()
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
NetworkRecvStatus ServerNetworkGameSocketHandler::SendSettingsAccessUpdate(bool ok)
|
||||
{
|
||||
Packet *p = new Packet(PACKET_SERVER_SETTINGS_ACCESS);
|
||||
p->Send_bool(ok);
|
||||
this->SendPacket(p);
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
|
||||
/***********
|
||||
* Receiving functions
|
||||
* DEF_SERVER_RECEIVE_COMMAND has parameter: NetworkClientSocket *cs, Packet *p
|
||||
@@ -970,7 +1010,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_GAME_PASSWORD(P
|
||||
|
||||
/* Check game password. Allow joining if we cleared the password meanwhile */
|
||||
if (!StrEmpty(_settings_client.network.server_password) &&
|
||||
strcmp(password, _settings_client.network.server_password) != 0) {
|
||||
strcmp(password, GenerateCompanyPasswordHash(_settings_client.network.server_password, _settings_client.network.network_id, _settings_game.game_creation.generation_seed ^ this->server_hash_bits)) != 0) {
|
||||
/* Password is invalid */
|
||||
return this->SendError(NETWORK_ERROR_WRONG_PASSWORD);
|
||||
}
|
||||
@@ -1006,6 +1046,29 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMPANY_PASSWOR
|
||||
return this->SendWelcome();
|
||||
}
|
||||
|
||||
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_SETTINGS_PASSWORD(Packet *p)
|
||||
{
|
||||
if (this->status != STATUS_ACTIVE) {
|
||||
/* Illegal call, return error and ignore the packet */
|
||||
return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
|
||||
}
|
||||
|
||||
char password[NETWORK_PASSWORD_LENGTH];
|
||||
p->Recv_string(password, sizeof(password));
|
||||
|
||||
/* Check settings password. Deny if no password is set */
|
||||
if (StrEmpty(_settings_client.network.settings_password) ||
|
||||
strcmp(password, GenerateCompanyPasswordHash(_settings_client.network.settings_password, _settings_client.network.network_id, _settings_game.game_creation.generation_seed ^ this->settings_hash_bits)) != 0) {
|
||||
DEBUG(net, 0, "[settings-ctrl] wrong password from client-id %d", this->client_id);
|
||||
this->settings_authed = false;
|
||||
} else {
|
||||
DEBUG(net, 0, "[settings-ctrl] client-id %d", this->client_id);
|
||||
this->settings_authed = true;
|
||||
}
|
||||
|
||||
return this->SendSettingsAccessUpdate(this->settings_authed);
|
||||
}
|
||||
|
||||
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_GETMAP(Packet *p)
|
||||
{
|
||||
NetworkClientSocket *new_cs;
|
||||
@@ -1100,12 +1163,12 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMMAND(Packet
|
||||
}
|
||||
|
||||
|
||||
if ((GetCommandFlags(cp.cmd) & CMD_SERVER) && ci->client_id != CLIENT_ID_SERVER) {
|
||||
if ((GetCommandFlags(cp.cmd) & CMD_SERVER) && ci->client_id != CLIENT_ID_SERVER && !this->settings_authed) {
|
||||
IConsolePrintF(CC_ERROR, "WARNING: server only command from: client %d (IP: %s), kicking...", ci->client_id, this->GetClientIP());
|
||||
return this->SendError(NETWORK_ERROR_KICKED);
|
||||
}
|
||||
|
||||
if ((GetCommandFlags(cp.cmd) & CMD_SPECTATOR) == 0 && !Company::IsValidID(cp.company) && ci->client_id != CLIENT_ID_SERVER) {
|
||||
if ((GetCommandFlags(cp.cmd) & CMD_SPECTATOR) == 0 && !Company::IsValidID(cp.company) && ci->client_id != CLIENT_ID_SERVER && !this->settings_authed) {
|
||||
IConsolePrintF(CC_ERROR, "WARNING: spectator issuing command from client %d (IP: %s), kicking...", ci->client_id, this->GetClientIP());
|
||||
return this->SendError(NETWORK_ERROR_KICKED);
|
||||
}
|
||||
@@ -1115,7 +1178,8 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMMAND(Packet
|
||||
* to match the company in the packet. If it doesn't, the client has done
|
||||
* something pretty naughty (or a bug), and will be kicked
|
||||
*/
|
||||
if (!(cp.cmd == CMD_COMPANY_CTRL && cp.p1 == 0 && ci->client_playas == COMPANY_NEW_COMPANY) && ci->client_playas != cp.company) {
|
||||
if (!(cp.cmd == CMD_COMPANY_CTRL && cp.p1 == 0 && ci->client_playas == COMPANY_NEW_COMPANY) && ci->client_playas != cp.company &&
|
||||
!((GetCommandFlags(cp.cmd) & CMD_SERVER) && this->settings_authed)) {
|
||||
IConsolePrintF(CC_ERROR, "WARNING: client %d (IP: %s) tried to execute a command as company %d, kicking...",
|
||||
ci->client_playas + 1, this->GetClientIP(), cp.company + 1);
|
||||
return this->SendError(NETWORK_ERROR_COMPANY_MISMATCH);
|
||||
@@ -1171,12 +1235,22 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_ERROR(Packet *p
|
||||
NetworkAdminClientError(this->client_id, errorno);
|
||||
|
||||
if (errorno == NETWORK_ERROR_DESYNC) {
|
||||
CrashLog::DesyncCrashLog(&(this->desync_log), nullptr);
|
||||
std::string server_desync_log;
|
||||
CrashLog::DesyncCrashLog(&(this->desync_log), &server_desync_log, DesyncExtraInfo{});
|
||||
this->SendDesyncLog(server_desync_log);
|
||||
|
||||
// decrease the sync frequency for this point onwards
|
||||
_settings_client.network.sync_freq = min<uint16>(_settings_client.network.sync_freq, 16);
|
||||
|
||||
// have the server and all clients run some sanity checks
|
||||
NetworkSendCommand(0, 0, 0, CMD_DESYNC_CHECK, nullptr, nullptr, _local_company, 0);
|
||||
}
|
||||
|
||||
SendPacketsState send_state = this->SendPackets(true);
|
||||
if (send_state != SPS_CLOSED) {
|
||||
this->status = STATUS_CLOSE_PENDING;
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
}
|
||||
return this->CloseConnection(NETWORK_RECV_STATUS_CONN_LOST);
|
||||
}
|
||||
|
||||
@@ -1476,7 +1550,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_RCON(Packet *p)
|
||||
p->Recv_string(pass, sizeof(pass));
|
||||
p->Recv_string(command, sizeof(command));
|
||||
|
||||
if (strcmp(pass, _settings_client.network.rcon_password) != 0) {
|
||||
if (strcmp(pass, GenerateCompanyPasswordHash(_settings_client.network.rcon_password, _settings_client.network.network_id, _settings_game.game_creation.generation_seed ^ this->rcon_hash_bits)) != 0) {
|
||||
DEBUG(net, 0, "[rcon] wrong password from client-id %d", this->client_id);
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
@@ -1910,6 +1984,7 @@ void NetworkServer_Tick(bool send_frame)
|
||||
break;
|
||||
|
||||
case NetworkClientSocket::STATUS_MAP_WAIT:
|
||||
case NetworkClientSocket::STATUS_CLOSE_PENDING:
|
||||
/* This is an internal state where we do not wait
|
||||
* on the client to move to a different state. */
|
||||
break;
|
||||
@@ -1919,7 +1994,7 @@ void NetworkServer_Tick(bool send_frame)
|
||||
NOT_REACHED();
|
||||
}
|
||||
|
||||
if (cs->status >= NetworkClientSocket::STATUS_PRE_ACTIVE) {
|
||||
if (cs->status >= NetworkClientSocket::STATUS_PRE_ACTIVE && cs->status != NetworkClientSocket::STATUS_CLOSE_PENDING) {
|
||||
/* Check if we can send command, and if we have anything in the queue */
|
||||
NetworkHandleCommandQueue(cs);
|
||||
|
||||
@@ -1981,7 +2056,8 @@ void NetworkServerShowStatusToConsole()
|
||||
"loading map",
|
||||
"map done",
|
||||
"ready",
|
||||
"active"
|
||||
"active",
|
||||
"close pending"
|
||||
};
|
||||
assert_compile(lengthof(stat_str) == NetworkClientSocket::STATUS_END);
|
||||
|
||||
|
@@ -29,6 +29,7 @@ protected:
|
||||
NetworkRecvStatus Receive_CLIENT_COMPANY_INFO(Packet *p) override;
|
||||
NetworkRecvStatus Receive_CLIENT_GAME_PASSWORD(Packet *p) override;
|
||||
NetworkRecvStatus Receive_CLIENT_COMPANY_PASSWORD(Packet *p) override;
|
||||
NetworkRecvStatus Receive_CLIENT_SETTINGS_PASSWORD(Packet *p) override;
|
||||
NetworkRecvStatus Receive_CLIENT_GETMAP(Packet *p) override;
|
||||
NetworkRecvStatus Receive_CLIENT_MAP_OK(Packet *p) override;
|
||||
NetworkRecvStatus Receive_CLIENT_ACK(Packet *p) override;
|
||||
@@ -63,6 +64,7 @@ public:
|
||||
STATUS_DONE_MAP, ///< The client has downloaded the map.
|
||||
STATUS_PRE_ACTIVE, ///< The client is catching up the delayed frames.
|
||||
STATUS_ACTIVE, ///< The client is active within in the game.
|
||||
STATUS_CLOSE_PENDING, ///< The client connection is pending closure.
|
||||
STATUS_END, ///< Must ALWAYS be on the end of this list!! (period).
|
||||
};
|
||||
|
||||
@@ -72,6 +74,10 @@ public:
|
||||
ClientStatus status; ///< Status of this client
|
||||
CommandQueue outgoing_queue; ///< The command-queue awaiting delivery
|
||||
int receive_limit; ///< Amount of bytes that we can receive at this moment
|
||||
uint32 server_hash_bits; ///< Server password hash entropy bits
|
||||
uint32 rcon_hash_bits; ///< Rcon password hash entropy bits
|
||||
uint32 settings_hash_bits; ///< Settings password hash entropy bits
|
||||
bool settings_authed = false;///< Authorised to control all game settings
|
||||
|
||||
struct PacketWriter *savegame; ///< Writer used to write the savegame.
|
||||
NetworkAddress client_address; ///< IP-address of the client (so he can be banned)
|
||||
@@ -95,6 +101,7 @@ public:
|
||||
|
||||
NetworkRecvStatus SendClientInfo(NetworkClientInfo *ci);
|
||||
NetworkRecvStatus SendError(NetworkErrorCode error);
|
||||
NetworkRecvStatus SendDesyncLog(const std::string &log);
|
||||
NetworkRecvStatus SendChat(NetworkAction action, ClientID client_id, bool self_send, const char *msg, NetworkTextMessageData data);
|
||||
NetworkRecvStatus SendJoin(ClientID client_id);
|
||||
NetworkRecvStatus SendFrame();
|
||||
@@ -102,6 +109,7 @@ public:
|
||||
NetworkRecvStatus SendCommand(const CommandPacket *cp);
|
||||
NetworkRecvStatus SendCompanyUpdate();
|
||||
NetworkRecvStatus SendConfigUpdate();
|
||||
NetworkRecvStatus SendSettingsAccessUpdate(bool ok);
|
||||
|
||||
static void Send();
|
||||
static void AcceptConnection(SOCKET s, const NetworkAddress &address);
|
||||
|
@@ -719,9 +719,12 @@ bool NewHouseTileLoop(TileIndex tile)
|
||||
|
||||
/* Check callback 21, which determines if a house should be destroyed. */
|
||||
if (HasBit(hs->callback_mask, CBM_HOUSE_DESTRUCTION)) {
|
||||
uint16 callback_res = GetHouseCallback(CBID_HOUSE_DESTRUCTION, 0, 0, GetHouseType(tile), Town::GetByTile(tile), tile);
|
||||
Town *t = Town::GetByTile(tile);
|
||||
uint16 callback_res = GetHouseCallback(CBID_HOUSE_DESTRUCTION, 0, 0, GetHouseType(tile), t, tile);
|
||||
if (callback_res != CALLBACK_FAILED && Convert8bitBooleanCallback(hs->grf_prop.grffile, CBID_HOUSE_DESTRUCTION, callback_res)) {
|
||||
ClearTownHouse(Town::GetByTile(tile), tile);
|
||||
ClearTownHouse(t, tile);
|
||||
extern void RemoveNearbyStations(Town *t);
|
||||
RemoveNearbyStations(t);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -78,6 +78,7 @@
|
||||
#include "string_func_extra.h"
|
||||
#include "industry.h"
|
||||
#include "cargopacket.h"
|
||||
#include "core/checksum_func.hpp"
|
||||
|
||||
#include "linkgraph/linkgraphschedule.h"
|
||||
#include "tracerestrict.h"
|
||||
@@ -104,6 +105,8 @@ GameEventFlags _game_events_overall;
|
||||
|
||||
time_t _game_load_time;
|
||||
|
||||
SimpleChecksum64 _state_checksum;
|
||||
|
||||
/**
|
||||
* Error handling for fatal user errors.
|
||||
* @param s the string to print.
|
||||
@@ -404,6 +407,7 @@ static void ShutdownGame()
|
||||
*/
|
||||
static void LoadIntroGame(bool load_newgrfs = true)
|
||||
{
|
||||
UnshowCriticalError();
|
||||
Window *v;
|
||||
FOR_ALL_WINDOWS_FROM_FRONT(v) delete v;
|
||||
|
||||
@@ -1636,6 +1640,12 @@ void CheckCaches(bool force_check, std::function<void(const char *)> log)
|
||||
|
||||
if (!CargoPacket::ValidateDeferredCargoPayments()) CCLOG("Cargo packets deferred payments validation failed");
|
||||
|
||||
if (_order_destination_refcount_map_valid) {
|
||||
btree::btree_map<uint32, uint32> saved_order_destination_refcount_map = std::move(_order_destination_refcount_map);
|
||||
IntialiseOrderDestinationRefcountMap();
|
||||
if (saved_order_destination_refcount_map != _order_destination_refcount_map) CCLOG("Order destination refcount map mismatch");
|
||||
}
|
||||
|
||||
#undef CCLOG
|
||||
}
|
||||
|
||||
@@ -1745,6 +1755,11 @@ void StateGameLoop()
|
||||
CallWindowGameTickEvent();
|
||||
NewsLoop();
|
||||
cur_company.Restore();
|
||||
|
||||
Company *c;
|
||||
FOR_ALL_COMPANIES(c) {
|
||||
UpdateStateChecksum(c->money);
|
||||
}
|
||||
}
|
||||
|
||||
assert(IsLocalCompany());
|
||||
|
@@ -63,6 +63,7 @@ void IntialiseOrderDestinationRefcountMap()
|
||||
ClearOrderDestinationRefcountMap();
|
||||
const Vehicle *v;
|
||||
FOR_ALL_VEHICLES(v) {
|
||||
if (v != v->FirstShared()) continue;
|
||||
const Order *order;
|
||||
FOR_VEHICLE_ORDERS(v, order) {
|
||||
if (order->IsType(OT_GOTO_STATION) || order->IsType(OT_GOTO_WAYPOINT) || order->IsType(OT_IMPLICIT)) {
|
||||
|
@@ -516,8 +516,8 @@ void CDECL HandleCrash(int signum, siginfo_t *si, void *context)
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void CrashLog::DesyncCrashLog(const std::string *log_in, std::string *log_out)
|
||||
/* static */ void CrashLog::DesyncCrashLog(const std::string *log_in, std::string *log_out, const DesyncExtraInfo &info)
|
||||
{
|
||||
CrashLogOSX log(CrashLogOSX::DesyncTag{});
|
||||
log.MakeDesyncCrashLog(log_in, log_out);
|
||||
log.MakeDesyncCrashLog(log_in, log_out, info);
|
||||
}
|
||||
|
@@ -620,8 +620,8 @@ static void CDECL HandleCrash(int signum)
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void CrashLog::DesyncCrashLog(const std::string *log_in, std::string *log_out)
|
||||
/* static */ void CrashLog::DesyncCrashLog(const std::string *log_in, std::string *log_out, const DesyncExtraInfo &info)
|
||||
{
|
||||
CrashLogUnix log(CrashLogUnix::DesyncTag{});
|
||||
log.MakeDesyncCrashLog(log_in, log_out);
|
||||
log.MakeDesyncCrashLog(log_in, log_out, info);
|
||||
}
|
||||
|
@@ -612,10 +612,10 @@ static void CDECL CustomAbort(int signal)
|
||||
SetUnhandledExceptionFilter(ExceptionHandler);
|
||||
}
|
||||
|
||||
/* static */ void CrashLog::DesyncCrashLog(const std::string *log_in, std::string *log_out)
|
||||
/* static */ void CrashLog::DesyncCrashLog(const std::string *log_in, std::string *log_out, const DesyncExtraInfo &info)
|
||||
{
|
||||
CrashLogWindows log(nullptr);
|
||||
log.MakeDesyncCrashLog(log_in, log_out);
|
||||
log.MakeDesyncCrashLog(log_in, log_out, info);
|
||||
}
|
||||
|
||||
/* The crash log GUI */
|
||||
|
@@ -39,6 +39,7 @@
|
||||
#include "framerate_type.h"
|
||||
#include "scope_info.h"
|
||||
#include "string_func.h"
|
||||
#include "core/checksum_func.hpp"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
@@ -1067,6 +1068,7 @@ static Trackdir RoadFindPathToDest(RoadVehicle *v, TileIndex tile, DiagDirection
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
UpdateStateChecksum((((uint64) v->index) << 32) | (path_found << 16) | best_track);
|
||||
v->HandlePathfindingResult(path_found);
|
||||
|
||||
found_best_track:;
|
||||
@@ -1746,6 +1748,8 @@ Money RoadVehicle::GetRunningCost() const
|
||||
|
||||
bool RoadVehicle::Tick()
|
||||
{
|
||||
UpdateStateChecksum((((uint64) this->x_pos) << 32) | this->y_pos);
|
||||
UpdateStateChecksum((((uint64) this->state) << 32) | this->frame);
|
||||
if (this->IsFrontEngine()) {
|
||||
if (!(this->IsRoadVehicleStopped())) this->running_ticks++;
|
||||
return RoadVehController(this);
|
||||
|
@@ -108,6 +108,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
|
||||
{ XSLFI_GAME_EVENTS, XSCF_NULL, 1, 1, "game_events", nullptr, nullptr, nullptr },
|
||||
{ XSLFI_ROAD_LAYOUT_CHANGE_CTR, XSCF_NULL, 1, 1, "road_layout_change_ctr", nullptr, nullptr, nullptr },
|
||||
{ XSLFI_TOWN_CARGO_MATRIX, XSCF_NULL, 1, 1, "town_cargo_matrix", nullptr, nullptr, nullptr },
|
||||
{ XSLFI_STATE_CHECKSUM, XSCF_NULL, 1, 1, "state_checksum", nullptr, nullptr, nullptr },
|
||||
{ XSLFI_DEBUG, XSCF_IGNORABLE_ALL, 1, 1, "debug", nullptr, nullptr, "DBGL" },
|
||||
{ XSLFI_NULL, XSCF_NULL, 0, 0, nullptr, nullptr, nullptr, nullptr },// This is the end marker
|
||||
};
|
||||
|
@@ -75,6 +75,7 @@ enum SlXvFeatureIndex {
|
||||
XSLFI_GAME_EVENTS, ///< Game event flags
|
||||
XSLFI_ROAD_LAYOUT_CHANGE_CTR, ///< Road layout change counter
|
||||
XSLFI_TOWN_CARGO_MATRIX, ///< Town cargo matrix savegame format changes
|
||||
XSLFI_STATE_CHECKSUM, ///< State checksum
|
||||
XSLFI_DEBUG, ///< Debugging info
|
||||
|
||||
XSLFI_RIFF_HEADER_60_BIT, ///< Size field in RIFF chunk header is 60 bit
|
||||
|
@@ -19,6 +19,7 @@
|
||||
#include "../core/random_func.hpp"
|
||||
#include "../fios.h"
|
||||
#include "../road_type.h"
|
||||
#include "../core/checksum_func.hpp"
|
||||
|
||||
#include "saveload.h"
|
||||
|
||||
@@ -86,6 +87,7 @@ static const SaveLoadGlobVarList _date_desc[] = {
|
||||
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_120),
|
||||
SLEG_VAR(_random.state[0], SLE_UINT32),
|
||||
SLEG_VAR(_random.state[1], SLE_UINT32),
|
||||
SLEG_CONDVAR_X(_state_checksum.state, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_STATE_CHECKSUM)),
|
||||
SLE_CONDNULL(1, SL_MIN_VERSION, SLV_10),
|
||||
SLE_CONDNULL(4, SLV_10, SLV_120),
|
||||
SLEG_VAR(_cur_company_tick_index, SLE_FILE_U8 | SLE_VAR_U32),
|
||||
@@ -114,6 +116,7 @@ static const SaveLoadGlobVarList _date_check_desc[] = {
|
||||
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_120),
|
||||
SLE_NULL(4), // _random.state[0]
|
||||
SLE_NULL(4), // _random.state[1]
|
||||
SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_STATE_CHECKSUM)), // _state_checksum.state
|
||||
SLE_CONDNULL(1, SL_MIN_VERSION, SLV_10),
|
||||
SLE_CONDNULL(4, SLV_10, SLV_120),
|
||||
SLE_NULL(1), // _cur_company_tick_index
|
||||
|
@@ -336,10 +336,9 @@ static void Load_TOWN()
|
||||
uint arr_len = t->cargo_accepted.area.w / AcceptanceMatrix::GRID * t->cargo_accepted.area.h / AcceptanceMatrix::GRID;
|
||||
t->cargo_accepted.data = MallocT<CargoTypes>(arr_len);
|
||||
SlArray(t->cargo_accepted.data, arr_len, SLE_UINT64);
|
||||
|
||||
/* Rebuild total cargo acceptance. */
|
||||
UpdateTownCargoTotal(t);
|
||||
}
|
||||
/* Rebuild total cargo acceptance. */
|
||||
UpdateTownCargoTotal(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -810,7 +810,7 @@ void IniSaveWindowSettings(IniFile *ini, const char *grpname, void *desc)
|
||||
*/
|
||||
bool SettingDesc::IsEditable(bool do_command) const
|
||||
{
|
||||
if (!do_command && !(this->save.conv & SLF_NO_NETWORK_SYNC) && _networking && !_network_server && !(this->desc.flags & SGF_PER_COMPANY)) return false;
|
||||
if (!do_command && !(this->save.conv & SLF_NO_NETWORK_SYNC) && _networking && !(_network_server || _network_settings_access) && !(this->desc.flags & SGF_PER_COMPANY)) return false;
|
||||
if ((this->desc.flags & SGF_NETWORK_ONLY) && !_networking && _game_mode != GM_MENU) return false;
|
||||
if ((this->desc.flags & SGF_NO_NETWORK) && _networking) return false;
|
||||
if ((this->desc.flags & SGF_NEWGAME_ONLY) &&
|
||||
@@ -1264,7 +1264,7 @@ static bool MaxNoAIsChange(int32 i)
|
||||
{
|
||||
if (GetGameSettings().difficulty.max_no_competitors != 0 &&
|
||||
AI::GetInfoList()->size() == 0 &&
|
||||
(!_networking || _network_server)) {
|
||||
(!_networking || (_network_server || _network_settings_access))) {
|
||||
ShowErrorMessage(STR_WARNING_NO_SUITABLE_AI, INVALID_STRING_ID, WL_CRITICAL);
|
||||
}
|
||||
|
||||
@@ -1499,6 +1499,15 @@ static bool UpdateRconPassword(int32 p1)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool UpdateSettingsPassword(int32 p1)
|
||||
{
|
||||
if (strcmp(_settings_client.network.settings_password, "*") == 0) {
|
||||
_settings_client.network.settings_password[0] = '\0';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool UpdateClientConfigValues(int32 p1)
|
||||
{
|
||||
if (_network_server) NetworkServerSendConfigUpdate();
|
||||
@@ -2116,7 +2125,7 @@ bool SetSettingValue(uint index, int32 value, bool force_newgame)
|
||||
}
|
||||
|
||||
/* send non-company-based settings over the network */
|
||||
if (!_networking || (_networking && _network_server)) {
|
||||
if (!_networking || (_networking && (_network_server || _network_settings_access))) {
|
||||
return DoCommandP(0, index, value, CMD_CHANGE_SETTING);
|
||||
}
|
||||
return false;
|
||||
@@ -2276,7 +2285,7 @@ void IConsoleSetSetting(const char *name, const char *value, bool force_newgame)
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
if (_network_server) {
|
||||
if ((_network_server || _network_settings_access)) {
|
||||
IConsoleError("This command/variable is not available during network games.");
|
||||
} else {
|
||||
IConsoleError("This command/variable is only available to a network server.");
|
||||
|
@@ -228,7 +228,7 @@ struct GameOptionsWindow : Window {
|
||||
/* You can only change the drive side if you are in the menu or ingame with
|
||||
* no vehicles present. In a networking game only the server can change it */
|
||||
extern bool RoadVehiclesAreBuilt();
|
||||
if ((_game_mode != GM_MENU && RoadVehiclesAreBuilt()) || (_networking && !_network_server)) {
|
||||
if ((_game_mode != GM_MENU && RoadVehiclesAreBuilt()) || (_networking && !(_network_server || _network_settings_access))) {
|
||||
disabled = ~(1 << this->opt->vehicle.road_side); // disable the other value
|
||||
}
|
||||
|
||||
|
@@ -303,6 +303,7 @@ struct NetworkSettings {
|
||||
char server_password[NETWORK_PASSWORD_LENGTH]; ///< password for joining this server
|
||||
char rcon_password[NETWORK_PASSWORD_LENGTH]; ///< password for rconsole (server side)
|
||||
char admin_password[NETWORK_PASSWORD_LENGTH]; ///< password for the admin network
|
||||
char settings_password[NETWORK_PASSWORD_LENGTH]; ///< password for game settings (server side)
|
||||
bool server_advertise; ///< advertise the server to the masterserver
|
||||
uint8 lan_internet; ///< search on the LAN or internet for servers
|
||||
char client_name[NETWORK_CLIENT_NAME_LENGTH]; ///< name of the player (as client)
|
||||
|
@@ -37,6 +37,7 @@
|
||||
#include "framerate_type.h"
|
||||
#include "industry.h"
|
||||
#include "industry_map.h"
|
||||
#include "core/checksum_func.hpp"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
@@ -524,6 +525,7 @@ static Track ChooseShipTrack(Ship *v, TileIndex tile, DiagDirection enterdir, Tr
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
UpdateStateChecksum((((uint64) v->index) << 32) | (path_found << 16) | track);
|
||||
|
||||
v->HandlePathfindingResult(path_found);
|
||||
return track;
|
||||
@@ -972,6 +974,7 @@ reverse_direction:
|
||||
|
||||
bool Ship::Tick()
|
||||
{
|
||||
UpdateStateChecksum((((uint64) this->x_pos) << 32) | this->y_pos);
|
||||
if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
|
||||
|
||||
ShipController(this);
|
||||
|
@@ -616,6 +616,13 @@ class NIHTown : public NIHelper {
|
||||
SetDParam(0, t->cargo_accepted_total);
|
||||
b = GetString(b, STR_JUST_CARGO_LIST, lastof(buffer));
|
||||
print(buffer);
|
||||
|
||||
seprintf(buffer, lastof(buffer), " Nearby stations: %u", (uint) t->stations_near.size());
|
||||
print(buffer);
|
||||
for (const Station *st : t->stations_near) {
|
||||
seprintf(buffer, lastof(buffer), " %u: %s", st->index, st->GetCachedName());
|
||||
print(buffer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -53,6 +53,7 @@ static bool EnableSingleVehSharedOrderGuiChanged(int32 p1);
|
||||
static bool UpdateClientName(int32 p1);
|
||||
static bool UpdateServerPassword(int32 p1);
|
||||
static bool UpdateRconPassword(int32 p1);
|
||||
static bool UpdateSettingsPassword(int32 p1);
|
||||
static bool UpdateClientConfigValues(int32 p1);
|
||||
static bool CheckSharingRail(int32 p1);
|
||||
static bool CheckSharingRoad(int32 p1);
|
||||
@@ -5093,6 +5094,15 @@ guiflags = SGF_NETWORK_ONLY
|
||||
def = nullptr
|
||||
cat = SC_BASIC
|
||||
|
||||
[SDTC_STR]
|
||||
var = network.settings_password
|
||||
type = SLE_STRB
|
||||
flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC
|
||||
guiflags = SGF_NETWORK_ONLY
|
||||
def = nullptr
|
||||
proc = UpdateSettingsPassword
|
||||
cat = SC_EXPERT
|
||||
|
||||
[SDTC_STR]
|
||||
var = network.default_company_pass
|
||||
type = SLE_STRB
|
||||
|
@@ -277,7 +277,7 @@ static CallBackFunction SelectSignTool()
|
||||
|
||||
static CallBackFunction ToolbarPauseClick(Window *w)
|
||||
{
|
||||
if (_networking && !_network_server) return CBF_NONE; // only server can pause the game
|
||||
if (_networking && !(_network_server || _network_settings_access)) return CBF_NONE; // only server can pause the game
|
||||
|
||||
if (DoCommandP(0, PM_PAUSED_NORMAL, _pause_mode == PM_UNPAUSED, CMD_PAUSE)) {
|
||||
if (_settings_client.sound.confirm) SndPlayFx(SND_15_BEEP);
|
||||
@@ -2061,7 +2061,6 @@ struct MainToolbarWindow : Window {
|
||||
|
||||
_last_started_action = CBF_NONE;
|
||||
CLRBITS(this->flags, WF_WHITE_BORDER);
|
||||
this->SetWidgetDisabledState(WID_TN_PAUSE, _networking && !_network_server); // if not server, disable pause button
|
||||
this->SetWidgetDisabledState(WID_TN_FAST_FORWARD, _networking); // if networking, disable fast-forward button
|
||||
PositionMainToolbar(this);
|
||||
DoZoomInOutWindow(ZOOM_NONE, this);
|
||||
@@ -2091,6 +2090,8 @@ struct MainToolbarWindow : Window {
|
||||
this->SetWidgetDisabledState(WID_TN_TRAMS, !CanBuildVehicleInfrastructure(VEH_ROAD, RTT_TRAM));
|
||||
this->SetWidgetDisabledState(WID_TN_AIR, !CanBuildVehicleInfrastructure(VEH_AIRCRAFT));
|
||||
|
||||
this->SetWidgetDisabledState(WID_TN_PAUSE, _networking && !(_network_server || _network_settings_access)); // if not server, disable pause button
|
||||
|
||||
this->DrawWidgets();
|
||||
}
|
||||
|
||||
|
@@ -573,7 +573,7 @@ uint32 GetWorldPopulation()
|
||||
* Remove stations from nearby station list if a town is no longer in the catchment area of each.
|
||||
* @param t Town to work on
|
||||
*/
|
||||
static void RemoveNearbyStations(Town *t)
|
||||
void RemoveNearbyStations(Town *t)
|
||||
{
|
||||
for (StationList::iterator it = t->stations_near.begin(); it != t->stations_near.end(); /* incremented inside loop */) {
|
||||
const Station *st = *it;
|
||||
@@ -1010,11 +1010,11 @@ void UpdateTownCargoes(Town *t)
|
||||
t->cargo_produced = 0;
|
||||
|
||||
const TileArea &area = t->cargo_accepted.GetArea();
|
||||
if (area.tile == INVALID_TILE) return;
|
||||
|
||||
/* Update acceptance for each grid square. */
|
||||
TILE_AREA_LOOP_STEP(tile, area, AcceptanceMatrix::GRID) {
|
||||
UpdateTownCargoesSingleGridArea(t, tile, false);
|
||||
if (area.tile != INVALID_TILE) {
|
||||
/* Update acceptance for each grid square. */
|
||||
TILE_AREA_LOOP_STEP(tile, area, AcceptanceMatrix::GRID) {
|
||||
UpdateTownCargoesSingleGridArea(t, tile, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the total acceptance. */
|
||||
|
@@ -322,9 +322,6 @@ public:
|
||||
this->flags |= WF_DISABLE_VP_SCROLL;
|
||||
NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_TV_VIEWPORT);
|
||||
nvp->InitializeViewport(this, this->town->xy, ZOOM_LVL_NEWS);
|
||||
|
||||
/* disable renaming town in network games if you are not the server */
|
||||
this->SetWidgetDisabledState(WID_TV_CHANGE_NAME, _networking && !_network_server);
|
||||
}
|
||||
|
||||
~TownViewWindow()
|
||||
@@ -341,6 +338,7 @@ public:
|
||||
{
|
||||
extern const Town *_viewport_highlight_town;
|
||||
this->SetWidgetLoweredState(WID_TV_CATCHMENT, _viewport_highlight_town == this->town);
|
||||
this->SetWidgetDisabledState(WID_TV_CHANGE_NAME, _networking && !(_network_server || _network_settings_access));
|
||||
|
||||
this->DrawWidgets();
|
||||
}
|
||||
|
@@ -870,8 +870,8 @@ void SetTraceRestrictValueDefault(TraceRestrictItem &item, TraceRestrictValueTyp
|
||||
break;
|
||||
|
||||
case TRVT_CARGO_ID:
|
||||
assert(_sorted_standard_cargo_specs_size > 0);
|
||||
SetTraceRestrictValue(item, _sorted_cargo_specs[0]->Index());
|
||||
assert(_standard_cargo_mask != 0);
|
||||
SetTraceRestrictValue(item, FindFirstBit64(_standard_cargo_mask));
|
||||
SetTraceRestrictAuxField(item, 0);
|
||||
break;
|
||||
|
||||
|
@@ -43,6 +43,7 @@
|
||||
#include "engine_func.h"
|
||||
#include "bridge_signal_map.h"
|
||||
#include "scope_info.h"
|
||||
#include "core/checksum_func.hpp"
|
||||
|
||||
#include "table/strings.h"
|
||||
#include "table/train_cmd.h"
|
||||
@@ -1549,6 +1550,7 @@ CommandCost CmdMoveRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, u
|
||||
TraceRestrictRemoveVehicleFromAllSlots(src->index);
|
||||
ClrBit(src->flags, VRF_HAVE_SLOT);
|
||||
}
|
||||
OrderBackup::ClearVehicle(src);
|
||||
}
|
||||
|
||||
/* We weren't a front engine but are becoming one. So
|
||||
@@ -2175,6 +2177,53 @@ void ReverseTrainDirection(Train *v)
|
||||
/* Clear path reservation in front if train is not stuck. */
|
||||
if (!HasBit(v->flags, VRF_TRAIN_STUCK)) FreeTrainTrackReservation(v);
|
||||
|
||||
std::vector<Train *> re_reserve_trains;
|
||||
{
|
||||
/* Temporarily clear and restore reservations to bidi tunnel/bridge entrances when reversing train inside,
|
||||
* to avoid outgoing and incoming reservations becoming merged */
|
||||
auto find_train_reservations = [&re_reserve_trains, &v](TileIndex tile) {
|
||||
TrackBits reserved = GetAcrossTunnelBridgeReservationTrackBits(tile);
|
||||
Track track;
|
||||
while ((track = RemoveFirstTrack(&reserved)) != INVALID_TRACK) {
|
||||
Train *res_train = GetTrainForReservation(tile, track);
|
||||
if (res_train != nullptr && res_train != v) {
|
||||
FreeTrainTrackReservation(res_train);
|
||||
re_reserve_trains.push_back(res_train);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (IsTunnelBridgeWithSignalSimulation(v->tile) && IsTunnelBridgeSignalSimulationBidirectional(v->tile)) {
|
||||
find_train_reservations(v->tile);
|
||||
find_train_reservations(GetOtherTunnelBridgeEnd(v->tile));
|
||||
}
|
||||
Train *last = v->Last();
|
||||
if (IsTunnelBridgeWithSignalSimulation(last->tile) && IsTunnelBridgeSignalSimulationBidirectional(last->tile)) {
|
||||
find_train_reservations(last->tile);
|
||||
find_train_reservations(GetOtherTunnelBridgeEnd(last->tile));
|
||||
}
|
||||
}
|
||||
|
||||
if ((v->track & TRACK_BIT_WORMHOLE) && IsTunnelBridgeWithSignalSimulation(v->tile)) {
|
||||
/* Clear exit tile reservation if train was on approach to exit and had reserved it */
|
||||
Axis axis = DiagDirToAxis(GetTunnelBridgeDirection(v->tile));
|
||||
DiagDirection axial_dir = DirToDiagDirAlongAxis(v->direction, axis);
|
||||
TileIndex next_tile = TileVirtXY(v->x_pos, v->y_pos) + TileOffsByDiagDir(axial_dir);
|
||||
if (next_tile == v->tile || next_tile == GetOtherTunnelBridgeEnd(v->tile)) {
|
||||
Trackdir exit_td = TrackEnterdirToTrackdir(FindFirstTrack(GetAcrossTunnelBridgeTrackBits(next_tile)), ReverseDiagDir(GetTunnelBridgeDirection(next_tile)));
|
||||
CFollowTrackRail ft(GetTileOwner(next_tile), GetRailTypeInfo(v->railtype)->compatible_railtypes);
|
||||
if (ft.Follow(next_tile, exit_td)) {
|
||||
TrackdirBits reserved = ft.m_new_td_bits & TrackBitsToTrackdirBits(GetReservedTrackbits(ft.m_new_tile));
|
||||
if (reserved == TRACKDIR_BIT_NONE) {
|
||||
UnreserveAcrossRailTunnelBridge(next_tile);
|
||||
MarkTileDirtyByTile(next_tile, ZOOM_LVL_DRAW_MAP);
|
||||
}
|
||||
} else {
|
||||
UnreserveAcrossRailTunnelBridge(next_tile);
|
||||
MarkTileDirtyByTile(next_tile, ZOOM_LVL_DRAW_MAP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if we were approaching a rail/road-crossing */
|
||||
TileIndex crossing = TrainApproachingCrossingTile(v);
|
||||
|
||||
@@ -2214,6 +2263,10 @@ void ReverseTrainDirection(Train *v)
|
||||
crossing = TrainApproachingCrossingTile(v);
|
||||
if (crossing != INVALID_TILE) MaybeBarCrossingWithSound(crossing);
|
||||
|
||||
for (uint i = 0; i < re_reserve_trains.size(); ++i) {
|
||||
TryPathReserve(re_reserve_trains[i], true);
|
||||
}
|
||||
|
||||
/* If we are inside a depot after reversing, don't bother with path reserving. */
|
||||
if (v->track == TRACK_BIT_DEPOT) {
|
||||
/* Can't be stuck here as inside a depot is always a safe tile. */
|
||||
@@ -2724,6 +2777,9 @@ void FreeTrainTrackReservation(const Train *v, TileIndex origin, Trackdir orig_t
|
||||
/* Don't free reservation if it's not ours. */
|
||||
if (TracksOverlap(GetReservedTrackbits(tile) | TrackToTrackBits(TrackdirToTrack(td)))) return;
|
||||
|
||||
/* Do not attempt to unreserve out of a signalled tunnel/bridge entrance, as this would unreserve the reservations of another train coming in */
|
||||
if (IsTunnelBridgeWithSignalSimulation(tile) && TrackdirExitsTunnelBridge(tile, td) && IsTunnelBridgeSignalSimulationEntranceOnly(tile)) return;
|
||||
|
||||
CFollowTrackRail ft(v, GetRailTypeInfo(v->railtype)->compatible_railtypes);
|
||||
while (ft.Follow(tile, td)) {
|
||||
tile = ft.m_new_tile;
|
||||
@@ -3127,6 +3183,7 @@ static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir,
|
||||
TileIndex new_tile = res_dest.tile;
|
||||
|
||||
Track next_track = DoTrainPathfind(v, new_tile, dest_enterdir, tracks, path_found, do_track_reservation, &res_dest);
|
||||
UpdateStateChecksum((((uint64) v->index) << 32) | (path_found << 16) | next_track);
|
||||
if (new_tile == tile) best_track = next_track;
|
||||
v->HandlePathfindingResult(path_found);
|
||||
}
|
||||
@@ -4217,7 +4274,10 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
|
||||
}
|
||||
if (v->Next() == nullptr) {
|
||||
if (v->tunnel_bridge_signal_num > 0 && distance == (TILE_SIZE * _settings_game.construction.simulated_wormhole_signals) - TILE_SIZE) HandleSignalBehindTrain(v, v->tunnel_bridge_signal_num - 2);
|
||||
if (old_tile == v->tile) {
|
||||
DiagDirection tunnel_bridge_dir = GetTunnelBridgeDirection(v->tile);
|
||||
Axis axis = DiagDirToAxis(tunnel_bridge_dir);
|
||||
DiagDirection axial_dir = DirToDiagDirAlongAxis(v->direction, axis);
|
||||
if (old_tile == ((axial_dir == tunnel_bridge_dir) ? v->tile : GetOtherTunnelBridgeEnd(v->tile))) {
|
||||
/* We left ramp into wormhole. */
|
||||
v->x_pos = gp.x;
|
||||
v->y_pos = gp.y;
|
||||
@@ -4971,6 +5031,7 @@ Money Train::GetRunningCost() const
|
||||
*/
|
||||
bool Train::Tick()
|
||||
{
|
||||
UpdateStateChecksum((((uint64) this->x_pos) << 32) | (this->y_pos << 16) | this->track );
|
||||
if (this->IsFrontEngine()) {
|
||||
if (!(this->vehstatus & VS_STOPPED) || this->cur_speed > 0) this->running_ticks++;
|
||||
|
||||
|
@@ -372,6 +372,18 @@ static inline bool IsTunnelBridgeSignalSimulationEntrance(TileIndex t)
|
||||
return HasBit(_m[t].m5, 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a tunnel/bridge entrance tile with signal only?
|
||||
* @param t the tile that might be a tunnel/bridge.
|
||||
* @pre IsTileType(t, MP_TUNNELBRIDGE)
|
||||
* @return true if and only if this tile is a tunnel/bridge entrance only.
|
||||
*/
|
||||
static inline bool IsTunnelBridgeSignalSimulationEntranceOnly(TileIndex t)
|
||||
{
|
||||
assert_tile(IsTileType(t, MP_TUNNELBRIDGE), t);
|
||||
return HasBit(_m[t].m5, 5) && !HasBit(_m[t].m5, 6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a tunnel/bridge exit?
|
||||
* @param t the tile that might be a tunnel/bridge.
|
||||
|
Reference in New Issue
Block a user