From e0f494abcd61cfab107bb2750a8149da88c301af Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 10 Feb 2020 16:39:47 +0800 Subject: [PATCH] Allow server AI starting in a same process with client --- .../controller/network_ai_controller.cpp | 10 +- .../controller/network_ai_controller.hpp | 3 + src/main.cpp | 21 +---- src/modes/linear_world.cpp | 6 +- src/modes/world.cpp | 15 ++- src/network/network_config.cpp | 1 + src/network/network_config.hpp | 9 ++ src/network/protocols/client_lobby.cpp | 3 +- src/network/protocols/client_lobby.hpp | 1 + src/network/protocols/lobby_protocol.cpp | 11 ++- src/network/protocols/server_lobby.cpp | 91 ++++++++++++++----- src/network/protocols/server_lobby.hpp | 8 ++ .../online/create_server_screen.cpp | 4 + .../online/networking_lobby.cpp | 15 +-- 14 files changed, 139 insertions(+), 59 deletions(-) diff --git a/src/karts/controller/network_ai_controller.cpp b/src/karts/controller/network_ai_controller.cpp index dfaaa4e3d..100c57b36 100644 --- a/src/karts/controller/network_ai_controller.cpp +++ b/src/karts/controller/network_ai_controller.cpp @@ -36,7 +36,9 @@ NetworkAIController::NetworkAIController(AbstractKart *kart, { m_ai_controller = ai; m_ai_controls = new KartControl; - Camera::createCamera(kart, local_player_id); + // We only need camera for real AI instance for debugging view + if (NetworkConfig::get()->isNetworkAIInstance()) + Camera::createCamera(kart, local_player_id); ai->setControls(m_ai_controls); } // NetworkAIController @@ -47,6 +49,12 @@ NetworkAIController::~NetworkAIController() delete m_ai_controls; } // ~NetworkAIController +// ---------------------------------------------------------------------------- +bool NetworkAIController::isLocalPlayerController() const +{ + return NetworkConfig::get()->isNetworkAIInstance(); +} // isLocalPlayerController + // ---------------------------------------------------------------------------- void NetworkAIController::update(int ticks) { diff --git a/src/karts/controller/network_ai_controller.hpp b/src/karts/controller/network_ai_controller.hpp index 5d1251875..bf55a6838 100644 --- a/src/karts/controller/network_ai_controller.hpp +++ b/src/karts/controller/network_ai_controller.hpp @@ -38,6 +38,9 @@ public: virtual ~NetworkAIController(); virtual void update(int ticks) OVERRIDE; virtual void reset() OVERRIDE; + // ------------------------------------------------------------------------ + virtual bool isLocalPlayerController() const OVERRIDE; + // ------------------------------------------------------------------------ static void setAIFrequency(int freq) { m_ai_frequency = freq; } }; // class NetworkAIController diff --git a/src/main.cpp b/src/main.cpp index a2fde5be0..48512bcd0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1355,11 +1355,7 @@ int handleCmdLine(bool has_server_config, bool has_parent_process) int ai_num = 0; if (CommandLine::has("--server-ai", &ai_num)) - { - Log::info("main", "Add %d server ai(s) server configurable will be " - "disabled.", ai_num); - ServerConfig::m_server_configurable = false; - } + NetworkConfig::get()->setNumFixedAI(ai_num); std::string addr; bool has_addr = CommandLine::has("--connect-now", &addr); @@ -1444,21 +1440,6 @@ int handleCmdLine(bool has_server_config, bool has_parent_process) Log::info("main", "Creating a LAN server '%s'.", server_name.c_str()); } - if (ai_num > 0) - { - std::string cmd = - std::string("--stdout=server_ai.log --no-graphics" - " --network-ai-freq=10 --connect-now=127.0.0.1:") + - StringUtils::toString(STKHost::get()->getPrivatePort()) + - " --no-console-log --disable-polling --network-ai=" - + StringUtils::toString(ai_num); - if (!server_password.empty()) - cmd += " --server-password=" + server_password; - STKHost::get()->setSeparateProcess( - new SeparateProcess( - SeparateProcess::getCurrentExecutableLocation(), cmd, - false/*create_pipe*/, "childprocess_ai"/*childprocess_name*/)); - } } if (CommandLine::has("--auto-connect")) diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index f4b54e62a..6c4b74b52 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -35,6 +35,7 @@ #include "network/network_player_profile.hpp" #include "network/network_string.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 "network/stk_peer.hpp" @@ -172,7 +173,8 @@ void LinearWorld::reset(bool restart) */ void LinearWorld::update(int ticks) { - if (NetworkConfig::get()->isServer() && getPhase() == RACE_PHASE) + auto sl = LobbyProtocol::get(); + if (sl && getPhase() == RACE_PHASE) { bool all_players_finished = true; bool has_ai = false; @@ -185,7 +187,7 @@ void LinearWorld::update(int ticks) if (npp) { auto peer = npp->getPeer(); - if (peer && peer->isAIPeer()) + if ((peer && peer->isAIPeer()) || sl->isAIProfile(npp)) has_ai = true; else if (!getKart(i)->hasFinishedRace()) all_players_finished = false; diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 7d78480b5..a5a32c71b 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -468,7 +468,16 @@ std::shared_ptr World::createKart { case RaceManager::KT_PLAYER: { - if (NetworkConfig::get()->isNetworkAIInstance()) + int local_player_count = -1; + if (NetworkConfig::get()->isClient()) + { + local_player_count = + (int)NetworkConfig::get()->getNetworkPlayers().size(); + } + // local_player_id >= local_player_count for fixed AI defined in create + // server screen + if (NetworkConfig::get()->isNetworkAIInstance() || + local_player_id >= local_player_count) { AIBaseController* ai = NULL; if (race_manager->isBattleMode()) @@ -495,8 +504,6 @@ std::shared_ptr World::createKart case RaceManager::KT_NETWORK_PLAYER: { controller = new NetworkPlayerController(new_kart.get()); - if (!online_name.empty()) - new_kart->setOnScreenText(online_name.c_str()); m_num_players++; break; } @@ -511,6 +518,8 @@ std::shared_ptr World::createKart break; } + if (!controller->isLocalPlayerController() && !online_name.empty()) + new_kart->setOnScreenText(online_name.c_str()); new_kart->setController(controller); race_manager->setKartColor(index, ri->getHue()); return new_kart; diff --git a/src/network/network_config.cpp b/src/network/network_config.cpp index 03e52e136..beb3ca706 100644 --- a/src/network/network_config.cpp +++ b/src/network/network_config.cpp @@ -73,6 +73,7 @@ NetworkConfig::NetworkConfig() m_network_ai_instance = false; m_state_frequency = 10; m_nat64_prefix_data.fill(-1); + m_num_fixed_ai = 0; } // NetworkConfig // ---------------------------------------------------------------------------- diff --git a/src/network/network_config.hpp b/src/network/network_config.hpp index 11e855a2b..bed60aa39 100644 --- a/src/network/network_config.hpp +++ b/src/network/network_config.hpp @@ -85,6 +85,11 @@ private: * AI. (usually used together with ai-handling in server config) */ bool m_network_ai_instance; + /** No. of fixed AI in all-in-one graphical client server, the player + * connecting with 127.* or ::1/128 will be in charged of controlling the + * AI. */ + unsigned m_num_fixed_ai; + /** The LAN port on which a client is waiting for a server connection. */ uint16_t m_client_port; @@ -273,6 +278,10 @@ public: { return m_nat64_prefix_data; } // ------------------------------------------------------------------------ void initClientPort(); + // ------------------------------------------------------------------------ + void setNumFixedAI(unsigned num) { m_num_fixed_ai = num; } + // ------------------------------------------------------------------------ + unsigned getNumFixedAI() const { return m_num_fixed_ai; } }; // class NetworkConfig #endif // HEADER_NETWORK_CONFIG diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index 08e637091..aa01b5e32 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -802,7 +802,8 @@ void ClientLobby::updatePlayerList(Event* event) lp.m_user_name = _("%s (handicapped)", lp.m_user_name); } lp.m_kart_team = (KartTeam)data.getUInt8(); - if (lp.m_host_id == STKHost::get()->getMyHostId()) + // No handicap for AI peer + if (!ai && lp.m_host_id == STKHost::get()->getMyHostId()) { if (is_peer_server_owner) client_server_owner = true; diff --git a/src/network/protocols/client_lobby.hpp b/src/network/protocols/client_lobby.hpp index 1dbb8cb08..240488756 100644 --- a/src/network/protocols/client_lobby.hpp +++ b/src/network/protocols/client_lobby.hpp @@ -50,6 +50,7 @@ struct LobbyPlayer std::string m_country_code; /* Icon id for spectator in NetworkingLobby::loadedFromFile is 5. */ bool isSpectator() const { return m_icon_id == 5; } + bool isAI() const { return m_icon_id == 6; } }; class ClientLobby : public LobbyProtocol diff --git a/src/network/protocols/lobby_protocol.cpp b/src/network/protocols/lobby_protocol.cpp index dffbb7a23..db73de877 100644 --- a/src/network/protocols/lobby_protocol.cpp +++ b/src/network/protocols/lobby_protocol.cpp @@ -99,6 +99,12 @@ void LobbyProtocol::configRemoteKart( // Set number of global and local players. race_manager->setNumPlayers((int)players.size(), local_player_size); + int local_player_count = -1; + if (NetworkConfig::get()->isClient()) + { + local_player_count = + (int)NetworkConfig::get()->getNetworkPlayers().size(); + } // Create the kart information for the race manager: // ------------------------------------------------- for (unsigned int i = 0; i < players.size(); i++) @@ -110,7 +116,10 @@ void LobbyProtocol::configRemoteKart( // on the server, and all non-local players on a client (the local // karts are created in the ClientLobby). int local_player_id = profile->getLocalPlayerId(); - if (!is_local) + + // local_player_id >= local_player_count for fixed AI defined in create + // server screen + if (!is_local || local_player_id >= local_player_count) { // No device or player profile is needed for remote kart. local_player_id = diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 6e495854b..608137056 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -57,6 +57,7 @@ #include "utils/random_generator.hpp" #include "utils/string_utils.hpp" #include "utils/time.hpp" +#include "utils/translation.hpp" #include #include @@ -670,6 +671,8 @@ void ServerLobby::setup() for (auto player : ai->getPlayerProfiles()) player->setKartName(""); } + for (auto ai : m_ai_profiles) + ai->setKartName(""); StateManager::get()->resetActivePlayers(); // We use maximum 16bit unsigned limit @@ -1512,15 +1515,23 @@ void ServerLobby::asynchronousUpdate() ItemManager::updateRandomSeed(m_item_seed); m_game_setup->setRace(winner_vote); auto players = STKHost::get()->getPlayersForNewGame(); - auto ai = m_ai_peer.lock(); - if (supportsAI() && ai) + auto ai_instance = m_ai_peer.lock(); + if (supportsAI()) { - auto ai_profiles = ai->getPlayerProfiles(); - if (m_ai_count > 0) + if (ai_instance) { - ai_profiles.resize(m_ai_count); - players.insert(players.end(), ai_profiles.begin(), - ai_profiles.end()); + auto ai_profiles = ai_instance->getPlayerProfiles(); + if (m_ai_count > 0) + { + ai_profiles.resize(m_ai_count); + players.insert(players.end(), ai_profiles.begin(), + ai_profiles.end()); + } + } + else if (!m_ai_profiles.empty()) + { + players.insert(players.end(), m_ai_profiles.begin(), + m_ai_profiles.end()); } } m_game_setup->sortPlayersForGrandPrix(players); @@ -3303,7 +3314,7 @@ void ServerLobby::connectionRequested(Event* event) unsigned total_players = 0; STKHost::get()->updatePlayers(NULL, NULL, &total_players); - if (total_players + player_count > + if (total_players + player_count + m_ai_profiles.size() > (unsigned)ServerConfig::m_server_max_players) { NetworkString *message = getNetworkString(2); @@ -3506,6 +3517,29 @@ void ServerLobby::handleUnencryptedConnection(std::shared_ptr peer, .addUInt8(m_player_reports_table_exists ? 1 : 0); peer->setSpectator(false); + + // The 127.* or ::1/128 will be in charged for controlling AI + if (m_ai_profiles.empty() && peer->getAddress().isLoopback()) + { + unsigned ai_add = NetworkConfig::get()->getNumFixedAI(); + // We need to reserve at least 1 slot for new player + if (player_count + ai_add + 1 > ServerConfig::m_server_max_players) + ai_add = ServerConfig::m_server_max_players - player_count - 1; + for (unsigned i = 0; i < ai_add; i++) + { +#ifdef SERVER_ONLY + core::stringw name = L"Bot"; +#else + core::stringw name = _("Bot"); +#endif + if (i > 0) + name += core::stringw(" ") + StringUtils::toWString(i); + m_ai_profiles.insert(std::make_shared + (peer, name, peer->getHostId(), 0.0f, 0, HANDICAP_NONE, + player_count + i, KART_TEAM_NONE, "")); + } + } + if (game_started) { peer->setWaitingForGame(true); @@ -3538,6 +3572,7 @@ void ServerLobby::handleUnencryptedConnection(std::shared_ptr peer, getRankingForPlayer(peer->getPlayerProfiles()[0]); } } + #ifdef ENABLE_SQLITE3 if (m_server_stats_table.empty() || peer->isAIPeer()) return; @@ -3624,28 +3659,36 @@ void ServerLobby::updatePlayerList(bool update_when_reset_server) auto all_profiles = STKHost::get()->getAllPlayerProfiles(); // N - 1 AI - auto ai = m_ai_peer.lock(); - if (supportsAI() && ai) + auto ai_instance = m_ai_peer.lock(); + if (supportsAI()) { - auto ai_profiles = ai->getPlayerProfiles(); - if (m_state.load() == WAITING_FOR_START_GAME || - update_when_reset_server) + if (ai_instance) { - if (all_profiles.size() > ai_profiles.size()) - ai_profiles.clear(); - else if (!all_profiles.empty()) + auto ai_profiles = ai_instance->getPlayerProfiles(); + if (m_state.load() == WAITING_FOR_START_GAME || + update_when_reset_server) { - ai_profiles.resize( - ai_profiles.size() - all_profiles.size() + 1); + if (all_profiles.size() > ai_profiles.size()) + ai_profiles.clear(); + else if (!all_profiles.empty()) + { + ai_profiles.resize( + ai_profiles.size() - all_profiles.size() + 1); + } } + else + { + // Use fixed number of AI calculated when started game + ai_profiles.resize(m_ai_count); + } + all_profiles.insert(all_profiles.end(), ai_profiles.begin(), + ai_profiles.end()); } - else + else if (!m_ai_profiles.empty()) { - // Use fixed number of AI calculated when started game - ai_profiles.resize(m_ai_count); + all_profiles.insert(all_profiles.end(), m_ai_profiles.begin(), + m_ai_profiles.end()); } - all_profiles.insert(all_profiles.end(), ai_profiles.begin(), - ai_profiles.end()); } m_lobby_players.store((int)all_profiles.size()); @@ -3676,7 +3719,7 @@ void ServerLobby::updatePlayerList(bool update_when_reset_server) m_peers_ready.find(p) != m_peers_ready.end() && m_peers_ready.at(p)) boolean_combine |= (1 << 3); - if (p && p->isAIPeer()) + if ((p && p->isAIPeer()) || isAIProfile(profile)) boolean_combine |= (1 << 4); pl->addUInt8(boolean_combine); pl->addUInt8(profile->getHandicap()); diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 33df34686..098abf4a4 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -127,8 +127,14 @@ private: * (disconnected). */ std::weak_ptr m_server_owner; + /** AI peer which holds the list of reserved AI for dedicated server. */ std::weak_ptr m_ai_peer; + /** AI profiles for all-in-one graphical client server, this will be a + * fixed count thorough the live time of server, which its value is + * configured in NetworkConfig. */ + std::set > m_ai_profiles; + std::atomic m_server_owner_id; /** Official karts and tracks available in server. */ @@ -379,6 +385,8 @@ public: void saveIPBanTable(const SocketAddress& addr); void listBanTable(); void initServerStatsTable(); + bool isAIProfile(const std::shared_ptr& npp) const + { return m_ai_profiles.find(npp) != m_ai_profiles.end(); } }; // class ServerLobby #endif // SERVER_LOBBY_HPP diff --git a/src/states_screens/online/create_server_screen.cpp b/src/states_screens/online/create_server_screen.cpp index 72ddd5474..5deb9885c 100644 --- a/src/states_screens/online/create_server_screen.cpp +++ b/src/states_screens/online/create_server_screen.cpp @@ -20,6 +20,7 @@ #include "audio/sfx_manager.hpp" #include "config/player_manager.hpp" #include "config/user_config.hpp" +#include "karts/controller/network_ai_controller.hpp" #include "network/network_config.hpp" #include "network/server.hpp" #include "network/server_config.hpp" @@ -391,7 +392,10 @@ void CreateServerScreen::createServer() if (m_supports_ai) { if (esi > 0) + { server_cfg << " --server-ai=" << esi; + NetworkAIController::setAIFrequency(10); + } } else { diff --git a/src/states_screens/online/networking_lobby.cpp b/src/states_screens/online/networking_lobby.cpp index 16c893acc..f42bb2b39 100644 --- a/src/states_screens/online/networking_lobby.cpp +++ b/src/states_screens/online/networking_lobby.cpp @@ -632,15 +632,16 @@ void NetworkingLobby::eventCallback(Widget* widget, const std::string& name, { return; } + LobbyPlayer& lp = + m_player_names.at(m_player_list->getSelectionInternalName()); + // For client server AI it doesn't make any sense to open the dialog + // There is no way to kick or add handicap to them + if (STKHost::get()->isClientServer() > 0 && lp.isAI()) + return; new NetworkPlayerDialog(host_online_local_ids[0], host_online_local_ids[1], host_online_local_ids[2], - m_player_names.at( - m_player_list->getSelectionInternalName()).m_user_name, - m_player_names.at( - m_player_list->getSelectionInternalName()).m_country_code, - m_allow_change_team, - m_player_names.at( - m_player_list->getSelectionInternalName()).m_handicap); + lp.m_user_name, lp.m_country_code, m_allow_change_team, + lp.m_handicap); } // click on a user else if (name == m_send_button->m_properties[PROP_ID]) {