Add initial live join handling for the one who does it

This commit is contained in:
Benau 2019-01-01 15:46:45 +08:00
parent 0431344886
commit 2f8da236e3
11 changed files with 348 additions and 18 deletions

View File

@ -193,7 +193,8 @@ bool LocalPlayerController::action(PlayerAction action, int value,
// If this is a client, send the action to networking layer
if (NetworkConfig::get()->isNetworking() &&
NetworkConfig::get()->isClient() &&
!RewindManager::get()->isRewinding())
!RewindManager::get()->isRewinding() &&
World::getWorld() && !World::getWorld()->isLiveJoinWorld())
{
if (auto gp = GameProtocol::lock())
{

View File

@ -1412,7 +1412,7 @@ void Kart::update(int ticks)
// Hover the kart above reset position before entering the game
if (m_live_join_util != 0 &&
(m_live_join_util < World::getWorld()->getTicksSinceStart() ||
(m_live_join_util > World::getWorld()->getTicksSinceStart() ||
World::getWorld()->isLiveJoinWorld()))
{
btRigidBody *body = getBody();

View File

@ -326,6 +326,8 @@ public:
unsigned int getCurrentNumPlayers() const { return m_num_players -
m_eliminated_players;}
// ------------------------------------------------------------------------
virtual void addReservedKart(int kart_id) { m_eliminated_karts--; }
// ------------------------------------------------------------------------
/** The code that draws the timer should call this first to know
* whether the game mode wants a timer drawn. */
virtual bool shouldDrawTimer() const

View File

@ -275,7 +275,7 @@ void WorldStatus::updateTime(int ticks)
// Add 3 seconds delay before telling server finish loading
// world, so previous (if any) disconnected player has left
// fully
if (m_auxiliary_ticks == 360)
if (m_auxiliary_ticks == stk_config->time2Ticks(3.0f))
{
auto lobby = LobbyProtocol::get<LobbyProtocol>();
assert(lobby);
@ -545,4 +545,8 @@ void WorldStatus::endLiveJoinWorld(int ticks_now)
{
m_live_join_world = false;
m_auxiliary_ticks = 0;
m_phase = RACE_PHASE;
startEngines();
music_manager->startMusic();
setTicksForRewind(ticks_now);
} // endLiveJoinWorld

View File

@ -27,6 +27,8 @@
#include "input/device_manager.hpp"
#include "items/item_manager.hpp"
#include "items/powerup_manager.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/controller/controller.hpp"
#include "karts/kart_properties_manager.hpp"
#include "modes/linear_world.hpp"
#include "network/crypto.hpp"
@ -109,6 +111,8 @@ ClientLobby::~ClientLobby()
void ClientLobby::setup()
{
m_auto_back_to_lobby_time = std::numeric_limits<uint64_t>::max();
m_start_live_game_time = std::numeric_limits<uint64_t>::max();
m_live_join_ticks = -1;
m_received_server_result = false;
TracksScreen::getInstance()->resetVote();
LobbyProtocol::setup();
@ -155,6 +159,7 @@ bool ClientLobby::notifyEvent(Event* event)
case LE_SERVER_OWNERSHIP: becomingServerOwner(); break;
case LE_BAD_TEAM: handleBadTeam(); break;
case LE_BAD_CONNECTION: handleBadConnection(); break;
case LE_LIVE_JOIN_ACK: liveJoinAcknowledged(event); break;
default:
return false;
break;
@ -272,6 +277,22 @@ void ClientLobby::addAllPlayers(Event* event)
// Disable until render gui during loading is bug free
//StateManager::get()->enterGameState();
// Live join if state is CONNECTED
if (m_state.load() == CONNECTED)
{
World* w = World::getWorld();
w->setLiveJoinWorld(true);
for (unsigned i = 0; i < w->getNumKarts(); i++)
{
AbstractKart* k = w->getKart(i);
// The final joining ticks will be set by server later
if (k->getController()->isLocalPlayerController())
k->setLiveJoinKart(-1);
else
k->getNode()->setVisible(false);
}
}
// Switch to assign mode in case a player hasn't chosen any karts
input_manager->getDeviceManager()->setAssignMode(ASSIGN);
} // addAllPlayers
@ -374,6 +395,11 @@ void ClientLobby::update(int ticks)
break;
case REQUESTING_CONNECTION:
case CONNECTED:
if (m_start_live_game_time != std::numeric_limits<uint64_t>::max() &&
STKHost::get()->getNetworkTimer() >= m_start_live_game_time)
{
finishLiveJoin();
}
if (NetworkConfig::get()->isAutoConnect() && !m_auto_started)
{
// Send a message to the server to start
@ -931,7 +957,51 @@ void ClientLobby::exitResultScreen(Event *event)
void ClientLobby::finishedLoadingWorld()
{
NetworkString* ns = getNetworkString(1);
// Live join if state is CONNECTED
ns->setSynchronous(m_state.load() == CONNECTED);
ns->addUInt8(LE_CLIENT_LOADED_WORLD);
sendToServer(ns, true);
delete ns;
} // finishedLoadingWorld
//-----------------------------------------------------------------------------
void ClientLobby::liveJoinAcknowledged(Event* event)
{
World* w = World::getWorld();
if (!w)
return;
const NetworkString& data = event->data();
m_start_live_game_time = data.getUInt64();
powerup_manager->setRandomSeed(m_start_live_game_time);
m_start_live_game_time = data.getUInt64();
m_live_join_ticks = data.getUInt32();
for (unsigned i = 0; i < w->getNumKarts(); i++)
{
AbstractKart* k = w->getKart(i);
if (k->getController()->isLocalPlayerController())
k->setLiveJoinKart(m_live_join_ticks);
}
} // liveJoinAcknowledged
//-----------------------------------------------------------------------------
void ClientLobby::finishLiveJoin()
{
m_start_live_game_time = std::numeric_limits<uint64_t>::max();
World* w = World::getWorld();
if (!w)
return;
Log::info("ClientLobby", "Live join started at %lf",
StkTime::getRealTime());
w->setLiveJoinWorld(false);
w->endLiveJoinWorld(m_live_join_ticks);
for (unsigned i = 0; i < w->getNumKarts(); i++)
{
AbstractKart* k = w->getKart(i);
if (!k->getController()->isLocalPlayerController() &&
!k->isEliminated())
k->getNode()->setVisible(true);
}
m_state.store(RACING);
} // finishLiveJoin

View File

@ -81,6 +81,10 @@ private:
uint64_t m_auto_back_to_lobby_time;
uint64_t m_start_live_game_time;
int m_live_join_ticks;
/** The state of the finite state machine. */
std::atomic<ClientState> m_state;
@ -95,6 +99,8 @@ private:
irr::core::stringw m_total_players;
void liveJoinAcknowledged(Event* event);
void finishLiveJoin();
public:
ClientLobby(const TransportAddress& a, std::shared_ptr<Server> s);
virtual ~ClientLobby();

View File

@ -173,6 +173,9 @@ void GameProtocol::controllerAction(int kart_id, PlayerAction action,
*/
void GameProtocol::handleControllerAction(Event *event)
{
STKPeer* peer = event->getPeer();
if (NetworkConfig::get()->isServer() && peer->isWaitingForGame())
return;
NetworkString &data = event->data();
uint8_t count = data.getUInt8();
bool will_trigger_rewind = false;
@ -193,10 +196,10 @@ void GameProtocol::handleControllerAction(Event *event)
}
uint8_t kart_id = data.getUInt8();
if (NetworkConfig::get()->isServer() &&
!event->getPeer()->availableKartID(kart_id))
!peer->availableKartID(kart_id))
{
Log::warn("GameProtocol", "Wrong kart id %d from %s.",
kart_id, event->getPeer()->getAddress().toString().c_str());
kart_id, peer->getAddress().toString().c_str());
return;
}
@ -227,9 +230,9 @@ void GameProtocol::handleControllerAction(Event *event)
{
// Send update to all clients except the original sender if the event
// is after the server time
event->getPeer()->updateLastActivity();
peer->updateLastActivity();
if (!will_trigger_rewind)
STKHost::get()->sendPacketExcept(event->getPeer(), &data, false);
STKHost::get()->sendPacketExcept(peer, &data, false);
// FIXME unless there is a network jitter more than 100ms (more than
// server delay), time adjust is not necessary

View File

@ -68,7 +68,8 @@ public:
LE_BAD_CONNECTION,
LE_CONFIG_SERVER,
LE_CHANGE_HANDICAP,
LE_LIVE_JOIN
LE_LIVE_JOIN,
LE_LIVE_JOIN_ACK
};
enum RejectReason : uint8_t

View File

@ -21,6 +21,7 @@
#include "config/user_config.hpp"
#include "items/item_manager.hpp"
#include "items/powerup_manager.hpp"
#include "graphics/render_info.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/controller/player_controller.hpp"
#include "karts/kart_properties.hpp"
@ -298,6 +299,7 @@ bool ServerLobby::notifyEvent(Event* event)
{
case LE_RACE_FINISHED_ACK: playerFinishedResult(event); break;
case LE_LIVE_JOIN: liveJoinRequest(event); break;
case LE_CLIENT_LOADED_WORLD: finishedLoadingLiveJoinClient(event); break;
default: Log::error("ServerLobby", "Unknown message type %d - ignored.",
message_type);
break;
@ -647,10 +649,230 @@ NetworkString* ServerLobby::getLoadWorldMessage(
} // getLoadWorldMessage
//-----------------------------------------------------------------------------
bool ServerLobby::canLiveJoinNow() const
{
return race_manager->supportsLiveJoining() &&
World::getWorld() && RaceEventManager::getInstance()->isRunning() &&
!RaceEventManager::getInstance()->isRaceOver() &&
(World::getWorld()->getPhase() == WorldStatus::RACE_PHASE ||
World::getWorld()->getPhase() == WorldStatus::GOAL_PHASE);
} // canLiveJoinNow
//-----------------------------------------------------------------------------
/** \ref peer will be reset back to the lobby.
*/
void ServerLobby::rejectLiveJoin(STKPeer* peer)
{
NetworkString* reset = getNetworkString(1);
reset->setSynchronous(true);
reset->addUInt8(LE_EXIT_RESULT);
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;
} // rejectLiveJoin
//-----------------------------------------------------------------------------
/** This message is like kartSelectionRequested, but it will send the peer
* load world message if he can join the current started game.
*/
void ServerLobby::liveJoinRequest(Event* event)
{
STKPeer* peer = event->getPeer();
const NetworkString& data = event->data();
if (!canLiveJoinNow())
{
rejectLiveJoin(peer);
return;
}
setPlayerKarts(data, peer);
std::vector<int> used_id;
for (unsigned i = 0; i < peer->getPlayerProfiles().size(); i++)
{
int id = getReservedId(peer->getPlayerProfiles()[i], i);
if (id == -1)
break;
used_id.push_back(id);
}
if (used_id.size() != peer->getPlayerProfiles().size())
{
for (unsigned i = 0; i < peer->getPlayerProfiles().size(); i++)
peer->getPlayerProfiles()[i]->setKartName("");
for (unsigned i = 0; i < used_id.size(); i++)
{
RemoteKartInfo& rki = race_manager->getKartInfo(used_id[i]);
rki.makeReserved();
}
Log::info("ServerLobby", "Too many players (%d) try to live join",
(int)peer->getPlayerProfiles().size());
rejectLiveJoin(peer);
return;
}
peer->clearAvailableKartIDs();
for (int id : used_id)
{
Log::info("ServerLobby", "%s live joining with reserved kart id %d.",
peer->getAddress().toString().c_str(), id);
peer->addAvailableKartID(id);
}
std::vector<std::shared_ptr<NetworkPlayerProfile> > players;
for (unsigned i = 0; i < race_manager->getNumPlayers(); i++)
{
const RemoteKartInfo& rki = race_manager->getKartInfo(i);
std::shared_ptr<NetworkPlayerProfile> player =
rki.getNetworkPlayerProfile().lock();
if (!player)
{
player = NetworkPlayerProfile::getReservedProfile(
race_manager->getMinorMode() ==
RaceManager::MINOR_MODE_FREE_FOR_ALL ?
KART_TEAM_NONE : rki.getKartTeam());
}
players.push_back(player);
}
NetworkString* load_world_message = getLoadWorldMessage(players);
peer->sendPacket(load_world_message, true/*reliable*/);
delete load_world_message;
peer->updateLastActivity();
} // liveJoinRequest
//-----------------------------------------------------------------------------
/** Decide where to put the live join player depends on his team and game mode.
*/
int ServerLobby::getReservedId(std::shared_ptr<NetworkPlayerProfile>& p,
unsigned local_id) const
{
const bool is_ffa =
race_manager->getMinorMode() == RaceManager::MINOR_MODE_FREE_FOR_ALL;
int red_count = 0;
int blue_count = 0;
for (unsigned i = 0; i < race_manager->getNumPlayers(); i++)
{
RemoteKartInfo& rki = race_manager->getKartInfo(i);
if (rki.isReserved())
continue;
bool disconnected = rki.disconnected();
if (race_manager->getKartInfo(i).getKartTeam() == KART_TEAM_RED &&
!disconnected)
red_count++;
else if (race_manager->getKartInfo(i).getKartTeam() ==
KART_TEAM_BLUE && !disconnected)
blue_count++;
}
KartTeam target_team = red_count > blue_count ? KART_TEAM_BLUE :
KART_TEAM_RED;
for (unsigned i = 0; i < race_manager->getNumPlayers(); i++)
{
RemoteKartInfo& rki = race_manager->getKartInfo(i);
std::shared_ptr<NetworkPlayerProfile> player =
rki.getNetworkPlayerProfile().lock();
if (!player)
{
if (is_ffa)
{
rki.copyFrom(p, local_id);
return i;
}
if (ServerConfig::m_team_choosing)
{
if ((p->getTeam() == KART_TEAM_RED &&
rki.getKartTeam() == KART_TEAM_RED) ||
(p->getTeam() == KART_TEAM_BLUE &&
rki.getKartTeam() == KART_TEAM_BLUE))
{
rki.copyFrom(p, local_id);
return i;
}
}
else
{
if (rki.getKartTeam() == target_team)
{
p->setTeam(target_team);
rki.copyFrom(p, local_id);
return i;
}
}
}
}
return -1;
} // getReservedId
//-----------------------------------------------------------------------------
/** Finally put the kart in the world and inform client the current world
* status, (including current confirmed item state, kart scores...)
*/
void ServerLobby::finishedLoadingLiveJoinClient(Event* event)
{
STKPeer* peer = event->getPeer();
if (!canLiveJoinNow())
{
rejectLiveJoin(peer);
return;
}
bool live_joined_in_time = true;
for (const int id : peer->getAvailableKartIDs())
{
const RemoteKartInfo& rki = race_manager->getKartInfo(id);
if (rki.isReserved())
{
live_joined_in_time = false;
break;
}
}
if (!live_joined_in_time)
{
Log::warn("ServerLobby", "%s can't live-join in time.",
peer->getAddress().toString().c_str());
rejectLiveJoin(peer);
return;
}
World* w = World::getWorld();
assert(w);
// Give 3 seconds for all peers to get new kart info
const int live_join_util_ticks = w->getTicksSinceStart() +
stk_config->time2Ticks(3.0f);
uint64_t live_join_start_time = STKHost::get()->getNetworkTimer();
live_join_start_time -= m_server_delay;
live_join_start_time += 3000;
for (const int id : peer->getAvailableKartIDs())
{
const RemoteKartInfo& rki = race_manager->getKartInfo(id);
AbstractKart* k = w->getKart(id);
k->changeKart(rki.getKartName(), rki.getDifficulty(),
rki.getKartTeam() == KART_TEAM_RED ?
std::make_shared<RenderInfo>(1.0f) :
rki.getKartTeam() == KART_TEAM_BLUE ?
std::make_shared<RenderInfo>(0.66f) :
std::make_shared<RenderInfo>(rki.getDefaultKartColor()));
k->setLiveJoinKart(live_join_util_ticks);
w->addReservedKart(id);
}
Log::info("ServerLobby", "%s live-joining succeeded.",
peer->getAddress().toString().c_str());
NetworkString* ns = getNetworkString(10);
ns->setSynchronous(true);
ns->addUInt8(LE_LIVE_JOIN_ACK).addUInt64(m_client_starting_time)
.addUInt64(live_join_start_time).addUInt32(live_join_util_ticks);
peer->setWaitingForGame(false);
peer->sendPacket(ns, true/*reliable*/);
delete ns;
updatePlayerList();
peer->updateLastActivity();
} // finishedLoadingLiveJoinClient
//-----------------------------------------------------------------------------
/** Simple finite state machine. Once this
* is known, register the server and its address with the stk server so that
@ -660,8 +882,7 @@ void ServerLobby::update(int ticks)
{
World* w = World::getWorld();
int sec = ServerConfig::m_kick_idle_player_seconds;
if (NetworkConfig::get()->isWAN() &&
sec > 0 && m_state.load() >= WAIT_FOR_WORLD_LOADED &&
if (sec > 0 && m_state.load() >= WAIT_FOR_WORLD_LOADED &&
m_state.load() <= RACING && m_server_has_loaded_world.load() == true)
{
for (unsigned i = 0; i < race_manager->getNumPlayers(); i++)
@ -672,15 +893,27 @@ void ServerLobby::update(int ticks)
if (!player)
continue;
auto peer = player->getPeer();
if (peer && peer->idleForSeconds() > sec &&
!peer->isDisconnected())
if (peer && peer->idleForSeconds() > sec)
{
if (w && w->getKart(i)->hasFinishedRace())
if (w && w->getKart(i)->isEliminated())
{
// Remove loading world too long live join peer
Log::info("ServerLobby", "%s hasn't live-joined within"
" %d seconds, remove it.",
peer->getAddress().toString().c_str(), sec);
RemoteKartInfo& rki = race_manager->getKartInfo(i);
rki.makeReserved();
continue;
Log::info("ServerLobby", "%s has been idle for more than"
" %d seconds, kick.",
peer->getAddress().toString().c_str(), sec);
peer->kick();
}
if (!peer->isDisconnected() && NetworkConfig::get()->isWAN())
{
if (w && w->getKart(i)->hasFinishedRace())
continue;
Log::info("ServerLobby", "%s has been idle for more than"
" %d seconds, kick.",
peer->getAddress().toString().c_str(), sec);
peer->kick();
}
}
}
}
@ -2963,10 +3196,12 @@ void ServerLobby::handlePlayerDisconnection() const
total++;
continue;
}
else
rki.makeReserved();
AbstractKart* k = World::getWorld()->getKart(i);
if (!k->isEliminated() && !k->hasFinishedRace())
{
rki.makeReserved();
CaptureTheFlag* ctf = dynamic_cast<CaptureTheFlag*>
(World::getWorld());
if (ctf)

View File

@ -183,6 +183,7 @@ private:
void playerFinishedResult(Event *event);
bool registerServer(bool now);
void finishedLoadingWorldClient(Event *event);
void finishedLoadingLiveJoinClient(Event *event);
void kickHost(Event* event);
void changeTeam(Event* event);
void handleChat(Event* event);
@ -276,6 +277,10 @@ private:
std::vector<std::shared_ptr<NetworkPlayerProfile> >& players) const;
void setPlayerKarts(const NetworkString& ns, STKPeer* peer) const;
void liveJoinRequest(Event* event);
void rejectLiveJoin(STKPeer* peer);
bool canLiveJoinNow() const;
int getReservedId(std::shared_ptr<NetworkPlayerProfile>& p,
unsigned local_id) const;
public:
ServerLobby();
virtual ~ServerLobby();

View File

@ -207,6 +207,9 @@ public:
bool availableKartID(unsigned id)
{ return m_available_kart_ids.find(id) != m_available_kart_ids.end(); }
// ------------------------------------------------------------------------
const std::set<unsigned>& getAvailableKartIDs() const
{ return m_available_kart_ids; }
// ------------------------------------------------------------------------
void setUserVersion(const std::string& uv) { m_user_version = uv; }
// ------------------------------------------------------------------------
const std::string& getUserVersion() const { return m_user_version; }