Implement a proper end and restart race in network

This commit is contained in:
Benau 2018-04-18 15:18:55 +08:00
parent 6eaa88a3f5
commit 99a25bb837
14 changed files with 167 additions and 157 deletions

View File

@ -161,11 +161,12 @@ bool LocalPlayerController::action(PlayerAction action, int value,
NetworkConfig::get()->isClient() &&
!RewindManager::get()->isRewinding() )
{
GameProtocol::lock()
->controllerAction(m_kart->getWorldKartId(),
action, value,
if (auto gp = GameProtocol::lock())
{
gp->controllerAction(m_kart->getWorldKartId(), action, value,
m_steer_val_l, m_steer_val_r);
}
}
return PlayerController::action(action, value, /*dry_run*/false);
} // action

View File

@ -69,6 +69,7 @@
#include "modes/soccer_world.hpp"
#include "modes/world.hpp"
#include "network/network_config.hpp"
#include "network/race_event_manager.hpp"
#include "network/rewind_manager.hpp"
#include "physics/btKart.hpp"
#include "physics/btKartRaycast.hpp"
@ -863,7 +864,7 @@ void Kart::finishedRace(float time, bool from_server)
// it would trigger a race end again.
if (m_finished_race) return;
/* if(!from_server)
if (NetworkConfig::get()->isNetworking() && !from_server)
{
if (NetworkConfig::get()->isServer())
{
@ -877,7 +878,7 @@ void Kart::finishedRace(float time, bool from_server)
return;
}
} // !from_server
*/
m_finished_race = true;
m_finish_time = time;
m_controller->finishedRace(time);
@ -949,7 +950,8 @@ void Kart::setRaceResult()
else
m_race_result = false;
}
else if (this->getPosition() <= 0.5f*race_manager->getNumberOfKarts() ||
else if (this->getPosition() <= 0.5f *
World::getWorld()->getCurrentNumKarts() ||
this->getPosition() == 1)
m_race_result = true;
else
@ -957,7 +959,8 @@ void Kart::setRaceResult()
}
else
{
if (this->getPosition() <= 0.5f*race_manager->getNumberOfKarts() ||
if (this->getPosition() <= 0.5f *
World::getWorld()->getCurrentNumKarts() ||
this->getPosition() == 1)
m_race_result = true;
else

View File

@ -168,6 +168,18 @@ public:
{
return stk_config->ticks2Time(m_fastest_lap_ticks);
}
// ------------------------------------------------------------------------
/** Network use: get fastest lap in ticks */
int getFastestLapTicks() const
{
return m_fastest_lap_ticks;
}
// ------------------------------------------------------------------------
/** Network use: set fastest lap in ticks */
void setFastestLapTicks(int ticks)
{
m_fastest_lap_ticks = ticks;
}
}; // LinearWorld
#endif

View File

@ -568,7 +568,7 @@ void World::terminateRace()
// to show it in the GUI
int best_highscore_rank = -1;
std::string highscore_who = "";
if (!this->isNetworkWorld())
if (!isNetworkWorld())
{
updateHighscores(&best_highscore_rank);
}
@ -1073,7 +1073,7 @@ void World::updateTrack(int ticks)
// ----------------------------------------------------------------------------
Highscores* World::getHighscores() const
{
if(!m_use_highscores) return NULL;
if (isNetworkWorld() || !m_use_highscores) return NULL;
const Highscores::HighscoreType type = "HST_" + getIdent();

View File

@ -149,6 +149,11 @@ void WorldStatus::setClockMode(const ClockType mode, const float initial_time)
*/
void WorldStatus::enterRaceOverState()
{
// Waiting for server result info
auto cl = LobbyProtocol::get<ClientLobby>();
if (cl && !cl->receivedServerResult())
return;
// Don't enter race over if it's already race over
if (m_phase == DELAY_FINISH_PHASE || m_phase == RESULT_DISPLAY_PHASE ||
m_phase == FINISH_PHASE)

View File

@ -101,7 +101,7 @@ bool GameSetup::isGrandPrix() const
} // isGrandPrix
//-----------------------------------------------------------------------------
void GameSetup::configClientAcceptConnection(NetworkString* ns)
void GameSetup::addServerInfo(NetworkString* ns)
{
assert(NetworkConfig::get()->isServer());
ns->encodeString(NetworkConfig::get()->getServerName());
@ -127,4 +127,4 @@ void GameSetup::configClientAcceptConnection(NetworkString* ns)
ns->addUInt8((uint8_t)0);
}
ns->encodeString(NetworkConfig::get()->getMOTD());
} // configClientAcceptConnection
} // addServerInfo

View File

@ -125,7 +125,7 @@ public:
// ------------------------------------------------------------------------
void setGrandPrixTrack(int tracks_no) { m_extra_server_info = tracks_no; }
// ------------------------------------------------------------------------
void configClientAcceptConnection(NetworkString* ns);
void addServerInfo(NetworkString* ns);
// ------------------------------------------------------------------------
void loadWorld();
// ------------------------------------------------------------------------

View File

@ -24,7 +24,7 @@
#include "guiengine/message_queue.hpp"
#include "input/device_manager.hpp"
#include "karts/kart_properties_manager.hpp"
#include "modes/world_with_rank.hpp"
#include "modes/linear_world.hpp"
#include "network/event.hpp"
#include "network/game_setup.hpp"
#include "network/network_config.hpp"
@ -102,6 +102,7 @@ void ClientLobby::setAddress(const TransportAddress &address)
void ClientLobby::setup()
{
clearPlayers();
m_received_server_result = false;
TracksScreen::getInstance()->resetVote();
LobbyProtocol::setup();
m_state.store(NONE);
@ -115,6 +116,7 @@ void ClientLobby::setup()
void ClientLobby::doneWithResults()
{
NetworkString* done = getNetworkString(1);
done->setSynchronous(true);
done->addUInt8(LE_RACE_FINISHED_ACK);
sendToServer(done, /*reliable*/true);
delete done;
@ -139,6 +141,7 @@ bool ClientLobby::notifyEvent(Event* event)
case LE_UPDATE_PLAYER_LIST: updatePlayerList(event); break;
case LE_CHAT: handleChat(event); break;
case LE_CONNECTION_ACCEPTED: connectionAccepted(event); break;
case LE_SERVER_INFO: handleServerInfo(event); break;
default:
return false;
break;
@ -219,10 +222,9 @@ void ClientLobby::addAllPlayers(Event* event)
std::shared_ptr<STKPeer> peer = event->getPeerSP();
peer->cleanPlayerProfiles();
m_game_setup->update(true/*remove_disconnected_players*/);
std::vector<std::shared_ptr<NetworkPlayerProfile> > players;
unsigned player_count = data.getUInt8();
assert(m_game_setup->getPlayerCount() == 0);
for (unsigned i = 0; i < player_count; i++)
{
@ -423,8 +425,8 @@ void ClientLobby::disconnectedPlayer(Event* event)
*/
void ClientLobby::connectionAccepted(Event* event)
{
// At least 10 bytes should remain now
if (!checkDataSize(event, 10)) return;
// At least 4 bytes should remain now
if (!checkDataSize(event, 4)) return;
NetworkString &data = event->data();
STKPeer* peer = event->getPeer();
@ -439,8 +441,18 @@ void ClientLobby::connectionAccepted(Event* event)
NetworkConfig::get()->getNetworkPlayers().size());
// connection token
uint32_t token = data.getToken();
if (!peer->isClientServerTokenSet())
peer->setClientServerToken(token);
m_state.store(CONNECTED);
} // connectionAccepted
//-----------------------------------------------------------------------------
void ClientLobby::handleServerInfo(Event* event)
{
// At least 6 bytes should remain now
if (!checkDataSize(event, 6)) return;
NetworkString &data = event->data();
// Add server info
core::stringw str, each_line;
uint8_t u_data;
@ -509,8 +521,7 @@ void ClientLobby::connectionAccepted(Event* event)
if (!str.empty())
NetworkingLobby::getInstance()->addMoreServerInfo(str);
m_state.store(CONNECTED);
} // connectionAccepted
} // handleServerInfo
//-----------------------------------------------------------------------------
void ClientLobby::updatePlayerList(Event* event)
@ -682,34 +693,24 @@ void ClientLobby::startSelection(Event* event)
*/
void ClientLobby::raceFinished(Event* event)
{
if(!checkDataSize(event, 1)) return;
NetworkString &data = event->data();
Log::error("ClientLobby",
"Server notified that the race is finished.");
Log::info("ClientLobby", "Server notified that the race is finished.");
if (m_game_setup->isGrandPrix())
{
// fastest lap, and than each kart before / after grand prix points
}
else if (race_manager->modeHasLaps())
{
int t = data.getUInt32();
static_cast<LinearWorld*>(World::getWorld())->setFastestLapTicks(t);
}
// stop race protocols
auto pm = ProtocolManager::lock();
assert(pm);
pm->findAndTerminate(PROTOCOL_CONTROLLER_EVENTS);
pm->findAndTerminate(PROTOCOL_GAME_EVENTS);
// finish the race
WorldWithRank* ranked_world = (WorldWithRank*)(World::getWorld());
ranked_world->beginSetKartPositions();
ranked_world->setPhase(WorldStatus::RESULT_DISPLAY_PHASE);
int position = 1;
while(data.size()>0)
{
uint8_t kart_id = data.getUInt8();
ranked_world->setKartPosition(kart_id,position);
Log::info("ClientLobby", "Kart %d has finished #%d",
kart_id, position);
position++;
}
ranked_world->endSetKartPositions();
m_state.store(RACE_FINISHED);
ranked_world->terminateRace();
m_received_server_result = true;
} // raceFinished
//-----------------------------------------------------------------------------
@ -718,15 +719,8 @@ void ClientLobby::raceFinished(Event* event)
*/
void ClientLobby::exitResultScreen(Event *event)
{
// stop race protocols
auto pm = ProtocolManager::lock();
assert(pm);
pm->findAndTerminate(PROTOCOL_CONTROLLER_EVENTS);
pm->findAndTerminate(PROTOCOL_GAME_EVENTS);
// Will be reset to linked if connected to server, see update(float dt)
setup();
m_state.store(CONNECTED);
RaceResultGUI::getInstance()->backToLobby();
} // exitResultScreen

View File

@ -22,6 +22,7 @@ private:
void displayPlayerVote(Event* event);
void updatePlayerList(Event* event);
void handleChat(Event* event);
void handleServerInfo(Event* event);
void becomingServerOwner();
void clearPlayers();
@ -48,6 +49,8 @@ private:
std::set<std::string> m_available_karts;
std::set<std::string> m_available_tracks;
bool m_received_server_result = false;
void addAllPlayers(Event* event);
public:
@ -55,6 +58,7 @@ public:
virtual ~ClientLobby();
void setAddress(const TransportAddress &address);
void doneWithResults();
bool receivedServerResult() { return m_received_server_result; }
void startingRaceNow();
const std::set<std::string>& getAvailableKarts() const
{ return m_available_karts; }

View File

@ -326,8 +326,7 @@ void GameProtocol::rewind(BareNetworkString *buffer)
int value_r = buffer->getUInt32();
Controller *c = World::getWorld()->getKart(kart_id)->getController();
PlayerController *pc = dynamic_cast<PlayerController*>(c);
// FIXME this can be endcontroller when finishing the race
//assert(pc);
// This can be endcontroller when finishing the race
if (pc)
pc->actionFromNetwork(action, value, value_l, value_r);
} // rewind

View File

@ -43,9 +43,8 @@ public:
LE_CONNECTION_REQUESTED = 1, // a connection to the server
LE_CONNECTION_REFUSED, // Connection to server refused
LE_CONNECTION_ACCEPTED, // Connection to server accepted
LE_KART_SELECTION_UPDATE, // inform client about kart selected
LE_SERVER_INFO, // inform client about server info
LE_REQUEST_BEGIN, // begin of kart selection
LE_KART_SELECTION_REFUSED, // Client not auth. to start selection
LE_UPDATE_PLAYER_LIST, // inform client about player list update
LE_KART_SELECTION, // Player selected kart
LE_PLAYER_DISCONNECTED, // Client disconnected

View File

@ -20,7 +20,7 @@
#include "config/user_config.hpp"
#include "karts/kart_properties_manager.hpp"
#include "modes/world.hpp"
#include "modes/linear_world.hpp"
#include "network/event.hpp"
#include "network/game_setup.hpp"
#include "network/network_config.hpp"
@ -106,6 +106,7 @@ ServerLobby::~ServerLobby()
void ServerLobby::setup()
{
LobbyProtocol::setup();
StateManager::get()->resetActivePlayers();
// We use maximum 16bit unsigned limit
auto all_k = kart_properties_manager->getAllAvailableKarts();
auto all_t = track_manager->getAllTrackIdentifiers();
@ -122,7 +123,7 @@ void ServerLobby::setup()
// Initialise the data structures to detect if all clients and
// the server are ready:
m_server_has_loaded_world.store(false);
m_peers_ready.clear();
resetPeersReady();
m_peers_votes.clear();
m_server_delay = 0.0;
Log::info("ServerLobby", "Reset server to initial state.");
@ -144,6 +145,7 @@ bool ServerLobby::notifyEvent(Event* event)
switch (message_type)
{
case LE_REQUEST_BEGIN: startSelection(event); break;
case LE_RACE_FINISHED_ACK: playerFinishedResult(event); break;
default: Log::error("ServerLobby", "Unknown message type %d - ignored.",
message_type);
break;
@ -205,7 +207,6 @@ bool ServerLobby::notifyEventAsynchronous(Event* event)
case LE_CLIENT_LOADED_WORLD: finishedLoadingWorldClient(event); break;
case LE_STARTED_RACE: startedRaceOnClient(event); break;
case LE_VOTE: playerVote(event); break;
case LE_RACE_FINISHED_ACK: playerFinishedResult(event); break;
case LE_KICK_HOST: kickHost(event); break;
case LE_CHAT: handleChat(event); break;
default: break;
@ -299,10 +300,7 @@ void ServerLobby::asynchronousUpdate()
return;
m_state = WAIT_FOR_RACE_STARTED;
// Reset for next state usage
for (auto p : m_peers_ready)
{
p.second = false;
}
resetPeersReady();
signalRaceStartToClients();
break;
}
@ -352,10 +350,7 @@ void ServerLobby::asynchronousUpdate()
configRemoteKart(players);
// Reset for next state usage
for (auto p : m_peers_ready)
{
p.second = false;
}
resetPeersReady();
m_state = LOAD_WORLD;
sendMessageToPeersChangingToken(load_world);
delete load_world;
@ -367,21 +362,6 @@ void ServerLobby::asynchronousUpdate()
} // asynchronousUpdate
//-----------------------------------------------------------------------------
bool ServerLobby::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;
} // checkPeersReady
//-----------------------------------------------------------------------------
/** Simple finite state machine. Once this
* is known, register the server and its address with the stk server so that
@ -427,19 +407,9 @@ void ServerLobby::update(int ticks)
}
break;
case RESULT_DISPLAY:
if(StkTime::getRealTime() > m_timeout.load())
if (checkPeersReady() ||
StkTime::getRealTime() > m_timeout.load())
{
RaceResultGUI::getInstance()->backToLobby();
// notify the network world that it is stopped
RaceEventManager::getInstance()->stop();
// stop race protocols
auto pm = ProtocolManager::lock();
assert(pm);
pm->findAndTerminate(PROTOCOL_CONTROLLER_EVENTS);
pm->findAndTerminate(PROTOCOL_GAME_EVENTS);
setup();
m_state = NetworkConfig::get()->isLAN() ?
ACCEPTING_CLIENTS : REGISTER_SELF_ADDRESS;
// Send a notification to all clients to exit
// the race result screen
NetworkString *exit_result_screen = getNetworkString(1);
@ -448,6 +418,17 @@ void ServerLobby::update(int ticks)
sendMessageToPeersChangingToken(exit_result_screen,
/*reliable*/true);
delete exit_result_screen;
std::lock_guard<std::mutex> lock(m_connection_mutex);
m_state = NetworkConfig::get()->isLAN() ?
ACCEPTING_CLIENTS : REGISTER_SELF_ADDRESS;
updatePlayerList();
NetworkString* server_info = getNetworkString();
server_info->setSynchronous(true);
server_info->addUInt8(LE_SERVER_INFO);
m_game_setup->addServerInfo(server_info);
sendMessageToPeersChangingToken(server_info);
delete server_info;
setup();
}
break;
case ERROR_LEAVE:
@ -675,39 +656,33 @@ void ServerLobby::checkIncomingConnectionRequests()
assert(World::getWorld());
if (!RaceEventManager::getInstance()->isRaceOver()) return;
m_player_ready_counter = 0;
// 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;
// calculate karts ranks :
int num_karts = race_manager->getNumberOfKarts();
std::vector<int> karts_results;
std::vector<float> karts_times;
for (int j = 0; j < num_karts; j++)
{
float kart_time = race_manager->getKartRaceTime(j);
for (unsigned int i = 0; i < karts_times.size(); i++)
{
if (kart_time < karts_times[i])
{
karts_times.insert(karts_times.begin() + i, kart_time);
karts_results.insert(karts_results.begin() + i, j);
break;
}
}
}
NetworkString *total = getNetworkString(1 + karts_results.size());
// Reset for next state usage
resetPeersReady();
NetworkString* total = getNetworkString();
total->setSynchronous(true);
total->addUInt8(LE_RACE_FINISHED);
for (unsigned int i = 0; i < karts_results.size(); i++)
if (m_game_setup->isGrandPrix())
{
total->addUInt8(karts_results[i]); // kart pos = i+1
Log::info("ServerLobby", "Kart %d finished #%d",
karts_results[i], i + 1);
// fastest lap, and than each kart before / after grand prix points
}
else if (race_manager->modeHasLaps())
{
int fastest_lap =
static_cast<LinearWorld*>(World::getWorld())->getFastestLapTicks();
total->addUInt32(fastest_lap);
}
RaceResultGUI::getInstance()->backToLobby();
// notify the network world that it is stopped
RaceEventManager::getInstance()->stop();
// stop race protocols
auto pm = ProtocolManager::lock();
assert(pm);
pm->findAndTerminate(PROTOCOL_CONTROLLER_EVENTS);
pm->findAndTerminate(PROTOCOL_GAME_EVENTS);
// 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;
sendMessageToPeersChangingToken(total, /*reliable*/ true);
delete total;
Log::info("ServerLobby", "End of game message sent");
@ -943,10 +918,16 @@ void ServerLobby::connectionRequested(Event* event)
message_ack->setSynchronous(true);
// connection success -- return the host id of peer
message_ack->addUInt8(LE_CONNECTION_ACCEPTED).addUInt32(peer->getHostId());
m_game_setup->configClientAcceptConnection(message_ack);
peer->sendPacket(message_ack);
delete message_ack;
NetworkString* server_info = getNetworkString();
server_info->setSynchronous(true);
server_info->addUInt8(LE_SERVER_INFO);
m_game_setup->addServerInfo(server_info);
peer->sendPacket(server_info);
delete server_info;
m_peers_ready[peer] = false;
for (std::shared_ptr<NetworkPlayerProfile> npp : peer->getPlayerProfiles())
{
@ -961,7 +942,7 @@ void ServerLobby::connectionRequested(Event* event)
//-----------------------------------------------------------------------------
void ServerLobby::updatePlayerList()
{
if (m_state.load() != ACCEPTING_CLIENTS)
if (m_state.load() > ACCEPTING_CLIENTS)
return;
auto all_profiles = STKHost::get()->getAllPlayerProfiles();
NetworkString* pl = getNetworkString();
@ -1241,14 +1222,10 @@ void ServerLobby::startedRaceOnClient(Event *event)
*/
void ServerLobby::playerFinishedResult(Event *event)
{
m_player_ready_counter++;
if(m_player_ready_counter >= (int)STKHost::get()->getPeerCount())
{
// We can't trigger the world/race exit here, since this is called
// from the protocol manager thread. So instead we force the timeout
// to get triggered (which is done from the main thread):
m_timeout.store(0);
}
if (m_state.load() != RESULT_DISPLAY)
return;
std::shared_ptr<STKPeer> peer = event->getPeerSP();
m_peers_ready.at(peer) = true;
} // playerFinishedResult
//-----------------------------------------------------------------------------

View File

@ -66,9 +66,6 @@ private:
/** It indicates if this server is registered with the stk server. */
std::atomic_bool m_server_registered;
/** Counts how many players are ready to go on. */
int m_player_ready_counter;
/** Timeout counter for various state. */
std::atomic<float> m_timeout;
@ -98,7 +95,34 @@ private:
void createServerIdFile();
void updatePlayerList();
void updateServerOwner();
bool checkPeersReady() const;
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;
}
void resetPeersReady()
{
for (auto it = m_peers_ready.begin(); it != m_peers_ready.end();)
{
if (it->first.expired())
{
it = m_peers_ready.erase(it);
}
else
{
it->second = false;
it++;
}
}
}
std::tuple<std::string, uint8_t, bool> handleVote();
public:

View File

@ -166,21 +166,14 @@ void RaceResultGUI::enableAllButtons()
// If we're in a network world, change the buttons text
if (World::getWorld()->isNetworkWorld())
{
Log::info("This work was networked", "This is a network world.");
top->setVisible(false);
middle->setText(_("Continue"));
middle->setVisible(true);
middle->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
bottom->setText(_("Quit the server"));
bottom->setVisible(true);
if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_GRAND_PRIX)
{
middle->setVisible(false); // you have to wait the server to start again
bottom->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
}
return;
}
Log::info("This work was NOT networked", "This is NOT a network world.");
// If something was unlocked
// -------------------------
@ -340,14 +333,13 @@ void RaceResultGUI::eventCallback(GUIEngine::Widget* widget,
// If we're playing online :
if (World::getWorld()->isNetworkWorld())
{
StateManager::get()->popMenu();
if (name == "middle") // Continue button (return to server lobby)
{
// Signal to the server that this client is back in the lobby now.
auto clrp = LobbyProtocol::get<ClientLobby>();
if(clrp)
clrp->doneWithResults();
backToLobby();
auto cl = LobbyProtocol::get<ClientLobby>();
if (cl)
cl->doneWithResults();
getWidget("middle")->setText(_("Waiting for others"));
}
if (name == "bottom") // Quit server (return to online lan / wan menu)
{