Add state to avoid deadlock when finishing the race

This commit is contained in:
Benau 2018-06-08 22:59:07 +08:00
parent 9af27d93c4
commit d9ede4f213
6 changed files with 86 additions and 68 deletions

View File

@ -332,7 +332,8 @@ void NetworkItemManager::restoreState(BareNetworkString *buffer, int count)
} // while count >0
// Inform the server which events have been received.
GameProtocol::lock()->sendItemEventConfirmation(World::getWorld()->getTimeTicks());
if (auto gp = GameProtocol::lock())
gp->sendItemEventConfirmation(World::getWorld()->getTimeTicks());
// Forward the confirmed item state till the world time:
int dt = World::getWorld()->getTimeTicks() - current_time;

View File

@ -70,7 +70,7 @@ public:
void sendItemUpdate();
void saveInitialState();
virtual void reset();
virtual void reset() OVERRIDE;
virtual void setItemConfirmationTime(std::weak_ptr<STKPeer> peer,
int ticks) OVERRIDE;
virtual void collectedItem(Item *item, AbstractKart *kart) OVERRIDE;

View File

@ -322,6 +322,11 @@ void ClientLobby::update(int ticks)
case PLAYING:
break;
case RACE_FINISHED:
if (!RaceEventManager::getInstance()->protocolStopped() ||
!GameProtocol::emptyInstance())
return;
if (!m_received_server_result)
m_received_server_result = true;
break;
case DONE:
m_state.store(EXITING);
@ -762,16 +767,9 @@ void ClientLobby::raceFinished(Event* event)
// stop race protocols
RaceEventManager::getInstance()->stop();
RaceEventManager::getInstance()->getProtocol()->requestTerminate();
GameProtocol::lock()->requestTerminate();
while (!RaceEventManager::getInstance()->protocolStopped())
StkTime::sleep(1);
while (!GameProtocol::emptyInstance())
StkTime::sleep(1);
m_received_server_result = true;
m_state.store(RACE_FINISHED);
} // raceFinished
//-----------------------------------------------------------------------------

View File

@ -99,6 +99,9 @@ ServerLobby::ServerLobby() : LobbyProtocol(NULL)
"STK addons server, don't bother host one if you don't have the "
"corresponding permission, they will be rejected if so.");
}
m_result_ns = getNetworkString();
m_result_ns->setSynchronous(true);
m_waiting_for_reset = false;
} // ServerLobby
//-----------------------------------------------------------------------------
@ -110,6 +113,7 @@ ServerLobby::~ServerLobby()
{
unregisterServer(true/*now*/);
}
delete m_result_ns;
} // ~ServerLobby
//-----------------------------------------------------------------------------
@ -142,6 +146,8 @@ void ServerLobby::setup()
m_peers_votes.clear();
m_server_delay = 0.0;
m_timeout.store(std::numeric_limits<float>::max());
m_waiting_for_reset = false;
Log::info("ServerLobby", "Reset server to initial state.");
} // setup
@ -426,6 +432,20 @@ void ServerLobby::asynchronousUpdate()
void ServerLobby::update(int ticks)
{
// Reset server to initial state if no more connected players
if (m_waiting_for_reset)
{
if (!RaceEventManager::getInstance()->protocolStopped() ||
!GameProtocol::emptyInstance())
return;
RaceResultGUI::getInstance()->backToLobby();
std::lock_guard<std::mutex> lock(m_connection_mutex);
m_game_setup->stopGrandPrix();
m_state = NetworkConfig::get()->isLAN() ?
ACCEPTING_CLIENTS : REGISTER_SELF_ADDRESS;
setup();
}
if ((m_state.load() > ACCEPTING_CLIENTS ||
m_game_setup->isGrandPrixStarted()) &&
STKHost::get()->getPeerCount() == 0 &&
@ -434,13 +454,12 @@ void ServerLobby::update(int ticks)
if (RaceEventManager::getInstance() &&
RaceEventManager::getInstance()->isRunning())
{
stopCurrentRace();
RaceEventManager::getInstance()->stop();
RaceEventManager::getInstance()->getProtocol()->requestTerminate();
GameProtocol::lock()->requestTerminate();
}
std::lock_guard<std::mutex> lock(m_connection_mutex);
m_game_setup->stopGrandPrix();
m_state = NetworkConfig::get()->isLAN() ?
ACCEPTING_CLIENTS : REGISTER_SELF_ADDRESS;
setup();
m_waiting_for_reset = true;
return;
}
// Reset for ranked server if in kart / track selection has only 1 player
@ -502,6 +521,22 @@ void ServerLobby::update(int ticks)
checkRaceFinished();
}
break;
case WAIT_FOR_RACE_STOPPED:
if (!RaceEventManager::getInstance()->protocolStopped() ||
!GameProtocol::emptyInstance())
return;
// This will go back to lobby in server (and exit the current race)
RaceResultGUI::getInstance()->backToLobby();
// Reset for next state usage
resetPeersReady();
// Set the delay before the server forces all clients to exit the race
// result screen and go back to the lobby
m_timeout.store((float)StkTime::getRealTime() + 15.0f);
m_state = RESULT_DISPLAY;
sendMessageToPeers(m_result_ns, /*reliable*/ true);
Log::info("ServerLobby", "End of game message sent");
break;
case RESULT_DISPLAY:
if (checkPeersReady() ||
StkTime::getRealTime() > m_timeout.load())
@ -811,28 +846,32 @@ void ServerLobby::checkRaceFinished()
if (!RaceEventManager::getInstance()->isRaceOver()) return;
Log::info("ServerLobby", "The game is considered finish.");
// notify the network world that it is stopped
RaceEventManager::getInstance()->stop();
// Reset for next state usage
resetPeersReady();
NetworkString* total = getNetworkString();
total->setSynchronous(true);
total->addUInt8(LE_RACE_FINISHED);
// stop race protocols before going back to lobby (end race)
RaceEventManager::getInstance()->getProtocol()->requestTerminate();
GameProtocol::lock()->requestTerminate();
// Save race result before delete the world
m_result_ns->clear();
m_result_ns->addUInt8(LE_RACE_FINISHED);
if (m_game_setup->isGrandPrix())
{
// fastest lap
int fastest_lap =
static_cast<LinearWorld*>(World::getWorld())->getFastestLapTicks();
total->addUInt32(fastest_lap);
m_result_ns->addUInt32(fastest_lap);
// all gp tracks
total->addUInt8((uint8_t)m_game_setup->getTotalGrandPrixTracks())
m_result_ns->addUInt8((uint8_t)m_game_setup->getTotalGrandPrixTracks())
.addUInt8((uint8_t)m_game_setup->getAllTracks().size());
for (const std::string& gp_track : m_game_setup->getAllTracks())
total->encodeString(gp_track);
m_result_ns->encodeString(gp_track);
// each kart score and total time
auto& players = m_game_setup->getPlayers();
total->addUInt8((uint8_t)players.size());
m_result_ns->addUInt8((uint8_t)players.size());
for (unsigned i = 0; i < players.size(); i++)
{
int last_score = race_manager->getKartScore(i);
@ -846,7 +885,7 @@ void ServerLobby::checkRaceFinished()
player->setScore(cur_score);
player->setOverallTime(overall_time);
}
total->addUInt32(last_score).addUInt32(cur_score)
m_result_ns->addUInt32(last_score).addUInt32(cur_score)
.addFloat(overall_time);
}
}
@ -854,23 +893,14 @@ void ServerLobby::checkRaceFinished()
{
int fastest_lap =
static_cast<LinearWorld*>(World::getWorld())->getFastestLapTicks();
total->addUInt32(fastest_lap);
m_result_ns->addUInt32(fastest_lap);
}
if (NetworkConfig::get()->isRankedServer())
{
computeNewRankings();
submitRankingsToAddons();
}
stopCurrentRace();
// Set the delay before the server forces all clients to exit the race
// result screen and go back to the lobby
m_timeout.store((float)StkTime::getRealTime() + 15.0f);
m_state = RESULT_DISPLAY;
sendMessageToPeers(total, /*reliable*/ true);
delete total;
Log::info("ServerLobby", "End of game message sent");
m_state.store(WAIT_FOR_RACE_STOPPED);
} // checkRaceFinished
//-----------------------------------------------------------------------------
@ -1060,27 +1090,6 @@ double ServerLobby::distributeBasePoints(uint32_t online_id)
return 0.0;
} // distributeBasePoints
//-----------------------------------------------------------------------------
/** Stop any race currently in server, should only be called in main thread.
*/
void ServerLobby::stopCurrentRace()
{
// notify the network world that it is stopped
RaceEventManager::getInstance()->stop();
// stop race protocols before going back to lobby (end race)
RaceEventManager::getInstance()->getProtocol()->requestTerminate();
GameProtocol::lock()->requestTerminate();
while (!RaceEventManager::getInstance()->protocolStopped())
StkTime::sleep(1);
while (!GameProtocol::emptyInstance())
StkTime::sleep(1);
// This will go back to lobby in server (and exit the current race)
RaceResultGUI::getInstance()->backToLobby();
} // stopCurrentRace
//-----------------------------------------------------------------------------
/** Called when a client disconnects.
* \param event The disconnect event.

View File

@ -16,6 +16,7 @@
#include <tuple>
class BareNetworkString;
class NetworkString;
class NetworkPlayerProfile;
class STKPeer;
@ -34,6 +35,7 @@ public:
WAIT_FOR_RACE_STARTED, // Wait for all clients to have started the race
DELAY_SERVER, // Additional server delay
RACING, // racing
WAIT_FOR_RACE_STOPPED, // Wait server for stopping all race protocols
RESULT_DISPLAY, // Show result screen
ERROR_LEAVE, // shutting down server
EXITING
@ -119,6 +121,10 @@ private:
/** Number of ranked races done for each current players */
std::map<uint32_t, unsigned> m_num_ranked_races;
bool m_waiting_for_reset;
NetworkString* m_result_ns;
// connection management
void clientDisconnected(Event* event);
void connectionRequested(Event* event);
@ -183,8 +189,6 @@ private:
uint32_t online_id,
const irr::core::stringw& online_name);
std::tuple<std::string, uint8_t, bool, bool> handleVote();
void stopCurrentRace();
void getRankingForPlayer(std::shared_ptr<NetworkPlayerProfile> p);
void submitRankingsToAddons();
void computeNewRankings();
@ -193,6 +197,7 @@ private:
double distributeBasePoints(uint32_t online_id);
double getModeFactor();
double getModeSpread();
void checkRaceFinished();
public:
ServerLobby();
@ -207,7 +212,6 @@ public:
void signalRaceStartToClients();
void startSelection(const Event *event=NULL);
void checkIncomingConnectionRequests();
void checkRaceFinished();
void finishedLoadingWorld() OVERRIDE;
ServerState getCurrentState() const { return m_state.load(); }
void updateBanList();

View File

@ -162,17 +162,20 @@ void RewindManager::addNetworkState(BareNetworkString *buffer, int ticks)
void RewindManager::saveState(bool local_save)
{
PROFILER_PUSH_CPU_MARKER("RewindManager - save state", 0x20, 0x7F, 0x20);
GameProtocol::lock()->startNewState(local_save);
auto gp = GameProtocol::lock();
if (!gp)
return;
gp->startNewState(local_save);
AllRewinder::const_iterator rewinder;
for (rewinder = m_all_rewinder.begin(); rewinder != m_all_rewinder.end(); ++rewinder)
{
// TODO: check if it's worth passing in a sufficiently large buffer from
// GameProtocol - this would save the copy operation.
BareNetworkString *buffer = (*rewinder)->saveState();
if (buffer && buffer->size() >= 0)
if (buffer)
{
m_overall_state_size += buffer->size();
GameProtocol::lock()->addState(buffer);
gp->addState(buffer);
} // size >= 0
delete buffer; // buffer can be freed
}
@ -186,10 +189,14 @@ void RewindManager::saveState(bool local_save)
*/
void RewindManager::saveLocalState()
{
auto gp = GameProtocol::lock();
if (!gp)
return;
int ticks = World::getWorld()->getTimeTicks();
saveState(/*local_state*/true);
NetworkString *state = GameProtocol::lock()->getState();
NetworkString *state = gp->getState();
// Copy the data to a new string, making the buffer in
// GameProtocol availble for again.
@ -208,7 +215,6 @@ void RewindManager::saveLocalState()
void RewindManager::restoreState(BareNetworkString *data)
{
data->reset();
int index = 0;
for (auto rewinder = m_all_rewinder.begin();
rewinder != m_all_rewinder.end(); ++rewinder)
@ -230,7 +236,6 @@ void RewindManager::update(int ticks_not_used)
m_all_rewinder.size() == 0 ||
m_is_rewinding) return;
float time = World::getWorld()->getTime();
int ticks = World::getWorld()->getTimeTicks();
m_not_rewound_ticks.store(ticks, std::memory_order_relaxed);
@ -245,7 +250,8 @@ void RewindManager::update(int ticks_not_used)
// Save state
saveState(/**allow_local_save*/false);
PROFILER_PUSH_CPU_MARKER("RewindManager - send state", 0x20, 0x7F, 0x40);
GameProtocol::lock()->sendState();
if (auto gp = GameProtocol::lock())
gp->sendState();
PROFILER_POP_CPU_MARKER();
m_last_saved_state = ticks;
} // update