diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp index 4d3b920909..72d3199aa3 100644 --- a/src/build_vehicle_gui.cpp +++ b/src/build_vehicle_gui.cpp @@ -1093,9 +1093,6 @@ struct BuildVehicleWindow : Window { this->GetWidget(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(WID_BV_LIST); widget->tool_tip = STR_BUY_VEHICLE_TRAIN_LIST_TOOLTIP + type; @@ -1564,6 +1561,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()) { diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index dfbf88345c..dfbcf41fa2 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -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 '"); + 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 %+"); diff --git a/src/network/core/tcp_game.cpp b/src/network/core/tcp_game.cpp index ef3a382219..fe7bce467c 100644 --- a/src/network/core/tcp_game.cpp +++ b/src/network/core/tcp_game.cpp @@ -79,6 +79,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); @@ -166,6 +168,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); } diff --git a/src/network/core/tcp_game.h b/src/network/core/tcp_game.h index 03eb5ded44..4ae96edb1c 100644 --- a/src/network/core/tcp_game.h +++ b/src/network/core/tcp_game.h @@ -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. @@ -262,6 +264,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. diff --git a/src/network/network.cpp b/src/network/network.cpp index 43e1922a3d..dbd45e4d0a 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -57,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. diff --git a/src/network/network.h b/src/network/network.h index 9f8e3b790b..1b9bb741cb 100644 --- a/src/network/network.h +++ b/src/network/network.h @@ -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 */ diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 67d33abb59..a031fb2f37 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -159,6 +159,7 @@ ClientNetworkGameSocketHandler::~ClientNetworkGameSocketHandler() { assert(ClientNetworkGameSocketHandler::my_client == this); ClientNetworkGameSocketHandler::my_client = nullptr; + _network_settings_access = false; delete this->savegame; } @@ -331,6 +332,8 @@ 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. */ @@ -415,6 +418,18 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendCompanyPassword(const char 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; +} + /** Request the map from the server. */ NetworkRecvStatus ClientNetworkGameSocketHandler::SendGetMap() { @@ -821,6 +836,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_WELCOME(Packet _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 */ @@ -1209,6 +1225,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? */ @@ -1263,6 +1290,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. diff --git a/src/network/network_client.h b/src/network/network_client.h index 25ee42d426..c173408619 100644 --- a/src/network/network_client.h +++ b/src/network/network_client.h @@ -49,6 +49,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; @@ -92,6 +93,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); diff --git a/src/network/network_func.h b/src/network/network_func.h index 781a5ce7d6..04b8178a60 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -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); diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 1297a9c536..e1e8cd750a 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -218,6 +218,7 @@ ServerNetworkGameSocketHandler::ServerNetworkGameSocketHandler(SOCKET s) : Netwo 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 @@ -537,6 +538,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendWelcome() 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); @@ -860,6 +862,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 @@ -1014,6 +1025,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; @@ -1108,12 +1142,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); } @@ -1123,7 +1157,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); diff --git a/src/network/network_server.h b/src/network/network_server.h index 2ea4441bd4..f73232f9a6 100644 --- a/src/network/network_server.h +++ b/src/network/network_server.h @@ -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; @@ -74,6 +75,8 @@ public: 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) @@ -104,6 +107,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); diff --git a/src/settings.cpp b/src/settings.cpp index 9d842f6568..9a9561154f 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -806,7 +806,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) && @@ -1260,7 +1260,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); } @@ -1495,6 +1495,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(); @@ -2112,7 +2121,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; @@ -2272,7 +2281,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."); diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 9435a1b5c3..765952d28d 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -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 } diff --git a/src/settings_type.h b/src/settings_type.h index 7f4dfdd0df..f8181a461f 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -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) diff --git a/src/table/settings.ini b/src/table/settings.ini index 5dcd56d638..e5b49c429d 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -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); @@ -5077,6 +5078,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 diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index 457962388e..1eb818f079 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -275,7 +275,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); @@ -2013,7 +2013,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); @@ -2041,6 +2040,8 @@ struct MainToolbarWindow : Window { this->SetWidgetDisabledState(WID_TN_RAILS, !CanBuildVehicleInfrastructure(VEH_TRAIN)); 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(); } diff --git a/src/town_gui.cpp b/src/town_gui.cpp index 557e7b36dc..33b6bb0730 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -322,9 +322,6 @@ public: this->flags |= WF_DISABLE_VP_SCROLL; NWidgetViewport *nvp = this->GetWidget(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(); }