Add AI handling for online racing games
This commit is contained in:
parent
170e4be0ca
commit
1614868b5d
@ -1440,7 +1440,7 @@ int handleCmdLine(bool has_server_config, bool has_parent_process)
|
||||
{
|
||||
std::string cmd =
|
||||
std::string("--stdout=server_ai.log --no-graphics"
|
||||
" --auto-connect --connect-now=127.0.0.1:") +
|
||||
" --connect-now=127.0.0.1:") +
|
||||
StringUtils::toString(STKHost::get()->getPrivatePort()) +
|
||||
" --no-console-log --network-ai="
|
||||
+ StringUtils::toString(ai_num);
|
||||
|
@ -536,6 +536,7 @@ void ServerLobby::setup()
|
||||
m_item_seed = 0;
|
||||
m_winner_peer_id = 0;
|
||||
m_client_starting_time = 0;
|
||||
m_ai_count = 0;
|
||||
auto players = STKHost::get()->getPlayersForNewGame();
|
||||
if (m_game_setup->isGrandPrix() && !m_game_setup->isGrandPrixStarted())
|
||||
{
|
||||
@ -547,6 +548,11 @@ void ServerLobby::setup()
|
||||
for (auto player : players)
|
||||
player->setKartName("");
|
||||
}
|
||||
if (auto ai = m_ai_peer.lock())
|
||||
{
|
||||
for (auto player : ai->getPlayerProfiles())
|
||||
player->setKartName("");
|
||||
}
|
||||
|
||||
StateManager::get()->resetActivePlayers();
|
||||
// We use maximum 16bit unsigned limit
|
||||
@ -664,7 +670,8 @@ void ServerLobby::kickHost(Event* event)
|
||||
NetworkString& data = event->data();
|
||||
uint32_t host_id = data.getUInt32();
|
||||
std::shared_ptr<STKPeer> peer = STKHost::get()->findPeerByHostId(host_id);
|
||||
if (peer)
|
||||
// Ignore kicking ai peer if ai handling is on
|
||||
if (peer && (!ServerConfig::m_ai_handling || !peer->isAIPeer()))
|
||||
peer->kick();
|
||||
} // kickHost
|
||||
|
||||
@ -1113,7 +1120,7 @@ void ServerLobby::asynchronousUpdate()
|
||||
m_timeout.store(std::numeric_limits<int64_t>::max());
|
||||
}
|
||||
if (m_timeout.load() < (int64_t)StkTime::getMonoTimeMs() ||
|
||||
(checkPeersReady() &&
|
||||
(checkPeersReady(true/*ignore_ai_peer*/) &&
|
||||
(int)players >= ServerConfig::m_min_start_game_players))
|
||||
{
|
||||
resetPeersReady();
|
||||
@ -1151,7 +1158,8 @@ void ServerLobby::asynchronousUpdate()
|
||||
// m_server_has_loaded_world is set by main thread with atomic write
|
||||
if (m_server_has_loaded_world.load() == false)
|
||||
return;
|
||||
if (!checkPeersReady())
|
||||
if (!checkPeersReady(
|
||||
ServerConfig::m_ai_handling && m_ai_count == 0/*ignore_ai_peer*/))
|
||||
return;
|
||||
// Reset for next state usage
|
||||
resetPeersReady();
|
||||
@ -1188,6 +1196,17 @@ 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_profiles = ai->getPlayerProfiles();
|
||||
if (m_ai_count > 0)
|
||||
{
|
||||
ai_profiles.resize(m_ai_count);
|
||||
players.insert(players.end(), ai_profiles.begin(),
|
||||
ai_profiles.end());
|
||||
}
|
||||
}
|
||||
m_game_setup->sortPlayersForGrandPrix(players);
|
||||
m_game_setup->sortPlayersForGame(players);
|
||||
for (unsigned i = 0; i < players.size(); i++)
|
||||
@ -1795,7 +1814,7 @@ void ServerLobby::update(int ticks)
|
||||
Log::info("ServerLobby", "End of game message sent");
|
||||
break;
|
||||
case RESULT_DISPLAY:
|
||||
if (checkPeersReady() ||
|
||||
if (checkPeersReady(true/*ignore_ai_peer*/) ||
|
||||
(int64_t)StkTime::getMonoTimeMs() > m_timeout.load())
|
||||
{
|
||||
// Send a notification to all clients to exit
|
||||
@ -2016,10 +2035,33 @@ void ServerLobby::startSelection(const Event *event)
|
||||
m_available_kts.second.erase(track_erase);
|
||||
}
|
||||
|
||||
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_FREE_FOR_ALL)
|
||||
{
|
||||
unsigned max_player = 0;
|
||||
STKHost::get()->updatePlayers(&max_player);
|
||||
if (auto ai = m_ai_peer.lock())
|
||||
{
|
||||
if (supportsAI())
|
||||
{
|
||||
unsigned total_ai_available =
|
||||
(unsigned)ai->getPlayerProfiles().size();
|
||||
m_ai_count = max_player > total_ai_available ?
|
||||
0 : total_ai_available - max_player + 1;
|
||||
// Disable ai peer for this game
|
||||
if (m_ai_count == 0)
|
||||
ai->setValidated(false);
|
||||
else
|
||||
ai->setValidated(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ai->setValidated(false);
|
||||
m_ai_count = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_ai_count = 0;
|
||||
|
||||
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_FREE_FOR_ALL)
|
||||
{
|
||||
auto it = m_available_kts.second.begin();
|
||||
while (it != m_available_kts.second.end())
|
||||
{
|
||||
@ -2842,6 +2884,7 @@ void ServerLobby::connectionRequested(Event* event)
|
||||
// Reject non-valiated player joinning if WAN server and not disabled
|
||||
// encforement of validation, unless it's player from localhost or lan
|
||||
// And no duplicated online id or split screen players in ranked server
|
||||
// AIPeer only from lan and only 1 if ai handling
|
||||
std::set<uint32_t> all_online_ids =
|
||||
STKHost::get()->getAllPlayerOnlineIds();
|
||||
bool duplicated_ranked_player =
|
||||
@ -2853,7 +2896,11 @@ void ServerLobby::connectionRequested(Event* event)
|
||||
NetworkConfig::get()->isWAN() &&
|
||||
ServerConfig::m_validating_player) ||
|
||||
(ServerConfig::m_strict_players &&
|
||||
(player_count != 1 || online_id == 0 || duplicated_ranked_player)))
|
||||
(player_count != 1 || online_id == 0 || duplicated_ranked_player)) ||
|
||||
(peer->isAIPeer() && !peer->getAddress().isLAN()) ||
|
||||
(peer->isAIPeer() &&
|
||||
ServerConfig::m_ai_handling && !m_ai_peer.expired()) ||
|
||||
(peer->isAIPeer() && m_game_setup->isGrandPrix()))
|
||||
{
|
||||
NetworkString* message = getNetworkString(2);
|
||||
message->setSynchronous(true);
|
||||
@ -2865,6 +2912,9 @@ void ServerLobby::connectionRequested(Event* event)
|
||||
return;
|
||||
}
|
||||
|
||||
if (ServerConfig::m_ai_handling && peer->isAIPeer())
|
||||
m_ai_peer = peer;
|
||||
|
||||
if (encrypted_size != 0)
|
||||
{
|
||||
m_pending_connection[peer] = std::make_pair(online_id,
|
||||
@ -2984,7 +3034,7 @@ void ServerLobby::handleUnencryptedConnection(std::shared_ptr<STKPeer> peer,
|
||||
peer->addPlayer(player);
|
||||
}
|
||||
|
||||
peer->setValidated();
|
||||
peer->setValidated(true);
|
||||
|
||||
// send a message to the one that asked to connect
|
||||
NetworkString* server_info = getNetworkString();
|
||||
@ -3053,7 +3103,7 @@ void ServerLobby::handleUnencryptedConnection(std::shared_ptr<STKPeer> peer,
|
||||
}
|
||||
}
|
||||
#ifdef ENABLE_SQLITE3
|
||||
if (m_server_stats_table.empty())
|
||||
if (m_server_stats_table.empty() || peer->isAIPeer())
|
||||
return;
|
||||
std::string query;
|
||||
if (ServerConfig::m_ipv6_server && !peer->getIPV6Address().empty())
|
||||
@ -3135,6 +3185,31 @@ void ServerLobby::updatePlayerList(bool update_when_reset_server)
|
||||
return;
|
||||
|
||||
auto all_profiles = STKHost::get()->getAllPlayerProfiles();
|
||||
// N - 1 AI
|
||||
auto ai = m_ai_peer.lock();
|
||||
if (supportsAI() && ai)
|
||||
{
|
||||
auto ai_profiles = ai->getPlayerProfiles();
|
||||
if (m_state.load() == WAITING_FOR_START_GAME ||
|
||||
update_when_reset_server)
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
NetworkString* pl = getNetworkString();
|
||||
pl->setSynchronous(true);
|
||||
pl->addUInt8(LE_UPDATE_PLAYER_LIST)
|
||||
@ -3390,6 +3465,8 @@ bool ServerLobby::handleAllVotes(PeerVote* winner_vote,
|
||||
auto peers = STKHost::get()->getPeers();
|
||||
for (auto peer : peers)
|
||||
{
|
||||
if (peer->isAIPeer())
|
||||
continue;
|
||||
if (peer->hasPlayerProfiles() && !peer->isWaitingForGame())
|
||||
cur_players += 1.0f;
|
||||
}
|
||||
@ -3862,6 +3939,9 @@ void ServerLobby::addWaitingPlayersToGame()
|
||||
getRankingForPlayer(peer->getPlayerProfiles()[0]);
|
||||
}
|
||||
}
|
||||
// Re-activiate the ai
|
||||
if (auto ai = m_ai_peer.lock())
|
||||
ai->setValidated(true);
|
||||
} // addWaitingPlayersToGame
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -4465,3 +4545,27 @@ void ServerLobby::saveInitialItems()
|
||||
assert(nim);
|
||||
nim->saveCompleteState(m_items_complete_state);
|
||||
} // saveInitialItems
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool ServerLobby::supportsAI()
|
||||
{
|
||||
return getGameMode() == 3 || getGameMode() == 4;
|
||||
} // supportsAI
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool ServerLobby::checkPeersReady(bool ignore_ai_peer) const
|
||||
{
|
||||
bool all_ready = true;
|
||||
for (auto p : m_peers_ready)
|
||||
{
|
||||
auto peer = p.first.lock();
|
||||
if (ignore_ai_peer && peer->isAIPeer())
|
||||
continue;
|
||||
if (!peer)
|
||||
continue;
|
||||
all_ready = all_ready && p.second;
|
||||
if (!all_ready)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} // checkPeersReady
|
||||
|
@ -116,6 +116,8 @@ private:
|
||||
* (disconnected). */
|
||||
std::weak_ptr<STKPeer> m_server_owner;
|
||||
|
||||
std::weak_ptr<STKPeer> m_ai_peer;
|
||||
|
||||
std::atomic<uint32_t> m_server_owner_id;
|
||||
|
||||
/** Official karts and tracks available in server. */
|
||||
@ -207,6 +209,9 @@ private:
|
||||
|
||||
uint64_t m_client_starting_time;
|
||||
|
||||
// Calculated before each game started
|
||||
unsigned m_ai_count;
|
||||
|
||||
// connection management
|
||||
void clientDisconnected(Event* event);
|
||||
void connectionRequested(Event* event);
|
||||
@ -227,19 +232,7 @@ private:
|
||||
void updateServerOwner();
|
||||
void handleServerConfiguration(Event* event);
|
||||
void updateTracksForMode();
|
||||
bool checkPeersReady() const
|
||||
{
|
||||
bool all_ready = true;
|
||||
for (auto p : m_peers_ready)
|
||||
{
|
||||
if (p.first.expired())
|
||||
continue;
|
||||
all_ready = all_ready && p.second;
|
||||
if (!all_ready)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool checkPeersReady(bool ignore_ai_peer) const;
|
||||
void resetPeersReady()
|
||||
{
|
||||
for (auto it = m_peers_ready.begin(); it != m_peers_ready.end();)
|
||||
@ -333,6 +326,7 @@ private:
|
||||
void testBannedForOnlineId(STKPeer* peer, uint32_t online_id) const;
|
||||
void writeDisconnectInfoTable(STKPeer* peer);
|
||||
void writePlayerReport(Event* event);
|
||||
bool supportsAI();
|
||||
public:
|
||||
ServerLobby();
|
||||
virtual ~ServerLobby();
|
||||
|
@ -385,6 +385,12 @@ namespace ServerConfig
|
||||
"empty to disable. "
|
||||
"This table can be shared for all servers if you use the same name."));
|
||||
|
||||
SERVER_CFG_PREFIX BoolServerConfigParam m_ai_handling
|
||||
SERVER_CFG_DEFAULT(BoolServerConfigParam(false, "ai-handling",
|
||||
"If true this server will auto add / remove AI connected with "
|
||||
"network-ai=x, which will kick N - 1 bot(s) where N is the number "
|
||||
"of human players. Only use this for non-GP racing server."));
|
||||
|
||||
// ========================================================================
|
||||
/** Server version, will be advanced if there are protocol changes. */
|
||||
static const uint32_t m_server_version = 6;
|
||||
|
@ -266,7 +266,11 @@ STKHost::STKHost(bool server)
|
||||
if (addr.port == 0 && !UserConfigParams::m_random_server_port)
|
||||
addr.port = stk_config->m_server_port;
|
||||
// Reserve 1 peer to deliver full server message
|
||||
m_network = new Network(ServerConfig::m_server_max_players + 1,
|
||||
int peer_count = ServerConfig::m_server_max_players + 1;
|
||||
// 1 more peer to hold ai peer
|
||||
if (ServerConfig::m_ai_handling)
|
||||
peer_count++;
|
||||
m_network = new Network(peer_count,
|
||||
/*channel_limit*/EVENT_CHANNEL_COUNT, /*max_in_bandwidth*/0,
|
||||
/*max_out_bandwidth*/ 0, &addr, true/*change_port_if_bound*/);
|
||||
}
|
||||
@ -1059,7 +1063,8 @@ void STKHost::mainLoop()
|
||||
|
||||
// Remove peer which has not been validated after a specific time
|
||||
// It is validated when the first connection request has finished
|
||||
if (!it->second->isValidated() &&
|
||||
if (!it->second->isAIPeer() &&
|
||||
!it->second->isValidated() &&
|
||||
it->second->getConnectedTime() > timeout)
|
||||
{
|
||||
Log::info("STKHost", "%s has not been validated for more"
|
||||
@ -1155,7 +1160,7 @@ void STKHost::mainLoop()
|
||||
getPeerCount());
|
||||
// Client always trust the server
|
||||
if (!is_server)
|
||||
stk_peer->setValidated();
|
||||
stk_peer->setValidated(true);
|
||||
} // ENET_EVENT_TYPE_CONNECT
|
||||
else if (event.type == ENET_EVENT_TYPE_DISCONNECT)
|
||||
{
|
||||
@ -1510,6 +1515,8 @@ std::vector<std::shared_ptr<NetworkPlayerProfile> >
|
||||
{
|
||||
if (peer.second->isDisconnected() || !peer.second->isValidated())
|
||||
continue;
|
||||
if (ServerConfig::m_ai_handling && peer.second->isAIPeer())
|
||||
continue;
|
||||
auto peer_profile = peer.second->getPlayerProfiles();
|
||||
p.insert(p.end(), peer_profile.begin(), peer_profile.end());
|
||||
}
|
||||
@ -1561,7 +1568,7 @@ void STKHost::initClientNetwork(ENetEvent& event, Network* new_network)
|
||||
}
|
||||
auto stk_peer = std::make_shared<STKPeer>(event.peer, this,
|
||||
m_next_unique_host_id++);
|
||||
stk_peer->setValidated();
|
||||
stk_peer->setValidated(true);
|
||||
m_peers[event.peer] = stk_peer;
|
||||
setPrivatePort();
|
||||
auto pm = ProtocolManager::lock();
|
||||
@ -1599,6 +1606,8 @@ std::vector<std::shared_ptr<NetworkPlayerProfile> >
|
||||
auto& stk_peer = p.second;
|
||||
if (stk_peer->isWaitingForGame())
|
||||
continue;
|
||||
if (ServerConfig::m_ai_handling && stk_peer->isAIPeer())
|
||||
continue;
|
||||
for (auto& q : stk_peer->getPlayerProfiles())
|
||||
players.push_back(q);
|
||||
}
|
||||
@ -1623,6 +1632,8 @@ void STKHost::updatePlayers(unsigned* ingame, unsigned* waiting,
|
||||
auto& stk_peer = p.second;
|
||||
if (!stk_peer->isValidated())
|
||||
continue;
|
||||
if (ServerConfig::m_ai_handling && stk_peer->isAIPeer())
|
||||
continue;
|
||||
if (stk_peer->isWaitingForGame())
|
||||
waiting_players += (uint32_t)stk_peer->getPlayerProfiles().size();
|
||||
else
|
||||
|
@ -140,7 +140,7 @@ public:
|
||||
void addPlayer(std::shared_ptr<NetworkPlayerProfile> p)
|
||||
{ m_players.push_back(p); }
|
||||
// ------------------------------------------------------------------------
|
||||
void setValidated() { m_validated.store(true); }
|
||||
void setValidated(bool val) { m_validated.store(val); }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns if the client is validated by server. */
|
||||
bool isValidated() const { return m_validated.load(); }
|
||||
|
Loading…
Reference in New Issue
Block a user