Allow always spectating with /spectate [0 or 1]

This commit is contained in:
Benau 2020-05-13 11:27:27 +08:00
parent b122572a0c
commit 53bf93f2df
8 changed files with 141 additions and 15 deletions

View File

@ -1174,6 +1174,8 @@ void ClientLobby::backToLobby(Event *event)
NetworkString &data = event->data();
core::stringw msg;
MessageQueue::MessageType mt = MessageQueue::MT_ERROR;
switch ((BackLobbyReason)data.getUInt8()) // the second byte
{
case BLR_NO_GAME_FOR_LIVE_JOIN:
@ -1192,15 +1194,23 @@ void ClientLobby::backToLobby(Event *event)
// I18N: Error message shown when all players will go back to lobby
// when server owner quited the game
if (!STKHost::get()->isClientServer())
msg = _("Server owner quit the game");
msg = _("Server owner quit the game.");
break;
case BLR_SPECTATING_NEXT_GAME:
// I18N: Status shown to player when he will be spectating the next game
msg = _("You will be spectating the next game.");
mt = MessageQueue::MT_GENERIC;
break;
default:
break;
}
if (!msg.empty())
{
SFXManager::get()->quickSound("anvil");
MessageQueue::add(MessageQueue::MT_ERROR, msg);
if (mt == MessageQueue::MT_GENERIC)
SFXManager::get()->quickSound("plopp");
else
SFXManager::get()->quickSound("anvil");
MessageQueue::add(mt, msg);
}
} // backToLobby

View File

@ -99,7 +99,8 @@ public:
BLR_NO_GAME_FOR_LIVE_JOIN = 1,
BLR_NO_PLACE_FOR_LIVE_JOIN = 2,
BLR_ONE_PLAYER_IN_RANKED_MATCH = 3,
BLR_SERVER_ONWER_QUITED_THE_GAME = 4
BLR_SERVER_ONWER_QUITED_THE_GAME = 4,
BLR_SPECTATING_NEXT_GAME = 5
};
protected:

View File

@ -1547,7 +1547,9 @@ void ServerLobby::asynchronousUpdate()
m_item_seed = (uint32_t)StkTime::getTimeSinceEpoch();
ItemManager::updateRandomSeed(m_item_seed);
m_game_setup->setRace(winner_vote);
auto players = STKHost::get()->getPlayersForNewGame();
bool has_always_on_spectators = false;
auto players = STKHost::get()
->getPlayersForNewGame(&has_always_on_spectators);
auto ai_instance = m_ai_peer.lock();
if (supportsAI())
{
@ -1617,6 +1619,10 @@ void ServerLobby::asynchronousUpdate()
resetPeersReady();
m_state = LOAD_WORLD;
sendMessageToPeers(load_world_message);
// updatePlayerList so the in lobby players (if any) can see always
// spectators join the game
if (has_always_on_spectators)
updatePlayerList();
delete load_world_message;
}
break;
@ -2427,13 +2433,37 @@ void ServerLobby::startSelection(const Event *event)
// Remove karts / tracks from server that are not supported on all clients
std::set<std::string> karts_erase, tracks_erase;
auto peers = STKHost::get()->getPeers();
std::set<STKPeer*> always_spectate_peers;
bool has_peer_plays_game = false;
for (auto peer : peers)
{
if (!peer->isValidated() || peer->isWaitingForGame())
continue;
peer->eraseServerKarts(m_available_kts.first, karts_erase);
peer->eraseServerTracks(m_available_kts.second, tracks_erase);
if (peer->alwaysSpectate())
always_spectate_peers.insert(peer.get());
else if (!peer->isAIPeer())
has_peer_plays_game = true;
}
// Disable always spectate peers if no players join the game
if (!has_peer_plays_game)
{
for (STKPeer* peer : always_spectate_peers)
peer->setAlwaysSpectate(false);
always_spectate_peers.clear();
}
else
{
// We make those always spectate peer waiting for game so it won't
// be able to vote, this will be reset in STKHost::getPlayersForNewGame
// This will also allow a correct number of in game players for max
// arena players handling
for (STKPeer* peer : always_spectate_peers)
peer->setWaitingForGame(true);
}
for (const std::string& kart_erase : karts_erase)
{
m_available_kts.first.erase(kart_erase);
@ -2589,6 +2619,19 @@ void ServerLobby::startSelection(const Event *event)
delete ns;
m_state = SELECTING;
if (!always_spectate_peers.empty())
{
NetworkString* back_lobby = getNetworkString(2);
back_lobby->setSynchronous(true);
back_lobby->addUInt8(LE_BACK_LOBBY).addUInt8(BLR_SPECTATING_NEXT_GAME);
STKHost::get()->sendPacketToAllPeersWith(
[always_spectate_peers](STKPeer* peer) {
return always_spectate_peers.find(peer) !=
always_spectate_peers.end(); }, back_lobby, /*reliable*/true);
delete back_lobby;
updatePlayerList();
}
if (!allowJoinedPlayersWaiting())
{
// Drop all pending players and keys if doesn't allow joinning-waiting
@ -3721,6 +3764,12 @@ void ServerLobby::updatePlayerList(bool update_when_reset_server)
!update_when_reset_server;
auto all_profiles = STKHost::get()->getAllPlayerProfiles();
size_t all_profiles_size = all_profiles.size();
for (auto& profile : all_profiles)
{
if (profile->getPeer()->alwaysSpectate())
all_profiles_size--;
}
// N - 1 AI
auto ai_instance = m_ai_peer.lock();
if (supportsAI())
@ -3731,12 +3780,12 @@ void ServerLobby::updatePlayerList(bool update_when_reset_server)
if (m_state.load() == WAITING_FOR_START_GAME ||
update_when_reset_server)
{
if (all_profiles.size() > ai_profiles.size())
if (all_profiles_size > ai_profiles.size())
ai_profiles.clear();
else if (!all_profiles.empty())
else if (all_profiles_size != 0)
{
ai_profiles.resize(
ai_profiles.size() - all_profiles.size() + 1);
ai_profiles.size() - all_profiles_size + 1);
}
}
else
@ -3774,7 +3823,9 @@ void ServerLobby::updatePlayerList(bool update_when_reset_server)
uint8_t boolean_combine = 0;
if (p && p->isWaitingForGame())
boolean_combine |= 1;
if (p && p->isSpectator())
if (p && (p->isSpectator() ||
((m_state.load() == WAITING_FOR_START_GAME ||
update_when_reset_server) && p->alwaysSpectate())))
boolean_combine |= (1 << 1);
if (p && m_server_owner_id.load() == p->getHostId())
boolean_combine |= (1 << 2);
@ -4413,7 +4464,8 @@ void ServerLobby::configPeersStartTime()
for (auto p : m_peers_ready)
{
auto peer = p.first.lock();
if (!peer)
// Spectators don't send input so we don't need to delay for them
if (!peer || peer->alwaysSpectate())
continue;
if (peer->getAveragePing() > max_ping_from_peers)
{
@ -5267,7 +5319,7 @@ bool ServerLobby::checkPeersReady(bool ignore_ai_peer) const
//-----------------------------------------------------------------------------
void ServerLobby::handleServerCommand(Event* event,
std::shared_ptr<STKPeer> peer) const
std::shared_ptr<STKPeer> peer)
{
NetworkString& data = event->data();
std::string language;
@ -5277,7 +5329,54 @@ void ServerLobby::handleServerCommand(Event* event,
auto argv = StringUtils::split(cmd, ' ');
if (argv.size() == 0)
return;
if (argv[0] == "listserveraddon")
if (argv[0] == "spectate")
{
if (m_game_setup->isGrandPrix() || !ServerConfig::m_live_players)
{
NetworkString* chat = getNetworkString();
chat->addUInt8(LE_CHAT);
chat->setSynchronous(true);
std::string msg = "Server doesn't support spectate";
chat->encodeString16(StringUtils::utf8ToWide(msg));
peer->sendPacket(chat, true/*reliable*/);
delete chat;
return;
}
if (argv.size() != 2 || (argv[1] != "0" && argv[1] != "1") ||
m_state.load() != WAITING_FOR_START_GAME)
{
NetworkString* chat = getNetworkString();
chat->addUInt8(LE_CHAT);
chat->setSynchronous(true);
std::string msg = "Usage: spectate [0 or 1], before game started";
chat->encodeString16(StringUtils::utf8ToWide(msg));
peer->sendPacket(chat, true/*reliable*/);
delete chat;
return;
}
if (argv[1] == "1")
{
if (m_process_type == PT_CHILD &&
peer->getHostId() == m_client_server_host_id.load())
{
NetworkString* chat = getNetworkString();
chat->addUInt8(LE_CHAT);
chat->setSynchronous(true);
std::string msg = "Graphical client server cannot spectate";
chat->encodeString16(StringUtils::utf8ToWide(msg));
peer->sendPacket(chat, true/*reliable*/);
delete chat;
return;
}
peer->setAlwaysSpectate(true);
}
else
peer->setAlwaysSpectate(false);
updatePlayerList();
}
else if (argv[0] == "listserveraddon")
{
NetworkString* chat = getNetworkString();
chat->addUInt8(LE_CHAT);

View File

@ -338,7 +338,7 @@ private:
std::vector<std::shared_ptr<NetworkPlayerProfile> > getLivePlayers() const;
void setPlayerKarts(const NetworkString& ns, STKPeer* peer) const;
bool handleAssets(const NetworkString& ns, STKPeer* peer);
void handleServerCommand(Event* event, std::shared_ptr<STKPeer> peer) const;
void handleServerCommand(Event* event, std::shared_ptr<STKPeer> peer);
void liveJoinRequest(Event* event);
void rejectLiveJoin(STKPeer* peer, BackLobbyReason blr);
bool canLiveJoinNow() const;

View File

@ -1528,13 +1528,21 @@ std::pair<int, int> STKHost::getAllPlayersTeamInfo() const
/** Get the players for starting a new game.
* \return A vector containing pointers on the players profiles. */
std::vector<std::shared_ptr<NetworkPlayerProfile> >
STKHost::getPlayersForNewGame() const
STKHost::getPlayersForNewGame(bool* has_always_on_spectators) const
{
std::vector<std::shared_ptr<NetworkPlayerProfile> > players;
std::lock_guard<std::mutex> lock(m_peers_mutex);
for (auto& p : m_peers)
{
auto& stk_peer = p.second;
// Handle always spectate for peer
if (has_always_on_spectators && stk_peer->alwaysSpectate())
{
*has_always_on_spectators = true;
stk_peer->setWaitingForGame(false);
stk_peer->setSpectator(true);
continue;
}
if (stk_peer->isWaitingForGame())
continue;
if (ServerConfig::m_ai_handling && stk_peer->isAIPeer())

View File

@ -386,7 +386,7 @@ public:
uint32_t getTotalPlayers() const { return m_total_players.load(); }
// ------------------------------------------------------------------------
std::vector<std::shared_ptr<NetworkPlayerProfile> >
getPlayersForNewGame() const;
getPlayersForNewGame(bool* has_always_on_spectators = NULL) const;
// ------------------------------------------------------------------------
void replaceNetwork(Network* new_network)
{

View File

@ -44,6 +44,7 @@ STKPeer::STKPeer(ENetPeer *enet_peer, STKHost* host, uint32_t host_id)
m_host_id = host_id;
m_connected_time = StkTime::getMonoTimeMs();
m_validated.store(false);
m_always_spectate.store(false);
m_average_ping.store(0);
m_packet_loss.store(0);
m_waiting_for_game.store(true);

View File

@ -85,6 +85,8 @@ protected:
std::atomic_bool m_warned_for_high_ping;
std::atomic_bool m_always_spectate;
/** Host id of this peer. */
uint32_t m_host_id;
@ -289,6 +291,11 @@ public:
int getConsecutiveMessages() const { return m_consecutive_messages; }
// ------------------------------------------------------------------------
const SocketAddress& getAddress() const { return *m_socket_address.get(); }
// ------------------------------------------------------------------------
void setAlwaysSpectate(bool val) { m_always_spectate.store(val); }
// ------------------------------------------------------------------------
bool alwaysSpectate() const { return m_always_spectate.load(); }
}; // STKPeer
#endif // STK_PEER_HPP