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)
{ 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 restoreCompleteState(const BareNetworkString& buffer);

View File

@ -18,8 +18,10 @@
#include "karts/kart_rewinder.hpp"
#include "audio/sfx_manager.hpp"
#include "items/attachment.hpp"
#include "items/powerup.hpp"
#include "guiengine/message_queue.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/explosion_animation.hpp"
#include "karts/rescue_animation.hpp"
@ -65,8 +67,6 @@ void KartRewinder::reset()
m_has_server_state = false;
} // reset
// ----------------------------------------------------------------------------
/** This function is called immediately before a rewind is done and saves
* the current transform for the kart. The difference between this saved
@ -110,6 +110,13 @@ void KartRewinder::computeError()
{
const int kartid = getWorldKartId();
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,
false/*notify_of_elimination*/);
setPosition(World::getWorld()->getCurrentNumKarts() + 1);

View File

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

View File

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

View File

@ -303,6 +303,7 @@ bool ServerLobby::notifyEvent(Event* event)
case LE_LIVE_JOIN: liveJoinRequest(event); break;
case LE_CLIENT_LOADED_WORLD: finishedLoadingLiveJoinClient(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.",
message_type);
break;
@ -656,15 +657,25 @@ NetworkString* ServerLobby::getLoadWorldMessage(
} // getLoadWorldMessage
//-----------------------------------------------------------------------------
/** Returns true if server can be live joined or spectating
*/
bool ServerLobby::canLiveJoinNow() const
{
return ServerConfig::m_live_players &&
race_manager->supportsLiveJoining() &&
World::getWorld() && RaceEventManager::getInstance()->isRunning() &&
race_manager->supportsLiveJoining() && worldIsActive();
} // 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() &&
(World::getWorld()->getPhase() == WorldStatus::RACE_PHASE ||
World::getWorld()->getPhase() == WorldStatus::GOAL_PHASE);
} // canLiveJoinNow
} // worldIsActive
//-----------------------------------------------------------------------------
/** \ref STKPeer peer will be reset back to the lobby with reason
@ -701,6 +712,7 @@ void ServerLobby::liveJoinRequest(Event* event)
return;
}
bool spectator = data.getUInt8() == 1;
peer->clearAvailableKartIDs();
if (!spectator)
{
setPlayerKarts(data, peer);
@ -728,7 +740,6 @@ void ServerLobby::liveJoinRequest(Event* event)
return;
}
peer->clearAvailableKartIDs();
for (int id : used_id)
{
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
//-----------------------------------------------------------------------------
/** Tell the client \ref RemoteKartInfo of a player when some player joining
* live.
*/
void ServerLobby::handleKartInfo(Event* event)
{
World* w = World::getWorld();
@ -3380,3 +3394,56 @@ void ServerLobby::handleKartInfo(Event* event)
peer->sendPacket(ns, true/*reliable*/);
delete ns;
} // 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 rejectLiveJoin(STKPeer* peer, BackLobbyReason blr);
bool canLiveJoinNow() const;
bool worldIsActive() const;
int getReservedId(std::shared_ptr<NetworkPlayerProfile>& p,
unsigned local_id) const;
void handleKartInfo(Event* event);
void clientWantsToBackLobby(Event* event);
public:
ServerLobby();
virtual ~ServerLobby();

View File

@ -28,7 +28,9 @@
#include "io/file_manager.hpp"
#include "modes/overworld.hpp"
#include "modes/world.hpp"
#include "network/protocols/lobby_protocol.hpp"
#include "network/network_config.hpp"
#include "network/network_string.hpp"
#include "network/stk_host.hpp"
#include "race/race_manager.hpp"
#include "states_screens/help_screen_1.hpp"
@ -155,14 +157,21 @@ GUIEngine::EventPropagation
}
race_manager->exitRace();
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;
}
else if (selection == "help")
@ -187,23 +196,26 @@ GUIEngine::EventPropagation
else if (selection == "newrace")
{
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;
}
else if (selection == "endrace")
@ -224,7 +236,6 @@ GUIEngine::EventPropagation
} // processEvent
// ----------------------------------------------------------------------------
void RacePausedDialog::beforeAddingWidgets()
{
GUIEngine::RibbonWidget* choice_ribbon =
@ -238,7 +249,8 @@ void RacePausedDialog::beforeAddingWidgets()
// Disable in game menu to avoid timer desync if not racing in network
// game
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");
if (index != -1)
@ -246,8 +258,19 @@ void RacePausedDialog::beforeAddingWidgets()
index = choice_ribbon->findItemNamed("options");
if (index != -1)
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