mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-31 02:00:28 +01:00
multiplayer stage 1: --host/--connect startup, client without a local scenario
Networking used to be reachable only through the eu07.ini network.server / network.client entries, and init_modes() refused to start at all unless a scenery file was already known. That made it impossible to join a session without knowing (and typing) the host's scenario up front, even though SERVER_HELLO already carries the scenario name. - add --host [address:port] and --connect address:port to the command line (default port 7420, --host defaults to 0.0.0.0), plus --scenario/--vehicle aliases and -h/--help; the existing ini settings keep working - drop the init_modes() restriction: a client goes straight to the scenario loader and waits there for the handshake, a host without -s still gets the launcher and simply refuses joins until a scenario is up - the loader screen reports the network state instead of sitting on a blank progress bar - a client that did not ask for a vehicle with -v starts as an observer rather than failing to find the default EU07-424; the lobby of stage 2 will take over the choice - handshake carries build identification and the server answers an impossible join with SERVER_REJECT and a readable reason instead of dropping the socket silently; protocol version bumped to 3 - deserialize_message() no longer dereferences a null message on an unknown message type
This commit is contained in:
@@ -1105,6 +1105,49 @@ void eu07_application::init_files()
|
|||||||
}
|
}
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// port used when the user did not spell one out in --host / --connect
|
||||||
|
uint32_t const EU07_DEFAULT_NETWORK_PORT = 7420;
|
||||||
|
|
||||||
|
// completes a user supplied endpoint into the "address:port" form expected by the tcp backend.
|
||||||
|
// note: only the plain ipv4/hostname form is split, anything with more colons is passed through
|
||||||
|
std::string network_endpoint(std::string const &Argument, std::string const &Defaultaddress)
|
||||||
|
{
|
||||||
|
auto address{Argument};
|
||||||
|
std::string port;
|
||||||
|
|
||||||
|
if (std::count(address.begin(), address.end(), ':') == 1)
|
||||||
|
{
|
||||||
|
auto const separator{address.find(':')};
|
||||||
|
port = address.substr(separator + 1);
|
||||||
|
address = address.substr(0, separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address.empty())
|
||||||
|
address = Defaultaddress;
|
||||||
|
if (port.empty())
|
||||||
|
port = std::to_string(EU07_DEFAULT_NETWORK_PORT);
|
||||||
|
|
||||||
|
return address + ":" + port;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_usage(std::string const &Executable)
|
||||||
|
{
|
||||||
|
std::cout
|
||||||
|
<< "usage: " << Executable << " [options]\n"
|
||||||
|
<< " -s, --scenario <path> scenario file to load\n"
|
||||||
|
<< " -v, --vehicle <name> vehicle to start in\n"
|
||||||
|
<< " --host [address:port] host a multiplayer session (default 0.0.0.0:"
|
||||||
|
<< EU07_DEFAULT_NETWORK_PORT << ")\n"
|
||||||
|
<< " --connect <address:port> join a multiplayer session (default port "
|
||||||
|
<< EU07_DEFAULT_NETWORK_PORT << ")\n"
|
||||||
|
<< " -h, --help this message"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
int eu07_application::init_settings(int Argc, char *Argv[])
|
int eu07_application::init_settings(int Argc, char *Argv[])
|
||||||
{
|
{
|
||||||
Global.asVersion = VERSION_INFO;
|
Global.asVersion = VERSION_INFO;
|
||||||
@@ -1126,24 +1169,48 @@ int eu07_application::init_settings(int Argc, char *Argv[])
|
|||||||
|
|
||||||
std::string token{Argv[i]};
|
std::string token{Argv[i]};
|
||||||
|
|
||||||
if (token == "-s")
|
if (token == "-s" || token == "--scenario")
|
||||||
{
|
{
|
||||||
if (i + 1 < Argc)
|
if (i + 1 < Argc)
|
||||||
{
|
{
|
||||||
Global.SceneryFile = ToLower(Argv[++i]);
|
Global.SceneryFile = ToLower(Argv[++i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (token == "-v")
|
else if (token == "-v" || token == "--vehicle")
|
||||||
{
|
{
|
||||||
if (i + 1 < Argc)
|
if (i + 1 < Argc)
|
||||||
{
|
{
|
||||||
Global.local_start_vehicle = ToLower(Argv[++i]);
|
Global.local_start_vehicle = ToLower(Argv[++i]);
|
||||||
|
Global.local_start_vehicle_override = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (token == "--host")
|
||||||
|
{
|
||||||
|
// the address is optional, so that a bare --host just listens on every interface
|
||||||
|
std::string endpoint{std::string("0.0.0.0:") + std::to_string(EU07_DEFAULT_NETWORK_PORT)};
|
||||||
|
if (i + 1 < Argc && Argv[i + 1][0] != '-')
|
||||||
|
{
|
||||||
|
endpoint = network_endpoint(Argv[++i], "0.0.0.0");
|
||||||
|
}
|
||||||
|
Global.network_servers.emplace_back("tcp", endpoint);
|
||||||
|
}
|
||||||
|
else if (token == "--connect")
|
||||||
|
{
|
||||||
|
if (i + 1 >= Argc)
|
||||||
|
{
|
||||||
|
std::cout << "--connect requires a server address" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
Global.network_client.emplace("tcp", network_endpoint(Argv[++i], "127.0.0.1"));
|
||||||
|
}
|
||||||
|
else if (token == "-h" || token == "--help")
|
||||||
|
{
|
||||||
|
print_usage(Argv[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::cout << "usage: " << std::string(Argv[0]) << " [-s sceneryfilepath]"
|
print_usage(Argv[0]);
|
||||||
<< " [-v vehiclename]" << std::endl;
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1423,17 +1490,23 @@ int eu07_application::init_modes()
|
|||||||
{
|
{
|
||||||
Global.local_random_engine.seed(std::random_device{}());
|
Global.local_random_engine.seed(std::random_device{}());
|
||||||
|
|
||||||
if ((!Global.network_servers.empty() || Global.network_client) && Global.SceneryFile.empty())
|
|
||||||
{
|
|
||||||
ErrorLog("launcher mode is currently not supported in network mode");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// activate the default mode
|
// activate the default mode
|
||||||
if (Global.SceneryFile.empty())
|
if (Global.network_client)
|
||||||
push_mode(mode::launcher);
|
{
|
||||||
else
|
// a multiplayer client learns the scenario name from the server handshake,
|
||||||
|
// so it goes straight to the loader and waits there for Global.ready_to_load
|
||||||
push_mode(mode::scenarioloader);
|
push_mode(mode::scenarioloader);
|
||||||
|
}
|
||||||
|
else if (Global.SceneryFile.empty())
|
||||||
|
{
|
||||||
|
// no scenario given: let the user pick one (a listening server simply
|
||||||
|
// refuses joins until the scenario is up)
|
||||||
|
push_mode(mode::launcher);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
push_mode(mode::scenarioloader);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1449,11 +1522,22 @@ bool eu07_application::init_network()
|
|||||||
for (auto const &pair : Global.network_servers)
|
for (auto const &pair : Global.network_servers)
|
||||||
{
|
{
|
||||||
// create all servers
|
// create all servers
|
||||||
|
WriteLog("net: hosting session on " + pair.second + " (" + pair.first + ")", logtype::net);
|
||||||
m_network->create_server(pair.first, pair.second);
|
m_network->create_server(pair.first, pair.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Global.network_client)
|
if (Global.network_client)
|
||||||
{
|
{
|
||||||
|
if (!Global.local_start_vehicle_override)
|
||||||
|
{
|
||||||
|
// in a multiplayer session the vehicle is assigned by the server;
|
||||||
|
// until the lobby exists the client simply starts out as an observer
|
||||||
|
Global.local_start_vehicle = "ghostview";
|
||||||
|
}
|
||||||
|
|
||||||
|
Global.network_status = "Connecting to " + Global.network_client->second + "...";
|
||||||
|
WriteLog("net: connecting to " + Global.network_client->second + " (" + Global.network_client->first + ")", logtype::net);
|
||||||
|
|
||||||
// create client
|
// create client
|
||||||
m_network->connect(Global.network_client->first, Global.network_client->second);
|
m_network->connect(Global.network_client->first, Global.network_client->second);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,9 +32,14 @@ bool scenarioloader_mode::init() {
|
|||||||
|
|
||||||
// mode-specific update of simulation data. returns: false on error, true otherwise
|
// mode-specific update of simulation data. returns: false on error, true otherwise
|
||||||
bool scenarioloader_mode::update() {
|
bool scenarioloader_mode::update() {
|
||||||
if (!Global.ready_to_load)
|
if (!Global.ready_to_load) {
|
||||||
// waiting for network connection
|
// waiting for the network handshake to tell us which scenario to load
|
||||||
|
m_userinterface->set_progress(
|
||||||
|
Global.network_status.empty()
|
||||||
|
? STR("Connecting to server")
|
||||||
|
: Global.network_status);
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!state) {
|
if (!state) {
|
||||||
WriteLog("using simulation seed: " + std::to_string(Global.random_seed), logtype::generic);
|
WriteLog("using simulation seed: " + std::to_string(Global.random_seed), logtype::generic);
|
||||||
@@ -76,8 +81,15 @@ void scenarioloader_mode::enter() {
|
|||||||
|
|
||||||
simulation::is_ready = false;
|
simulation::is_ready = false;
|
||||||
|
|
||||||
Application.set_title( Global.AppName + " (" + Global.SceneryFile + ")" );
|
if( Global.SceneryFile.empty() ) {
|
||||||
m_userinterface->set_progress(STR("Loading scenery"));
|
// multiplayer client: the scenario is not known until the server answers
|
||||||
|
Application.set_title( Global.AppName );
|
||||||
|
m_userinterface->set_progress( STR( "Connecting to server" ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Application.set_title( Global.AppName + " (" + Global.SceneryFile + ")" );
|
||||||
|
m_userinterface->set_progress(STR("Loading scenery"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// maintenance method, called when the mode is deactivated
|
// maintenance method, called when the mode is deactivated
|
||||||
|
|||||||
@@ -6,12 +6,24 @@ void network::client_hello::serialize(std::ostream &stream) const
|
|||||||
{
|
{
|
||||||
sn_utils::ls_int32(stream, version);
|
sn_utils::ls_int32(stream, version);
|
||||||
sn_utils::ls_uint32(stream, start_packet);
|
sn_utils::ls_uint32(stream, start_packet);
|
||||||
|
sn_utils::s_str(stream, app_version);
|
||||||
}
|
}
|
||||||
|
|
||||||
void network::client_hello::deserialize(std::istream &stream)
|
void network::client_hello::deserialize(std::istream &stream)
|
||||||
{
|
{
|
||||||
version = sn_utils::ld_int32(stream);
|
version = sn_utils::ld_int32(stream);
|
||||||
start_packet = sn_utils::ld_uint32(stream);
|
start_packet = sn_utils::ld_uint32(stream);
|
||||||
|
app_version = sn_utils::d_str(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void network::server_reject::serialize(std::ostream &stream) const
|
||||||
|
{
|
||||||
|
sn_utils::s_str(stream, reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
void network::server_reject::deserialize(std::istream &stream)
|
||||||
|
{
|
||||||
|
reason = sn_utils::d_str(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
void network::server_hello::serialize(std::ostream &stream) const
|
void network::server_hello::serialize(std::ostream &stream) const
|
||||||
@@ -20,6 +32,7 @@ void network::server_hello::serialize(std::ostream &stream) const
|
|||||||
sn_utils::ls_int64(stream, timestamp);
|
sn_utils::ls_int64(stream, timestamp);
|
||||||
sn_utils::ls_int64(stream, config);
|
sn_utils::ls_int64(stream, config);
|
||||||
sn_utils::s_str(stream, scenario);
|
sn_utils::s_str(stream, scenario);
|
||||||
|
sn_utils::s_str(stream, app_version);
|
||||||
}
|
}
|
||||||
|
|
||||||
void network::server_hello::deserialize(std::istream &stream)
|
void network::server_hello::deserialize(std::istream &stream)
|
||||||
@@ -28,6 +41,7 @@ void network::server_hello::deserialize(std::istream &stream)
|
|||||||
timestamp = sn_utils::ld_int64(stream);
|
timestamp = sn_utils::ld_int64(stream);
|
||||||
config = sn_utils::ld_int64(stream);
|
config = sn_utils::ld_int64(stream);
|
||||||
scenario = sn_utils::d_str(stream);
|
scenario = sn_utils::d_str(stream);
|
||||||
|
app_version = sn_utils::d_str(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ::network::request_command::serialize(std::ostream &stream) const
|
void ::network::request_command::serialize(std::ostream &stream) const
|
||||||
@@ -115,6 +129,13 @@ std::shared_ptr<network::message> network::deserialize_message(std::istream &str
|
|||||||
msg = std::make_shared<frame_info>();
|
msg = std::make_shared<frame_info>();
|
||||||
else if (type == message::REQUEST_COMMAND)
|
else if (type == message::REQUEST_COMMAND)
|
||||||
msg = std::make_shared<request_command>();
|
msg = std::make_shared<request_command>();
|
||||||
|
else if (type == message::SERVER_REJECT)
|
||||||
|
msg = std::make_shared<server_reject>();
|
||||||
|
|
||||||
|
if (!msg) {
|
||||||
|
// unknown message type; hand back a marker the peer handlers treat as a protocol error
|
||||||
|
return std::make_shared<message>(message::TYPE_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
msg->deserialize(stream);
|
msg->deserialize(stream);
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ struct message
|
|||||||
SERVER_HELLO,
|
SERVER_HELLO,
|
||||||
FRAME_INFO,
|
FRAME_INFO,
|
||||||
REQUEST_COMMAND,
|
REQUEST_COMMAND,
|
||||||
|
SERVER_REJECT,
|
||||||
TYPE_MAX
|
TYPE_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,6 +33,20 @@ struct client_hello : public message
|
|||||||
|
|
||||||
int32_t version;
|
int32_t version;
|
||||||
uint32_t start_packet;
|
uint32_t start_packet;
|
||||||
|
// build identification of the connecting simulator, informational only
|
||||||
|
std::string app_version;
|
||||||
|
};
|
||||||
|
|
||||||
|
// sent by the server instead of SERVER_HELLO when the client cannot join;
|
||||||
|
// carries a human readable reason so the player learns what went wrong
|
||||||
|
struct server_reject : public message
|
||||||
|
{
|
||||||
|
server_reject() : message(SERVER_REJECT) {}
|
||||||
|
|
||||||
|
std::string reason;
|
||||||
|
|
||||||
|
virtual void serialize(std::ostream &stream) const override;
|
||||||
|
virtual void deserialize(std::istream &stream) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct server_hello : public message
|
struct server_hello : public message
|
||||||
@@ -42,6 +57,8 @@ struct server_hello : public message
|
|||||||
int64_t timestamp;
|
int64_t timestamp;
|
||||||
int64_t config;
|
int64_t config;
|
||||||
std::string scenario;
|
std::string scenario;
|
||||||
|
// build identification of the server, informational only
|
||||||
|
std::string app_version;
|
||||||
|
|
||||||
virtual void serialize(std::ostream &stream) const override;
|
virtual void serialize(std::ostream &stream) const override;
|
||||||
virtual void deserialize(std::istream &stream) override;
|
virtual void deserialize(std::istream &stream) override;
|
||||||
|
|||||||
@@ -7,7 +7,9 @@
|
|||||||
#include "application/application.h"
|
#include "application/application.h"
|
||||||
#include "utilities/Globals.h"
|
#include "utilities/Globals.h"
|
||||||
|
|
||||||
std::uint32_t const EU07_NETWORK_VERSION = 2;
|
// 2 - legacy lockstep protocol
|
||||||
|
// 3 - handshake carries build identification and an explicit rejection message
|
||||||
|
std::uint32_t const EU07_NETWORK_VERSION = 3;
|
||||||
|
|
||||||
namespace network {
|
namespace network {
|
||||||
|
|
||||||
@@ -44,6 +46,7 @@ void network::connection::connected()
|
|||||||
client_hello msg;
|
client_hello msg;
|
||||||
msg.version = EU07_NETWORK_VERSION;
|
msg.version = EU07_NETWORK_VERSION;
|
||||||
msg.start_packet = packet_counter;
|
msg.start_packet = packet_counter;
|
||||||
|
msg.app_version = Global.asVersion;
|
||||||
send_message(msg);
|
send_message(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,8 +130,23 @@ void network::server::handle_message(std::shared_ptr<connection> conn, const mes
|
|||||||
if (msg.type == message::CLIENT_HELLO) {
|
if (msg.type == message::CLIENT_HELLO) {
|
||||||
const auto& cmd = dynamic_cast<const client_hello&>(msg);
|
const auto& cmd = dynamic_cast<const client_hello&>(msg);
|
||||||
|
|
||||||
if (cmd.version != EU07_NETWORK_VERSION // wrong version
|
std::string rejection;
|
||||||
|| !Global.ready_to_load) { // not ready yet
|
if (cmd.version != (int32_t)EU07_NETWORK_VERSION) {
|
||||||
|
rejection = "incompatible protocol version: server speaks "
|
||||||
|
+ std::to_string(EU07_NETWORK_VERSION)
|
||||||
|
+ ", client speaks " + std::to_string(cmd.version);
|
||||||
|
}
|
||||||
|
else if (!Global.ready_to_load || Global.SceneryFile.empty()) {
|
||||||
|
// the host has not picked a scenario yet, there is nothing to join
|
||||||
|
rejection = "the server has no scenario running yet";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rejection.empty()) {
|
||||||
|
WriteLog("net: rejecting peer: " + rejection, logtype::net);
|
||||||
|
|
||||||
|
server_reject reply;
|
||||||
|
reply.reason = rejection;
|
||||||
|
conn->send_message(reply);
|
||||||
conn->disconnect();
|
conn->disconnect();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -138,6 +156,7 @@ void network::server::handle_message(std::shared_ptr<connection> conn, const mes
|
|||||||
reply.timestamp = Global.starting_timestamp;
|
reply.timestamp = Global.starting_timestamp;
|
||||||
reply.config = 0; // TODO: pass bitfield with state of relevant setting switches
|
reply.config = 0; // TODO: pass bitfield with state of relevant setting switches
|
||||||
reply.scenario = Global.SceneryFile;
|
reply.scenario = Global.SceneryFile;
|
||||||
|
reply.app_version = Global.asVersion;
|
||||||
conn->state = connection::CATCHING_UP;
|
conn->state = connection::CATCHING_UP;
|
||||||
conn->backbuffer = backbuffer;
|
conn->backbuffer = backbuffer;
|
||||||
conn->backbuffer_pos = 0;
|
conn->backbuffer_pos = 0;
|
||||||
@@ -145,7 +164,7 @@ void network::server::handle_message(std::shared_ptr<connection> conn, const mes
|
|||||||
|
|
||||||
conn->send_message(reply);
|
conn->send_message(reply);
|
||||||
|
|
||||||
WriteLog("net: client accepted", logtype::net);
|
WriteLog("net: peer connected, build \"" + cmd.app_version + "\", scenario \"" + Global.SceneryFile + "\"", logtype::net);
|
||||||
}
|
}
|
||||||
else if (msg.type == message::REQUEST_COMMAND) {
|
else if (msg.type == message::REQUEST_COMMAND) {
|
||||||
const auto& cmd = dynamic_cast<const request_command&>(msg);
|
const auto& cmd = dynamic_cast<const request_command&>(msg);
|
||||||
@@ -163,6 +182,11 @@ void network::client::update()
|
|||||||
conn.reset();
|
conn.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Global.network_reject_reason.empty()) {
|
||||||
|
// the server explicitly refused us; retrying would only spam it
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!conn) {
|
if (!conn) {
|
||||||
if (!reconnect_delay) {
|
if (!reconnect_delay) {
|
||||||
connect();
|
connect();
|
||||||
@@ -240,6 +264,16 @@ void network::client::handle_message(std::shared_ptr<connection> conn, const mes
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg.type == message::SERVER_REJECT) {
|
||||||
|
const auto& cmd = dynamic_cast<const server_reject&>(msg);
|
||||||
|
|
||||||
|
ErrorLog("net: connection refused by the server: " + cmd.reason, logtype::net);
|
||||||
|
Global.network_reject_reason = cmd.reason;
|
||||||
|
Global.network_status = "Connection refused: " + cmd.reason;
|
||||||
|
conn->disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.type == message::SERVER_HELLO) {
|
if (msg.type == message::SERVER_HELLO) {
|
||||||
const auto& cmd = dynamic_cast<const server_hello&>(msg);
|
const auto& cmd = dynamic_cast<const server_hello&>(msg);
|
||||||
conn->state = connection::ACTIVE;
|
conn->state = connection::ACTIVE;
|
||||||
@@ -251,12 +285,16 @@ void network::client::handle_message(std::shared_ptr<connection> conn, const mes
|
|||||||
// TODO: configure simulation settings according to received cmd.config
|
// TODO: configure simulation settings according to received cmd.config
|
||||||
Global.SceneryFile = cmd.scenario;
|
Global.SceneryFile = cmd.scenario;
|
||||||
Global.ready_to_load = true;
|
Global.ready_to_load = true;
|
||||||
|
|
||||||
|
WriteLog("net: scenario handshake: \"" + cmd.scenario + "\", server build \"" + cmd.app_version + "\"", logtype::net);
|
||||||
} else if (Global.random_seed != cmd.seed) {
|
} else if (Global.random_seed != cmd.seed) {
|
||||||
ErrorLog("net: seed mismatch", logtype::net);
|
ErrorLog("net: seed mismatch", logtype::net);
|
||||||
conn->disconnect();
|
conn->disconnect();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Global.network_status.clear();
|
||||||
|
|
||||||
WriteLog("net: accept received", logtype::net);
|
WriteLog("net: accept received", logtype::net);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,9 @@ struct global_settings {
|
|||||||
std::string szDefaultExt{ szTexturesDDS };
|
std::string szDefaultExt{ szTexturesDDS };
|
||||||
std::string SceneryFile;
|
std::string SceneryFile;
|
||||||
std::string local_start_vehicle{ "EU07-424" };
|
std::string local_start_vehicle{ "EU07-424" };
|
||||||
|
// set when the starting vehicle was explicitly requested (command line -v),
|
||||||
|
// so multiplayer clients can tell an intentional choice from the built-in default
|
||||||
|
bool local_start_vehicle_override{ false };
|
||||||
int iConvertModels{ 0 }; // tworzenie plików binarnych
|
int iConvertModels{ 0 }; // tworzenie plików binarnych
|
||||||
int iConvertIndexRange{ 1000 }; // range of duplicate vertex scan
|
int iConvertIndexRange{ 1000 }; // range of duplicate vertex scan
|
||||||
bool file_binary_terrain{ true }; // enable binary terrain (de)serialization
|
bool file_binary_terrain{ true }; // enable binary terrain (de)serialization
|
||||||
@@ -347,6 +350,10 @@ struct global_settings {
|
|||||||
std::vector<std::pair<std::string, std::string>> network_servers;
|
std::vector<std::pair<std::string, std::string>> network_servers;
|
||||||
std::optional<std::pair<std::string, std::string>> network_client;
|
std::optional<std::pair<std::string, std::string>> network_client;
|
||||||
float desync = 0.0f;
|
float desync = 0.0f;
|
||||||
|
// human readable state of the network session, presented on the connecting/loading screen
|
||||||
|
std::string network_status;
|
||||||
|
// set when the server refused us; keeps the reason for the ui and stops reconnect attempts
|
||||||
|
std::string network_reject_reason;
|
||||||
|
|
||||||
std::unordered_map<int, std::string> trainset_overrides;
|
std::unordered_map<int, std::string> trainset_overrides;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user