diff --git a/src/main.cpp b/src/main.cpp index 6d8345138..211a0e4a5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1954,6 +1954,7 @@ int main(int argc, char *argv[] ) { main_loop = new MainLoop(parent_pid); has_parent_process = true; + ServerConfig::m_server_configurable = true; } else main_loop = new MainLoop(0/*parent_pid*/); diff --git a/src/network/game_setup.cpp b/src/network/game_setup.cpp index 796a6f074..72a2798bc 100644 --- a/src/network/game_setup.cpp +++ b/src/network/game_setup.cpp @@ -25,6 +25,7 @@ #include "network/network_config.hpp" #include "network/network_player_profile.hpp" #include "network/protocols/game_events_protocol.hpp" +#include "network/protocols/server_lobby.hpp" #include "network/server_config.hpp" #include "network/stk_host.hpp" #include "race/race_manager.hpp" @@ -186,7 +187,7 @@ void GameSetup::loadWorld() bool GameSetup::isGrandPrix() const { return m_extra_server_info != -1 && - ServerConfig::getLocalGameMode().second == + ServerConfig::getLocalGameModeFromConfig().second == RaceManager::MAJOR_MODE_GRAND_PRIX; } // isGrandPrix @@ -195,9 +196,11 @@ void GameSetup::addServerInfo(NetworkString* ns) { assert(NetworkConfig::get()->isServer()); ns->encodeString(m_server_name_utf8); - ns->addUInt8((uint8_t)ServerConfig::m_server_difficulty) + auto sl = LobbyProtocol::get(); + assert(sl); + ns->addUInt8((uint8_t)sl->getDifficulty()) .addUInt8((uint8_t)ServerConfig::m_server_max_players) - .addUInt8((uint8_t)ServerConfig::m_server_mode); + .addUInt8((uint8_t)sl->getGameMode()); if (hasExtraSeverInfo()) { if (isGrandPrix()) @@ -228,6 +231,7 @@ void GameSetup::addServerInfo(NetworkString* ns) ns->addUInt8(0).addFloat(0.0f); ns->encodeString16(m_message_of_today); + ns->addUInt8((uint8_t)ServerConfig::m_server_configurable); } // addServerInfo //----------------------------------------------------------------------------- diff --git a/src/network/network_string.hpp b/src/network/network_string.hpp index 92513ad26..8c656fe05 100644 --- a/src/network/network_string.hpp +++ b/src/network/network_string.hpp @@ -340,13 +340,13 @@ public: /** Returns an unsigned 8-bit integer. */ inline uint8_t getUInt8() const { - return m_buffer[m_current_offset++]; + return m_buffer.at(m_current_offset++); } // getUInt8 // ------------------------------------------------------------------------ /** Returns an unsigned 8-bit integer. */ inline int8_t getInt8() const { - return m_buffer[m_current_offset++]; + return m_buffer.at(m_current_offset++); } // getInt8 // ------------------------------------------------------------------------ /** Gets a 4 byte floating point value. */ diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index a84938ba7..07e237aa3 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -603,8 +603,7 @@ void ClientLobby::handleServerInfo(Event* event) NetworkingLobby::getInstance()->addMoreServerInfo(each_line); u_data = data.getUInt8(); - ServerConfig::m_server_mode = u_data; - auto game_mode = ServerConfig::getLocalGameMode(); + auto game_mode = ServerConfig::getLocalGameMode(u_data); race_manager->setMinorMode(game_mode.first); // We use single mode in network even it's grand prix race_manager->setMajorMode(RaceManager::MAJOR_MODE_SINGLE); @@ -660,6 +659,8 @@ void ClientLobby::handleServerInfo(Event* event) for (const core::stringw& motd : motd_line) NetworkingLobby::getInstance()->addMoreServerInfo(motd); } + bool server_config = data.getUInt8() == 1; + NetworkingLobby::getInstance()->toggleServerConfigButton(server_config); } // handleServerInfo //----------------------------------------------------------------------------- diff --git a/src/network/protocols/lobby_protocol.hpp b/src/network/protocols/lobby_protocol.hpp index bcd96f371..2172c53f1 100644 --- a/src/network/protocols/lobby_protocol.hpp +++ b/src/network/protocols/lobby_protocol.hpp @@ -62,7 +62,8 @@ public: LE_KICK_HOST, LE_CHANGE_TEAM, LE_BAD_TEAM, - LE_BAD_CONNECTION + LE_BAD_CONNECTION, + LE_CONFIG_SERVER }; enum RejectReason : uint8_t diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 6439135f2..84f4215ef 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -138,6 +138,8 @@ ServerLobby::ServerLobby() : LobbyProtocol(NULL) m_result_ns->setSynchronous(true); m_waiting_for_reset = false; m_server_id_online.store(0); + m_difficulty.store(ServerConfig::m_server_difficulty); + m_game_mode.store(ServerConfig::m_server_mode); } // ServerLobby //----------------------------------------------------------------------------- @@ -156,32 +158,14 @@ ServerLobby::~ServerLobby() } // ~ServerLobby //----------------------------------------------------------------------------- -void ServerLobby::setup() +void ServerLobby::updateTracksForMode() { - LobbyProtocol::setup(); - auto players = m_game_setup->getConnectedPlayers(); - if (m_game_setup->isGrandPrix() && !m_game_setup->isGrandPrixStarted()) - { - for (auto player : players) - player->resetGrandPrixData(); - } - if (!m_game_setup->isGrandPrix() || !m_game_setup->isGrandPrixStarted()) - { - for (auto player : players) - player->setKartName(""); - } - - StateManager::get()->resetActivePlayers(); - // We use maximum 16bit unsigned limit - auto all_k = kart_properties_manager->getAllAvailableKarts(); auto all_t = track_manager->getAllTrackIdentifiers(); - if (all_k.size() >= 65536) - all_k.resize(65535); if (all_t.size() >= 65536) all_t.resize(65535); - m_available_kts.first = { all_k.begin(), all_k.end() }; m_available_kts.second = { all_t.begin(), all_t.end() }; - RaceManager::MinorRaceModeType m = ServerConfig::getLocalGameMode().first; + RaceManager::MinorRaceModeType m = + ServerConfig::getLocalGameMode(m_game_mode.load()).first; switch (m) { case RaceManager::MINOR_MODE_NORMAL_RACE: @@ -250,6 +234,32 @@ void ServerLobby::setup() break; } +} // updateTracksForMode + +//----------------------------------------------------------------------------- +void ServerLobby::setup() +{ + LobbyProtocol::setup(); + auto players = m_game_setup->getConnectedPlayers(); + if (m_game_setup->isGrandPrix() && !m_game_setup->isGrandPrixStarted()) + { + for (auto player : players) + player->resetGrandPrixData(); + } + if (!m_game_setup->isGrandPrix() || !m_game_setup->isGrandPrixStarted()) + { + for (auto player : players) + player->setKartName(""); + } + + StateManager::get()->resetActivePlayers(); + // We use maximum 16bit unsigned limit + auto all_k = kart_properties_manager->getAllAvailableKarts(); + if (all_k.size() >= 65536) + all_k.resize(65535); + m_available_kts.first = { all_k.begin(), all_k.end() }; + updateTracksForMode(); + m_server_has_loaded_world.store(false); // Initialise the data structures to detect if all clients and @@ -366,6 +376,7 @@ bool ServerLobby::notifyEventAsynchronous(Event* event) case LE_CHANGE_TEAM: changeTeam(event); break; case LE_REQUEST_BEGIN: startSelection(event); break; case LE_CHAT: handleChat(event); break; + case LE_CONFIG_SERVER: handleServerConfiguration(event); break; default: break; } // switch } // if (event->getType() == EVENT_TYPE_MESSAGE) @@ -554,7 +565,8 @@ void ServerLobby::asynchronousUpdate() .addUInt32(player->getOnlineId()) .addUInt8(player->getPerPlayerDifficulty()) .addUInt8(player->getLocalPlayerId()) - .addUInt8(player->getTeam()); + .addUInt8(race_manager->teamEnabled() ? + player->getTeam() : KART_TEAM_NONE); if (player->getKartName().empty()) { RandomGenerator rg; @@ -866,7 +878,7 @@ void ServerLobby::startSelection(const Event *event) if (m_state != WAITING_FOR_START_GAME) { Log::warn("ServerLobby", - "Received startSelection while being in state %d", + "Received startSelection while being in state %d.", m_state.load()); return; } @@ -1540,7 +1552,7 @@ void ServerLobby::connectionRequested(Event* event) peer->sendPacket(message, true/*reliable*/, false/*encrypted*/); peer->reset(); delete message; - Log::verbose("ServerLobby", "Player has incompatible karts / tracks"); + Log::verbose("ServerLobby", "Player has incompatible karts / tracks."); return; } @@ -1685,8 +1697,7 @@ void ServerLobby::handleUnencryptedConnection(std::shared_ptr peer, (peer, i == 0 && !online_name.empty() ? online_name : name, peer->getHostId(), default_kart_color, i == 0 ? online_id : 0, per_player_difficulty, (uint8_t)i, KART_TEAM_NONE); - if (ServerConfig::m_team_choosing && - race_manager->teamEnabled()) + if (ServerConfig::m_team_choosing) { KartTeam cur_team = KART_TEAM_NONE; if (red_blue.first > red_blue.second) @@ -2600,3 +2611,93 @@ float ServerLobby::getStartupBoostOrPenaltyForKart(uint32_t ping, k->setStartupBoost(f); return f; } // getStartupBoostOrPenaltyForKart + +//----------------------------------------------------------------------------- +/*! \brief Called when the server owner request to change game mode or + * difficulty. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 2 + * ----------------------------------------------- + * Size | 1 | 1 | 1 | + * Data | difficulty | game mode | soccer goal target | + * ----------------------------------------------- + */ +void ServerLobby::handleServerConfiguration(Event* event) +{ + if (m_state != WAITING_FOR_START_GAME) + { + Log::warn("ServerLobby", + "Received handleServerConfiguration while being in state %d.", + m_state.load()); + return; + } + if (!ServerConfig::m_server_configurable) + { + Log::warn("ServerLobby", "server-configurable is not enabled."); + return; + } + if (event->getPeerSP() != m_server_owner.lock()) + { + Log::warn("ServerLobby", + "Client %d is not authorised to config server.", + event->getPeer()->getHostId()); + return; + } + NetworkString& data = event->data(); + uint8_t new_difficulty = data.getUInt8(); + uint8_t new_game_mode = data.getUInt8(); + bool new_soccer_goal_target = data.getUInt8() == 1; + auto modes = ServerConfig::getLocalGameMode(new_game_mode); + if (modes.second == RaceManager::MAJOR_MODE_GRAND_PRIX) + { + Log::warn("ServerLobby", "Grand prix is used for new mode."); + return; + } + + race_manager->setMinorMode(modes.first); + race_manager->setMajorMode(modes.second); + race_manager->setDifficulty(RaceManager::Difficulty(new_difficulty)); + if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER) + m_game_setup->setSoccerGoalTarget(new_soccer_goal_target); + m_difficulty.store(new_difficulty); + m_game_mode.store(new_game_mode); + updateTracksForMode(); + + auto peers = STKHost::get()->getPeers(); + for (auto& peer : peers) + { + auto assets = peer->getClientAssets(); + if (!peer->isValidated() || assets.second.empty()) + continue; + std::set tracks_erase; + for (const std::string& server_track : m_available_kts.second) + { + if (assets.second.find(server_track) == assets.second.end()) + { + tracks_erase.insert(server_track); + } + } + if (tracks_erase.size() == m_available_kts.second.size()) + { + NetworkString *message = getNetworkString(2); + message->setSynchronous(true); + message->addUInt8(LE_CONNECTION_REFUSED) + .addUInt8(RR_INCOMPATIBLE_DATA); + peer->cleanPlayerProfiles(); + peer->sendPacket(message, true/*reliable*/); + peer->reset(); + delete message; + Log::verbose("ServerLobby", + "Player has incompatible tracks for new game mode."); + } + } + NetworkString* server_info = getNetworkString(); + server_info->setSynchronous(true); + server_info->addUInt8(LE_SERVER_INFO); + m_game_setup->addServerInfo(server_info); + sendMessageToPeers(server_info); + delete server_info; + updatePlayerList(); +} // handleServerConfiguration diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 44b6486ca..b2f131204 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -153,12 +153,16 @@ private: std::atomic m_waiting_players_counts; + std::atomic m_server_id_online; + + std::atomic m_difficulty; + + std::atomic m_game_mode; + std::atomic m_last_success_poll_time; uint64_t m_server_started_at, m_server_delay; - std::atomic m_server_id_online; - bool m_registered_for_once_only; bool m_save_server_config; @@ -180,6 +184,8 @@ private: void createServerIdFile(); void updatePlayerList(bool update_when_reset_server = false); void updateServerOwner(); + void handleServerConfiguration(Event* event); + void updateTracksForMode(); bool checkPeersReady() const { bool all_ready = true; @@ -284,6 +290,8 @@ public: bool allowJoinedPlayersWaiting() const; void setSaveServerConfig(bool val) { m_save_server_config = val; } float getStartupBoostOrPenaltyForKart(uint32_t ping, unsigned kart_id); + int getDifficulty() const { return m_difficulty.load(); } + int getGameMode() const { return m_game_mode.load(); } }; // class ServerLobby #endif // SERVER_LOBBY_HPP diff --git a/src/network/server_config.cpp b/src/network/server_config.cpp index b87984698..dc6113f93 100644 --- a/src/network/server_config.cpp +++ b/src/network/server_config.cpp @@ -178,9 +178,9 @@ void writeServerConfigToDisk() // ---------------------------------------------------------------------------- /** Returns the minor and majar game mode from server database id. */ std::pair - getLocalGameMode() + getLocalGameMode(int mode) { - switch (m_server_mode) + switch (mode) { case 0: return { RaceManager::MINOR_MODE_NORMAL_RACE, @@ -217,6 +217,13 @@ std::pair } // getLocalGameMode +// ---------------------------------------------------------------------------- +std::pair + getLocalGameModeFromConfig() +{ + return getLocalGameMode(m_server_mode); +} // getLocalGameModeFromConfig + // ---------------------------------------------------------------------------- core::stringw getModeName(unsigned id) { @@ -259,7 +266,7 @@ void loadServerLobbyFromConfig() if (m_official_tracks_threshold > 1.0f) m_official_tracks_threshold = 1.0f; - auto modes = getLocalGameMode(); + auto modes = getLocalGameModeFromConfig(); race_manager->setMinorMode(modes.first); race_manager->setMajorMode(modes.second); unsigned difficulty = m_server_difficulty; @@ -277,7 +284,10 @@ void loadServerLobbyFromConfig() if (m_min_start_game_players > m_server_max_players) m_min_start_game_players = 1; m_team_choosing = false; + m_server_configurable = false; } + if (modes.second == RaceManager::MAJOR_MODE_GRAND_PRIX) + m_server_configurable = false; const bool is_soccer = race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER; diff --git a/src/network/server_config.hpp b/src/network/server_config.hpp index d4e9073de..e622a32b1 100644 --- a/src/network/server_config.hpp +++ b/src/network/server_config.hpp @@ -221,6 +221,13 @@ namespace ServerConfig "validating-player, auto-end, strict-player and owner-less will be " "turned on.")); + SERVER_CFG_PREFIX BoolServerConfigParam m_server_configurable + SERVER_CFG_DEFAULT(BoolServerConfigParam(false, "server-configurable", + "If true, the server owner can config the game mode and difficulty in " + "the GUI of lobby. This option cannot be used with owner-less or " + "grand prix server, and will be automatically turned on if the server " + "was created using the in-game GUI.")); + SERVER_CFG_PREFIX FloatServerConfigParam m_flag_return_timemout SERVER_CFG_DEFAULT(FloatServerConfigParam(20.0f, "flag-return-timemout", "Time in seconds when a flag is dropped a by player in CTF " @@ -315,7 +322,10 @@ namespace ServerConfig void writeServerConfigToDisk(); // ------------------------------------------------------------------------ std::pair - getLocalGameMode(); + getLocalGameModeFromConfig(); + // ------------------------------------------------------------------------ + std::pair + getLocalGameMode(int mode); // ------------------------------------------------------------------------ core::stringw getModeName(unsigned id); // ------------------------------------------------------------------------ diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index 17e41f05e..681e81dde 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -1047,8 +1047,8 @@ void STKHost::handleDirectSocketRequest(Network* direct_socket, s.addUInt8((uint8_t)(sl->getGameSetup()->getPlayerCount() + sl->getWaitingPlayersCount())); s.addUInt16(m_private_port); - s.addUInt8((uint8_t)ServerConfig::m_server_difficulty); - s.addUInt8((uint8_t)ServerConfig::m_server_mode); + s.addUInt8((uint8_t)sl->getDifficulty()); + s.addUInt8((uint8_t)sl->getGameMode()); s.addUInt8(!pw.empty()); s.addUInt8((uint8_t) (sl->getCurrentState() == ServerLobby::WAITING_FOR_START_GAME ? diff --git a/src/network/stk_peer.hpp b/src/network/stk_peer.hpp index 3ca017456..bc2f35951 100644 --- a/src/network/stk_peer.hpp +++ b/src/network/stk_peer.hpp @@ -172,6 +172,9 @@ public: } } // ------------------------------------------------------------------------ + std::pair, std::set > + getClientAssets() const { return m_available_kts; } + // ------------------------------------------------------------------------ void setPingInterval(uint32_t interval) { enet_peer_ping_interval(m_enet_peer, interval); } // ------------------------------------------------------------------------ diff --git a/src/states_screens/online/networking_lobby.cpp b/src/states_screens/online/networking_lobby.cpp index f82e9c93d..01727a489 100644 --- a/src/states_screens/online/networking_lobby.cpp +++ b/src/states_screens/online/networking_lobby.cpp @@ -93,6 +93,9 @@ void NetworkingLobby::loadedFromFile() m_start_button = getWidget("start"); assert(m_start_button!= NULL); + m_config_button = getWidget("config"); + assert(m_config_button!= NULL); + m_text_bubble = getWidget("text"); assert(m_text_bubble != NULL); @@ -142,7 +145,7 @@ void NetworkingLobby::init() { Screen::init(); - getWidget("config")->setVisible(false); + m_server_configurable = false; m_player_names.clear(); m_allow_change_team = false; m_has_auto_start_in_server = false; @@ -157,6 +160,7 @@ void NetworkingLobby::init() m_server_info_height = GUIEngine::getFont()->getDimension(L"X").Height; m_start_button->setLabel(_("Start race")); m_start_button->setVisible(false); + m_config_button->setVisible(false); m_state = LS_CONNECTING; getWidget("chat")->setVisible(false); getWidget("chat")->setActive(false); @@ -348,6 +352,9 @@ void NetworkingLobby::onUpdate(float delta) m_text_bubble->setText(total_msg, true); } + m_config_button->setVisible(STKHost::get()->isAuthorisedToControl() && + m_server_configurable); + if (STKHost::get()->isAuthorisedToControl() || (m_has_auto_start_in_server && m_cur_starting_timer != std::numeric_limits::max())) diff --git a/src/states_screens/online/networking_lobby.hpp b/src/states_screens/online/networking_lobby.hpp index 468cc1a07..c4e6b4002 100644 --- a/src/states_screens/online/networking_lobby.hpp +++ b/src/states_screens/online/networking_lobby.hpp @@ -76,13 +76,15 @@ private: int64_t m_cur_starting_timer; unsigned m_min_start_game_players; - bool m_allow_change_team, m_has_auto_start_in_server; + bool m_allow_change_team, m_has_auto_start_in_server, + m_server_configurable; GUIEngine::IconButtonWidget* m_back_widget; GUIEngine::LabelWidget* m_header; GUIEngine::LabelWidget* m_text_bubble; GUIEngine::LabelWidget* m_timeout_message; GUIEngine::IconButtonWidget* m_start_button; + GUIEngine::IconButtonWidget* m_config_button; GUIEngine::ListWidget* m_player_list; GUIEngine::TextBoxWidget* m_chat_box; GUIEngine::ButtonWidget* m_send_button; @@ -142,7 +144,7 @@ public: void initAutoStartTimer(bool grand_prix_started, unsigned min_players, float start_timeout, unsigned server_max_player); void setStartingTimerTo(float t); - + void toggleServerConfigButton(bool val) { m_server_configurable = val; } }; // class NetworkingLobby #endif