Allow server AI starting in a same process with client

This commit is contained in:
Benau 2020-02-10 16:39:47 +08:00
parent 92a11c075a
commit e0f494abcd
14 changed files with 139 additions and 59 deletions

View File

@ -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)
{

View File

@ -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

View File

@ -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"))

View File

@ -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<ServerLobby>();
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;

View File

@ -468,7 +468,16 @@ std::shared_ptr<AbstractKart> 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<AbstractKart> 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<AbstractKart> 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;

View File

@ -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
// ----------------------------------------------------------------------------

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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 =

View File

@ -57,6 +57,7 @@
#include "utils/random_generator.hpp"
#include "utils/string_utils.hpp"
#include "utils/time.hpp"
#include "utils/translation.hpp"
#include <algorithm>
#include <fstream>
@ -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<STKPeer> 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<NetworkPlayerProfile>
(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<STKPeer> 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());

View File

@ -127,8 +127,14 @@ private:
* (disconnected). */
std::weak_ptr<STKPeer> m_server_owner;
/** AI peer which holds the list of reserved AI for dedicated server. */
std::weak_ptr<STKPeer> 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<std::shared_ptr<NetworkPlayerProfile> > m_ai_profiles;
std::atomic<uint32_t> 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<NetworkPlayerProfile>& npp) const
{ return m_ai_profiles.find(npp) != m_ai_profiles.end(); }
}; // class ServerLobby
#endif // SERVER_LOBBY_HPP

View File

@ -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
{

View File

@ -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])
{