Allow player going back to lobby without disconnecting the server

This commit is contained in:
Benau 2019-01-06 00:21:51 +08:00
parent 641b16e1c7
commit bb31d6b226
7 changed files with 175 additions and 64 deletions

View File

@ -101,6 +101,9 @@ public:
void addLiveJoinPeer(std::weak_ptr<STKPeer> peer) void addLiveJoinPeer(std::weak_ptr<STKPeer> peer)
{ m_last_confirmed_item_ticks[peer] = 0; } { m_last_confirmed_item_ticks[peer] = 0; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void erasePeerInGame(std::weak_ptr<STKPeer> peer)
{ m_last_confirmed_item_ticks.erase(peer); }
// ------------------------------------------------------------------------
void saveCompleteState(BareNetworkString* buffer) const; void saveCompleteState(BareNetworkString* buffer) const;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void restoreCompleteState(const BareNetworkString& buffer); void restoreCompleteState(const BareNetworkString& buffer);

View File

@ -18,8 +18,10 @@
#include "karts/kart_rewinder.hpp" #include "karts/kart_rewinder.hpp"
#include "audio/sfx_manager.hpp"
#include "items/attachment.hpp" #include "items/attachment.hpp"
#include "items/powerup.hpp" #include "items/powerup.hpp"
#include "guiengine/message_queue.hpp"
#include "karts/abstract_kart.hpp" #include "karts/abstract_kart.hpp"
#include "karts/explosion_animation.hpp" #include "karts/explosion_animation.hpp"
#include "karts/rescue_animation.hpp" #include "karts/rescue_animation.hpp"
@ -65,8 +67,6 @@ void KartRewinder::reset()
m_has_server_state = false; m_has_server_state = false;
} // reset } // reset
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** This function is called immediately before a rewind is done and saves /** This function is called immediately before a rewind is done and saves
* the current transform for the kart. The difference between this saved * the current transform for the kart. The difference between this saved
@ -110,6 +110,13 @@ void KartRewinder::computeError()
{ {
const int kartid = getWorldKartId(); const int kartid = getWorldKartId();
Log::debug("KartRewinder", "Kart id %d disconnected.", kartid); Log::debug("KartRewinder", "Kart id %d disconnected.", kartid);
SFXManager::get()->quickSound("appear");
core::stringw player_name = getController()->getName();
// I18N: Message shown in game to tell player left the game in network
core::stringw msg = _("%s left the game.", player_name);
MessageQueue::add(MessageQueue::MT_FRIEND, msg);
World::getWorld()->eliminateKart(kartid, World::getWorld()->eliminateKart(kartid,
false/*notify_of_elimination*/); false/*notify_of_elimination*/);
setPosition(World::getWorld()->getCurrentNumKarts() + 1); setPosition(World::getWorld()->getCurrentNumKarts() + 1);

View File

@ -508,14 +508,18 @@ void ClientLobby::disconnectedPlayer(Event* event)
if (!checkDataSize(event, 1)) return; if (!checkDataSize(event, 1)) return;
NetworkString &data = event->data(); NetworkString &data = event->data();
SFXManager::get()->quickSound("appear");
unsigned disconnected_player_count = data.getUInt8(); unsigned disconnected_player_count = data.getUInt8();
uint32_t host_id = data.getUInt32(); uint32_t host_id = data.getUInt32();
m_peers_votes.erase(host_id); m_peers_votes.erase(host_id);
// If world exists the kart rewinder will know which player disconnects
if (!World::getWorld())
SFXManager::get()->quickSound("appear");
for (unsigned i = 0; i < disconnected_player_count; i++) for (unsigned i = 0; i < disconnected_player_count; i++)
{ {
std::string name; std::string name;
data.decodeString(&name); data.decodeString(&name);
if (!World::getWorld())
continue;
core::stringw player_name = StringUtils::utf8ToWide(name); core::stringw player_name = StringUtils::utf8ToWide(name);
core::stringw msg = _("%s disconnected.", player_name); core::stringw msg = _("%s disconnected.", player_name);
// Use the friend icon to avoid an error-like message // Use the friend icon to avoid an error-like message
@ -961,10 +965,14 @@ void ClientLobby::backToLobby(Event *event)
m_auto_started = false; m_auto_started = false;
m_state.store(CONNECTED); m_state.store(CONNECTED);
RaceEventManager::getInstance()->stop(); if (RaceEventManager::getInstance())
auto gep = RaceEventManager::getInstance()->getProtocol(); {
if (gep) RaceEventManager::getInstance()->stop();
gep->requestTerminate(); auto gep = RaceEventManager::getInstance()->getProtocol();
// Game events protocol is main thread event only
if (gep)
gep->requestTerminate();
}
auto gp = GameProtocol::lock(); auto gp = GameProtocol::lock();
if (gp) if (gp)
{ {

View File

@ -46,33 +46,34 @@ public:
/** Lists all lobby events (LE). */ /** Lists all lobby events (LE). */
enum : uint8_t enum : uint8_t
{ {
LE_CONNECTION_REQUESTED = 1, // a connection to the server LE_CONNECTION_REQUESTED = 1, // a connection to the server
LE_CONNECTION_REFUSED, // Connection to server refused LE_CONNECTION_REFUSED, // Connection to server refused
LE_CONNECTION_ACCEPTED, // Connection to server accepted LE_CONNECTION_ACCEPTED, // Connection to server accepted
LE_SERVER_INFO, // inform client about server info LE_SERVER_INFO, // inform client about server info
LE_REQUEST_BEGIN, // begin of kart selection LE_REQUEST_BEGIN, // begin of kart selection
LE_UPDATE_PLAYER_LIST, // inform client about player list update LE_UPDATE_PLAYER_LIST, // inform client about player list update
LE_KART_SELECTION, // Player selected kart LE_KART_SELECTION, // Player selected kart
LE_PLAYER_DISCONNECTED, // Client disconnected LE_PLAYER_DISCONNECTED, // Client disconnected
LE_CLIENT_LOADED_WORLD, // Client finished loading world LE_CLIENT_LOADED_WORLD, // Client finished loading world
LE_LOAD_WORLD, // Clients should load world LE_LOAD_WORLD, // Clients should load world
LE_START_RACE, // Server to client to start race LE_START_RACE, // Server to client to start race
LE_START_SELECTION, // inform client to start selection LE_START_SELECTION, // inform client to start selection
LE_RACE_FINISHED, // race has finished, display result LE_RACE_FINISHED, // race has finished, display result
LE_RACE_FINISHED_ACK, // client went back to lobby LE_RACE_FINISHED_ACK, // client went back to lobby
LE_BACK_LOBBY, // Force clients to go back to lobby LE_BACK_LOBBY, // Force clients to go back to lobby
LE_VOTE, // Track vote LE_VOTE, // Track vote
LE_CHAT, LE_CHAT, // Client chat message
LE_SERVER_OWNERSHIP, LE_SERVER_OWNERSHIP, // Tell client he is now the server owner
LE_KICK_HOST, LE_KICK_HOST, // Server owner kicks some other peer in game
LE_CHANGE_TEAM, LE_CHANGE_TEAM, // Client wants to change his team
LE_BAD_TEAM, LE_BAD_TEAM, // Tell server owner that the team is unbalanced
LE_BAD_CONNECTION, LE_BAD_CONNECTION, // High ping or too many packets loss
LE_CONFIG_SERVER, LE_CONFIG_SERVER, // Server owner config server game mode or difficulty
LE_CHANGE_HANDICAP, LE_CHANGE_HANDICAP, // Client changes handicap
LE_LIVE_JOIN, LE_LIVE_JOIN, // Client live join or spectate
LE_LIVE_JOIN_ACK, LE_LIVE_JOIN_ACK, // Server tell client live join or spectate succeed
LE_KART_INFO LE_KART_INFO, // Client or server exchange new kart info
LE_CLIENT_BACK_LOBBY // Client tell server to go back lobby
}; };
enum RejectReason : uint8_t enum RejectReason : uint8_t

View File

@ -303,6 +303,7 @@ bool ServerLobby::notifyEvent(Event* event)
case LE_LIVE_JOIN: liveJoinRequest(event); break; case LE_LIVE_JOIN: liveJoinRequest(event); break;
case LE_CLIENT_LOADED_WORLD: finishedLoadingLiveJoinClient(event); break; case LE_CLIENT_LOADED_WORLD: finishedLoadingLiveJoinClient(event); break;
case LE_KART_INFO: handleKartInfo(event); break; case LE_KART_INFO: handleKartInfo(event); break;
case LE_CLIENT_BACK_LOBBY: clientWantsToBackLobby(event); break;
default: Log::error("ServerLobby", "Unknown message type %d - ignored.", default: Log::error("ServerLobby", "Unknown message type %d - ignored.",
message_type); message_type);
break; break;
@ -656,15 +657,25 @@ NetworkString* ServerLobby::getLoadWorldMessage(
} // getLoadWorldMessage } // getLoadWorldMessage
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** Returns true if server can be live joined or spectating
*/
bool ServerLobby::canLiveJoinNow() const bool ServerLobby::canLiveJoinNow() const
{ {
return ServerConfig::m_live_players && return ServerConfig::m_live_players &&
race_manager->supportsLiveJoining() && race_manager->supportsLiveJoining() && worldIsActive();
World::getWorld() && RaceEventManager::getInstance()->isRunning() && } // canLiveJoinNow
//-----------------------------------------------------------------------------
/** Returns true if world is active for clients to live join, spectate or
* going back to lobby live
*/
bool ServerLobby::worldIsActive() const
{
return World::getWorld() && RaceEventManager::getInstance()->isRunning() &&
!RaceEventManager::getInstance()->isRaceOver() && !RaceEventManager::getInstance()->isRaceOver() &&
(World::getWorld()->getPhase() == WorldStatus::RACE_PHASE || (World::getWorld()->getPhase() == WorldStatus::RACE_PHASE ||
World::getWorld()->getPhase() == WorldStatus::GOAL_PHASE); World::getWorld()->getPhase() == WorldStatus::GOAL_PHASE);
} // canLiveJoinNow } // worldIsActive
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** \ref STKPeer peer will be reset back to the lobby with reason /** \ref STKPeer peer will be reset back to the lobby with reason
@ -701,6 +712,7 @@ void ServerLobby::liveJoinRequest(Event* event)
return; return;
} }
bool spectator = data.getUInt8() == 1; bool spectator = data.getUInt8() == 1;
peer->clearAvailableKartIDs();
if (!spectator) if (!spectator)
{ {
setPlayerKarts(data, peer); setPlayerKarts(data, peer);
@ -728,7 +740,6 @@ void ServerLobby::liveJoinRequest(Event* event)
return; return;
} }
peer->clearAvailableKartIDs();
for (int id : used_id) for (int id : used_id)
{ {
Log::info("ServerLobby", "%s live joining with reserved kart id %d.", Log::info("ServerLobby", "%s live joining with reserved kart id %d.",
@ -3352,6 +3363,9 @@ void ServerLobby::setPlayerKarts(const NetworkString& ns, STKPeer* peer) const
} // setPlayerKarts } // setPlayerKarts
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** Tell the client \ref RemoteKartInfo of a player when some player joining
* live.
*/
void ServerLobby::handleKartInfo(Event* event) void ServerLobby::handleKartInfo(Event* event)
{ {
World* w = World::getWorld(); World* w = World::getWorld();
@ -3380,3 +3394,56 @@ void ServerLobby::handleKartInfo(Event* event)
peer->sendPacket(ns, true/*reliable*/); peer->sendPacket(ns, true/*reliable*/);
delete ns; delete ns;
} // handleKartInfo } // handleKartInfo
//-----------------------------------------------------------------------------
/** Client if currently in-game (including spectator) wants to go back to
* lobby.
*/
void ServerLobby::clientWantsToBackLobby(Event* event)
{
World* w = World::getWorld();
std::shared_ptr<STKPeer> peer = event->getPeerSP();
if (!w || !worldIsActive() || peer->isWaitingForGame())
{
Log::warn("ServerLobby", "%s try to leave the game at wrong time.",
peer->getAddress().toString().c_str());
return;
}
for (const int id : peer->getAvailableKartIDs())
{
RemoteKartInfo& rki = race_manager->getKartInfo(id);
if (rki.getHostId() == peer->getHostId())
{
Log::info("ServerLobby", "%s left the game with kart id %d.",
peer->getAddress().toString().c_str(), id);
rki.setNetworkPlayerProfile(
std::shared_ptr<NetworkPlayerProfile>());
}
else
{
Log::error("ServerLobby", "%s doesn't exist anymore in server.",
peer->getAddress().toString().c_str());
}
}
NetworkItemManager* nim =
dynamic_cast<NetworkItemManager*>(ItemManager::get());
assert(nim);
nim->erasePeerInGame(peer);
m_peers_ready.erase(peer);
peer->setWaitingForGame(true);
NetworkString* reset = getNetworkString(2);
reset->setSynchronous(true);
reset->addUInt8(LE_BACK_LOBBY).addUInt8(BLR_NONE);
peer->sendPacket(reset, /*reliable*/true);
delete reset;
updatePlayerList();
NetworkString* server_info = getNetworkString();
server_info->setSynchronous(true);
server_info->addUInt8(LE_SERVER_INFO);
m_game_setup->addServerInfo(server_info);
peer->sendPacket(server_info, /*reliable*/true);
delete server_info;
} // clientWantsToBackLobby

View File

@ -279,9 +279,11 @@ private:
void liveJoinRequest(Event* event); void liveJoinRequest(Event* event);
void rejectLiveJoin(STKPeer* peer, BackLobbyReason blr); void rejectLiveJoin(STKPeer* peer, BackLobbyReason blr);
bool canLiveJoinNow() const; bool canLiveJoinNow() const;
bool worldIsActive() const;
int getReservedId(std::shared_ptr<NetworkPlayerProfile>& p, int getReservedId(std::shared_ptr<NetworkPlayerProfile>& p,
unsigned local_id) const; unsigned local_id) const;
void handleKartInfo(Event* event); void handleKartInfo(Event* event);
void clientWantsToBackLobby(Event* event);
public: public:
ServerLobby(); ServerLobby();
virtual ~ServerLobby(); virtual ~ServerLobby();

View File

@ -28,7 +28,9 @@
#include "io/file_manager.hpp" #include "io/file_manager.hpp"
#include "modes/overworld.hpp" #include "modes/overworld.hpp"
#include "modes/world.hpp" #include "modes/world.hpp"
#include "network/protocols/lobby_protocol.hpp"
#include "network/network_config.hpp" #include "network/network_config.hpp"
#include "network/network_string.hpp"
#include "network/stk_host.hpp" #include "network/stk_host.hpp"
#include "race/race_manager.hpp" #include "race/race_manager.hpp"
#include "states_screens/help_screen_1.hpp" #include "states_screens/help_screen_1.hpp"
@ -155,14 +157,21 @@ GUIEngine::EventPropagation
} }
race_manager->exitRace(); race_manager->exitRace();
race_manager->setAIKartOverride(""); race_manager->setAIKartOverride("");
StateManager::get()->resetAndGoToScreen(MainMenuScreen::getInstance());
if (race_manager->raceWasStartedFromOverworld()) if (NetworkConfig::get()->isNetworking())
{ {
OverWorld::enterOverWorld(); StateManager::get()->resetAndSetStack(
NetworkConfig::get()->getResetScreens().data());
NetworkConfig::get()->unsetNetworking();
}
else
{
StateManager::get()->resetAndGoToScreen(MainMenuScreen::getInstance());
if (race_manager->raceWasStartedFromOverworld())
{
OverWorld::enterOverWorld();
}
} }
NetworkConfig::get()->unsetNetworking();
return GUIEngine::EVENT_BLOCK; return GUIEngine::EVENT_BLOCK;
} }
else if (selection == "help") else if (selection == "help")
@ -187,23 +196,26 @@ GUIEngine::EventPropagation
else if (selection == "newrace") else if (selection == "newrace")
{ {
ModalDialog::dismiss(); ModalDialog::dismiss();
if (STKHost::existHost()) if (NetworkConfig::get()->isNetworking())
{ {
STKHost::get()->shutdown(); // back lobby
NetworkString back(PROTOCOL_LOBBY_ROOM);
back.setSynchronous(true);
back.addUInt8(LobbyProtocol::LE_CLIENT_BACK_LOBBY);
STKHost::get()->sendToServer(&back, true);
}
else
{
World::getWorld()->scheduleUnpause();
race_manager->exitRace();
Screen* new_stack[] =
{
MainMenuScreen::getInstance(),
RaceSetupScreen::getInstance(),
NULL
};
StateManager::get()->resetAndSetStack(new_stack);
} }
World::getWorld()->scheduleUnpause();
race_manager->exitRace();
Screen* new_stack[] =
{
MainMenuScreen::getInstance(),
RaceSetupScreen::getInstance(),
NULL
};
StateManager::get()->resetAndSetStack(
NetworkConfig::get()->isNetworking() ?
NetworkConfig::get()->getResetScreens().data() :
new_stack);
NetworkConfig::get()->unsetNetworking();
return GUIEngine::EVENT_BLOCK; return GUIEngine::EVENT_BLOCK;
} }
else if (selection == "endrace") else if (selection == "endrace")
@ -224,7 +236,6 @@ GUIEngine::EventPropagation
} // processEvent } // processEvent
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void RacePausedDialog::beforeAddingWidgets() void RacePausedDialog::beforeAddingWidgets()
{ {
GUIEngine::RibbonWidget* choice_ribbon = GUIEngine::RibbonWidget* choice_ribbon =
@ -238,7 +249,8 @@ void RacePausedDialog::beforeAddingWidgets()
// Disable in game menu to avoid timer desync if not racing in network // Disable in game menu to avoid timer desync if not racing in network
// game // game
if (NetworkConfig::get()->isNetworking() && if (NetworkConfig::get()->isNetworking() &&
World::getWorld()->getPhase() != WorldStatus::RACE_PHASE) !(World::getWorld()->getPhase() == WorldStatus::MUSIC_PHASE ||
World::getWorld()->getPhase() == WorldStatus::RACE_PHASE))
{ {
index = choice_ribbon->findItemNamed("help"); index = choice_ribbon->findItemNamed("help");
if (index != -1) if (index != -1)
@ -246,8 +258,19 @@ void RacePausedDialog::beforeAddingWidgets()
index = choice_ribbon->findItemNamed("options"); index = choice_ribbon->findItemNamed("options");
if (index != -1) if (index != -1)
choice_ribbon->setItemVisible(index, false); choice_ribbon->setItemVisible(index, false);
index = choice_ribbon->findItemNamed("newrace");
if (index != -1)
choice_ribbon->setItemVisible(index, false);
} }
if (NetworkConfig::get()->isNetworking())
} {
IconButtonWidget* new_race = dynamic_cast<IconButtonWidget*>
// ---------------------------------------------------------------------------- (choice_ribbon->findWidgetNamed("newrace"));
if (new_race)
{
//I18N show in race paused dialog in network to allow user to go
//back to lobby to end spectating (for example)
new_race->setText(_("Back to lobby"));
}
}
} // beforeAddingWidgets