1 //
2 //  SuperTuxKart - a fun racing game with go-kart
3 //  Copyright (C) 2013-2015 SuperTuxKart-Team
4 //
5 //  This program is free software; you can redistribute it and/or
6 //  modify it under the terms of the GNU General Public License
7 //  as published by the Free Software Foundation; either version 3
8 //  of the License, or (at your option) any later version.
9 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program; if not, write to the Free Software
17 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 #include "network/protocols/client_lobby.hpp"
20 
21 #include "addons/addons_manager.hpp"
22 #include "audio/music_manager.hpp"
23 #include "audio/sfx_manager.hpp"
24 #include "config/user_config.hpp"
25 #include "config/player_manager.hpp"
26 #include "graphics/camera.hpp"
27 #include "guiengine/engine.hpp"
28 #include "guiengine/modaldialog.hpp"
29 #include "guiengine/message_queue.hpp"
30 #include "guiengine/screen_keyboard.hpp"
31 #include "input/device_manager.hpp"
32 #include "input/input_device.hpp"
33 #include "items/network_item_manager.hpp"
34 #include "items/powerup_manager.hpp"
35 #include "karts/abstract_kart.hpp"
36 #include "karts/controller/controller.hpp"
37 #include "karts/kart_properties.hpp"
38 #include "karts/kart_properties_manager.hpp"
39 #include "modes/linear_world.hpp"
40 #include "network/crypto.hpp"
41 #include "network/event.hpp"
42 #include "network/game_setup.hpp"
43 #include "network/network_config.hpp"
44 #include "network/network_player_profile.hpp"
45 #include "network/network_timer_synchronizer.hpp"
46 #include "network/peer_vote.hpp"
47 #include "network/protocols/connect_to_server.hpp"
48 #include "network/protocols/game_protocol.hpp"
49 #include "network/protocols/game_events_protocol.hpp"
50 #include "network/protocols/server_lobby.hpp"
51 #include "network/protocol_manager.hpp"
52 #include "network/race_event_manager.hpp"
53 #include "network/server.hpp"
54 #include "network/server_config.hpp"
55 #include "network/stk_host.hpp"
56 #include "network/stk_peer.hpp"
57 #include "online/online_profile.hpp"
58 #include "online/xml_request.hpp"
59 #include "states_screens/dialogs/addons_pack.hpp"
60 #include "states_screens/online/networking_lobby.hpp"
61 #include "states_screens/online/network_kart_selection.hpp"
62 #include "states_screens/online/tracks_screen.hpp"
63 #include "tracks/track.hpp"
64 #include "tracks/track_manager.hpp"
65 #include "utils/log.hpp"
66 #include "utils/string_utils.hpp"
67 #include "utils/translation.hpp"
68 
69 #include <algorithm>
70 #include <cstdlib>
71 
72 // ============================================================================
73 /** The protocol that manages starting a race with the server. It uses a
74  *  finite state machine:
75 \dot
76 digraph interaction {
77 "NONE" -> "LINKED" [label="ENet connection with server established"]
78 "LINKED" -> "REQUESTING_CONNECTION" [label="Request connection from server"]
79 "REQUESTING_CONNECTION" -> CONNECTED [label="Connection accepted by server"]
80 "REQUESTING_CONNECTION" -> "?? TO BE DONE ??" [label="Connection refused by server"]
81 "CONNECTED" -> "KART_SELECTION" [label="Server tells us to start kart selection"]
82 "KART_SELECTION" -> "SELECTING_KARTS" [label="Show kart selection screen"]
83 "SELECTING_KARTS" -> "RACING" [label="Server sends start race message"]
84 }
85 \enddot
86 Note that some states are actually managed outside of the client lobby. For
87 example to select race details after selecting a kart is managed by the GUI
88 engine.
89 
90 */
91 
ClientLobby(std::shared_ptr<Server> s)92 ClientLobby::ClientLobby(std::shared_ptr<Server> s)
93            : LobbyProtocol()
94 {
95     m_auto_started = false;
96     m_waiting_for_game = false;
97     m_server_auto_game_time = false;
98     m_received_server_result = false;
99     m_state.store(NONE);
100     m_server = s;
101     setHandleDisconnections(true);
102     m_disconnected_msg[PDI_TIMEOUT] = _("Server connection timed out.");
103     m_disconnected_msg[PDI_NORMAL] = _("Server has been shut down.");
104     m_disconnected_msg[PDI_KICK] = _("You were kicked from the server.");
105     m_disconnected_msg[PDI_KICK_HIGH_PING] =
106         _("You were kicked: Ping too high.");
107     m_first_connect = true;
108     m_spectator = false;
109     m_server_live_joinable = false;
110     m_server_send_live_load_world = false;
111     m_server_enabled_chat = true;
112     m_server_enabled_track_voting = true;
113     m_server_enabled_report_player = false;
114 }   // ClientLobby
115 
116 //-----------------------------------------------------------------------------
~ClientLobby()117 ClientLobby::~ClientLobby()
118 {
119     if (m_server->supportsEncryption())
120     {
121         auto request = std::make_shared<Online::XMLRequest>();
122         NetworkConfig::get()->setServerDetails(request,
123             "clear-user-joined-server");
124         request->queue();
125     }
126 }   // ClientLobby
127 
128 //-----------------------------------------------------------------------------
setup()129 void ClientLobby::setup()
130 {
131     m_ranking_changes.clear();
132     m_spectator = false;
133     m_server_send_live_load_world = false;
134     m_auto_back_to_lobby_time = std::numeric_limits<uint64_t>::max();
135     m_start_live_game_time = std::numeric_limits<uint64_t>::max();
136     m_received_server_result = false;
137     if (!GUIEngine::isNoGraphics())
138         TracksScreen::getInstance()->resetVote();
139     LobbyProtocol::setup();
140     // The client lobby is only created when connected to server
141     m_state.store(LINKED);
142 }   // setup
143 
144 //-----------------------------------------------------------------------------
145 /** Called from the gui when a client clicked on 'continue' on the race result
146  *  screen. It notifies the server that this client has exited the screen and
147  *  is back at the lobby.
148  */
doneWithResults()149 void ClientLobby::doneWithResults()
150 {
151     NetworkString* done = getNetworkString(1);
152     done->setSynchronous(true);
153     done->addUInt8(LE_RACE_FINISHED_ACK);
154     sendToServer(done, /*reliable*/true);
155     delete done;
156 }   // doneWithResults
157 
158 //-----------------------------------------------------------------------------
notifyEvent(Event * event)159 bool ClientLobby::notifyEvent(Event* event)
160 {
161     assert(m_game_setup); // assert that the setup exists
162     if (event->getType() != EVENT_TYPE_MESSAGE)
163         return true;
164 
165     NetworkString &data = event->data();
166     assert(data.size()); // assert that data isn't empty
167     uint8_t message_type = data.getUInt8();
168     Log::info("ClientLobby", "Synchronous message of type %d",
169               message_type);
170     switch(message_type)
171     {
172         case LE_START_SELECTION:       startSelection(event);      break;
173         case LE_LOAD_WORLD:            addAllPlayers(event);       break;
174         case LE_RACE_FINISHED:         raceFinished(event);        break;
175         case LE_BACK_LOBBY:            backToLobby(event);         break;
176         case LE_UPDATE_PLAYER_LIST:    updatePlayerList(event);    break;
177         case LE_CHAT:                  handleChat(event);          break;
178         case LE_CONNECTION_ACCEPTED:   connectionAccepted(event);  break;
179         case LE_SERVER_INFO:           handleServerInfo(event);    break;
180         case LE_PLAYER_DISCONNECTED :  disconnectedPlayer(event);  break;
181         case LE_CONNECTION_REFUSED:    connectionRefused(event);   break;
182         case LE_VOTE:                  receivePlayerVote(event);   break;
183         case LE_SERVER_OWNERSHIP:      becomingServerOwner();      break;
184         case LE_BAD_TEAM:              handleBadTeam();            break;
185         case LE_BAD_CONNECTION:        handleBadConnection();      break;
186         case LE_LIVE_JOIN_ACK:        liveJoinAcknowledged(event); break;
187         case LE_KART_INFO:             handleKartInfo(event);      break;
188         case LE_START_RACE:            startGame(event);           break;
189         case LE_REPORT_PLAYER:         reportSuccess(event);       break;
190         default:
191             break;
192     }   // switch
193     return true;
194 }   // notifyEvent
195 
196 //-----------------------------------------------------------------------------
notifyEventAsynchronous(Event * event)197 bool ClientLobby::notifyEventAsynchronous(Event* event)
198 {
199     assert(m_game_setup); // assert that the setup exists
200     if (event->getType() == EVENT_TYPE_MESSAGE)
201     {
202         NetworkString &data = event->data();
203         assert(data.size()); // assert that data isn't empty
204         uint8_t message_type = data.getUInt8();
205 
206         Log::info("ClientLobby", "Asynchronous message of type %d",
207                   message_type);
208         switch(message_type)
209         {
210             default:                                                     break;
211         }   // switch
212     } // message
213     else if (event->getType() == EVENT_TYPE_DISCONNECTED)
214     {
215         // This means we left essentially.
216         // We can't delete STKHost from this thread, since the main
217         // thread might still test if STKHost exists and then call
218         // the ProtocolManager, which might already have been deleted.
219         // So only signal that STKHost should exit, which will be tested
220         // from the main thread.
221         STKHost::get()->disconnectAllPeers(false/*timeout_waiting*/);
222         STKHost::get()->setErrorMessage(
223             m_disconnected_msg.at(event->getPeerDisconnectInfo()));
224         STKHost::get()->requestShutdown();
225     } // disconnection
226     return true;
227 }   // notifyEventAsynchronous
228 
229 //-----------------------------------------------------------------------------
addAllPlayers(Event * event)230 void ClientLobby::addAllPlayers(Event* event)
231 {
232     if (World::getWorld())
233     {
234         Log::warn("ClientLobby", "World already loaded.");
235         return;
236     }
237 
238     // In case the user opened a user info dialog
239     GUIEngine::ModalDialog::dismiss();
240     GUIEngine::ScreenKeyboard::dismiss();
241 
242     if (!checkDataSize(event, 1))
243     {
244         // If recieved invalid message for players leave now
245         STKHost::get()->disconnectAllPeers(false/*timeout_waiting*/);
246         STKHost::get()->requestShutdown();
247         return;
248     }
249     // Timeout is too slow to synchronize, force it to stop and set current
250     // time
251     if (!STKHost::get()->getNetworkTimerSynchronizer()->isSynchronised())
252     {
253         if (ServerConfig::m_voting_timeout >= 10.0f)
254         {
255             core::stringw msg = _("Bad network connection is detected.");
256             MessageQueue::add(MessageQueue::MT_ERROR, msg);
257             Log::warn("ClientLobby", "Failed to synchronize timer before game "
258                 "start, maybe you enter the game too quick? (at least 5 "
259                 "seconds are required for synchronization.");
260         }
261         STKHost::get()->getNetworkTimerSynchronizer()->enableForceSetTimer();
262     }
263 
264     NetworkString& data = event->data();
265     uint32_t winner_peer_id = data.getUInt32();
266     PeerVote winner_vote(data);
267 
268     m_game_setup->setRace(winner_vote);
269     if (!GUIEngine::isNoGraphics())
270         TracksScreen::getInstance()->setResult(winner_peer_id, winner_vote);
271 
272     std::shared_ptr<STKPeer> peer = event->getPeerSP();
273     peer->cleanPlayerProfiles();
274     m_server_send_live_load_world = data.getUInt8() == 1;
275 
276     bool is_specator = true;
277     std::vector<std::shared_ptr<NetworkPlayerProfile> > players =
278         decodePlayers(data, peer, &is_specator);
279     setSpectator(is_specator);
280 
281     uint32_t random_seed = data.getUInt32();
282     ItemManager::updateRandomSeed(random_seed);
283     if (RaceManager::get()->isBattleMode())
284     {
285         int hit_capture_limit = data.getUInt32();
286         float time_limit = data.getFloat();
287         m_game_setup->setHitCaptureTime(hit_capture_limit, time_limit);
288         uint16_t flag_return_timeout = data.getUInt16();
289         RaceManager::get()->setFlagReturnTicks(flag_return_timeout);
290         unsigned flag_deactivated_time = data.getUInt16();
291         RaceManager::get()->setFlagDeactivatedTicks(flag_deactivated_time);
292     }
293     configRemoteKart(players, isSpectator() ? 1 :
294         (int)NetworkConfig::get()->getNetworkPlayers().size());
295     loadWorld();
296     // Disable until render gui during loading is bug free
297     //StateManager::get()->enterGameState();
298 
299     if (m_server_send_live_load_world)
300     {
301         World* w = World::getWorld();
302         w->setLiveJoinWorld(true);
303         Camera* cam = Camera::getCamera(0);
304         for (unsigned i = 0; i < w->getNumKarts(); i++)
305         {
306             AbstractKart* k = w->getKart(i);
307             // Change spectating target to first non-eliminated kart
308             if (isSpectator() && cam && !k->isEliminated())
309             {
310                 cam->setKart(k);
311                 cam = NULL;
312             }
313             // The final joining ticks will be set by server later
314             if (k->getController()->isLocalPlayerController())
315                 k->setLiveJoinKart(std::numeric_limits<int>::max());
316             else
317                 k->getNode()->setVisible(false);
318         }
319     }
320 
321     // Switch to assign mode in case a player hasn't chosen any karts
322     input_manager->getDeviceManager()->setAssignMode(ASSIGN);
323 }   // addAllPlayers
324 
325 //-----------------------------------------------------------------------------
326 /* Get list of players from server and see if we are spectating it. */
327 std::vector<std::shared_ptr<NetworkPlayerProfile> >
decodePlayers(const BareNetworkString & data,std::shared_ptr<STKPeer> peer,bool * is_spectator) const328   ClientLobby::decodePlayers(const BareNetworkString& data,
329                              std::shared_ptr<STKPeer> peer,
330                              bool* is_spectator) const
331 {
332     std::vector<std::shared_ptr<NetworkPlayerProfile> > players;
333     unsigned player_count = data.getUInt8();
334     for (unsigned i = 0; i < player_count; i++)
335     {
336         core::stringw player_name;
337         data.decodeStringW(&player_name);
338         uint32_t host_id = data.getUInt32();
339         float kart_color = data.getFloat();
340         uint32_t online_id = data.getUInt32();
341         HandicapLevel handicap = (HandicapLevel)data.getUInt8();
342         uint8_t local_id = data.getUInt8();
343         KartTeam team = (KartTeam)data.getUInt8();
344         std::string country_code;
345         data.decodeString(&country_code);
346         if (is_spectator && host_id == STKHost::get()->getMyHostId())
347             *is_spectator = false;
348         auto player = std::make_shared<NetworkPlayerProfile>(peer, player_name,
349             host_id, kart_color, online_id, handicap, local_id, team, country_code);
350         std::string kart_name;
351         data.decodeString(&kart_name);
352         player->setKartName(kart_name);
353         players.push_back(player);
354     }
355     return players;
356 }   // decodePlayers
357 
358 //-----------------------------------------------------------------------------
update(int ticks)359 void ClientLobby::update(int ticks)
360 {
361     switch (m_state.load())
362     {
363     case LINKED:
364     {
365         NetworkConfig::get()->clearServerCapabilities();
366         std::string ua = StringUtils::getUserAgentString();
367         if (NetworkConfig::get()->isNetworkAIInstance())
368             ua = "AI";
369         NetworkString* ns = getNetworkString();
370         ns->addUInt8(LE_CONNECTION_REQUESTED)
371             .addUInt32(ServerConfig::m_server_version).encodeString(ua)
372             .addUInt16((uint16_t)stk_config->m_network_capabilities.size());
373         for (const std::string& cap : stk_config->m_network_capabilities)
374             ns->encodeString(cap);
375 
376         getKartsTracksNetworkString(ns);
377         assert(!NetworkConfig::get()->isAddingNetworkPlayers());
378         const uint8_t player_count =
379             (uint8_t)NetworkConfig::get()->getNetworkPlayers().size();
380         ns->addUInt8(player_count);
381 
382         bool encryption = false;
383         // Make sure there is a player before calling getCurrentOnlineId
384         PlayerManager::get()->enforceCurrentPlayer();
385         uint32_t id = PlayerManager::getCurrentOnlineId();
386         bool lan_ai = !m_server->supportsEncryption() &&
387             NetworkConfig::get()->isNetworkAIInstance();
388         if (lan_ai)
389             id = 0;
390         BareNetworkString* rest = new BareNetworkString();
391         if (m_server->supportsEncryption() && id != 0)
392         {
393             ns->addUInt32(id);
394             encryption = true;
395         }
396         else
397         {
398             ns->addUInt32(id).addUInt32(0);
399             if (id != 0)
400             {
401                 ns->encodeString(
402                     PlayerManager::getCurrentOnlineProfile()->getUserName());
403             }
404         }
405 
406         rest->encodeString(ServerConfig::m_private_server_password)
407             .addUInt8(player_count);
408         for (unsigned i = 0;
409              i < NetworkConfig::get()->getNetworkPlayers().size(); i++)
410         {
411             auto& p = NetworkConfig::get()->getNetworkPlayers()[i];
412             PlayerProfile* player = std::get<1>(p);
413             core::stringw name = player->getName();
414             if (NetworkConfig::get()->isNetworkAIInstance())
415             {
416                 // I18N: Shown in lobby to indicate it's a bot in LAN game
417 #ifdef SERVER_ONLY
418                 name = L"Bot";
419 #else
420                 name = _("Bot");
421 #endif
422                 if (i > 0)
423                 {
424                     name += core::stringw(" ") + StringUtils::toWString(i);
425                 }
426             }
427             rest->encodeString(name).
428                 addFloat(player->getDefaultKartColor());
429             // Per-player handicap
430             rest->addUInt8(std::get<2>(p));
431         }
432 
433         finalizeConnectionRequest(ns, rest, encryption);
434         m_state.store(REQUESTING_CONNECTION);
435     }
436     break;
437     case RACE_FINISHED:
438         if (!RaceEventManager::get()->protocolStopped() ||
439             !GameProtocol::emptyInstance())
440             return;
441         if (!m_received_server_result)
442         {
443             m_received_server_result = true;
444             m_auto_back_to_lobby_time = StkTime::getMonoTimeMs() + 5000;
445             // In case someone opened paused race dialog or menu in network game
446             GUIEngine::ModalDialog::dismiss();
447             GUIEngine::ScreenKeyboard::dismiss();
448             if (StateManager::get()->getGameState() == GUIEngine::INGAME_MENU)
449                 StateManager::get()->enterGameState();
450             World::getWorld()->enterRaceOverState();
451         }
452         if (NetworkConfig::get()->isAutoConnect() &&
453             StkTime::getMonoTimeMs() > m_auto_back_to_lobby_time)
454         {
455             m_auto_back_to_lobby_time = std::numeric_limits<uint64_t>::max();
456             doneWithResults();
457         }
458         break;
459     case DONE:
460         m_state.store(EXITING);
461         requestTerminate();
462         break;
463     case REQUESTING_CONNECTION:
464     case CONNECTED:
465         if (m_start_live_game_time != std::numeric_limits<uint64_t>::max() &&
466             STKHost::get()->getNetworkTimer() >= m_start_live_game_time)
467         {
468             finishLiveJoin();
469         }
470         if (NetworkConfig::get()->isAutoConnect() && !m_auto_started)
471         {
472             // Send a message to the server to start
473             m_auto_started = true;
474             NetworkString start(PROTOCOL_LOBBY_ROOM);
475             start.addUInt8(LobbyProtocol::LE_REQUEST_BEGIN);
476             STKHost::get()->sendToServer(&start, true);
477         }
478     case SELECTING_ASSETS:
479     case RACING:
480     case EXITING:
481     case NONE:
482         break;
483     }
484 }   // update
485 
486 //-----------------------------------------------------------------------------
finalizeConnectionRequest(NetworkString * header,BareNetworkString * rest,bool encrypt)487 void ClientLobby::finalizeConnectionRequest(NetworkString* header,
488                                             BareNetworkString* rest,
489                                             bool encrypt)
490 {
491     if (encrypt)
492     {
493         auto crypto = Crypto::getClientCrypto();
494         Crypto::resetClientAES();
495         BareNetworkString* result = new BareNetworkString();
496         if (!crypto->encryptConnectionRequest(*rest))
497         {
498             // Failed
499             result->addUInt32(0);
500             *result += BareNetworkString(rest->getData(), rest->getTotalSize());
501             encrypt = false;
502         }
503         else
504         {
505             Log::info("ClientLobby", "Server will validate this online player.");
506             result->addUInt32(rest->getTotalSize());
507             *result += BareNetworkString(rest->getData(), rest->getTotalSize());
508         }
509         delete rest;
510         *header += *result;
511         delete result;
512         sendToServer(header);
513         delete header;
514         if (encrypt)
515         {
516             STKHost::get()->getServerPeerForClient()
517                 ->setCrypto(std::move(crypto));
518         }
519     }
520     else
521     {
522         *header += *rest;
523         delete rest;
524         sendToServer(header);
525         delete header;
526     }
527 }   // finalizeConnectionRequest
528 
529 //-----------------------------------------------------------------------------
receivePlayerVote(Event * event)530 void ClientLobby::receivePlayerVote(Event* event)
531 {
532     if (!checkDataSize(event, 4)) return;
533     // Get the player name who voted
534     NetworkString& data = event->data();
535     uint32_t host_id = data.getUInt32();
536     PeerVote vote(data);
537     Log::debug("ClientLobby",
538         "Vote from server: host %d, track %s, laps %d, reverse %d.",
539         host_id, vote.m_track_name.c_str(), vote.m_num_laps, vote.m_reverse);
540 
541     Track* track = track_manager->getTrack(vote.m_track_name);
542     if (!track)
543     {
544         Log::fatal("ClientLobby", "Missing track %s",
545             vote.m_track_name.c_str());
546     }
547     addVote(host_id, vote);
548     if (!GUIEngine::isNoGraphics())
549     {
550         TracksScreen::getInstance()->addVote(host_id, vote);
551         TracksScreen::getInstance()->updatePlayerVotes();
552     }
553 }   // receivePlayerVote
554 
555 //-----------------------------------------------------------------------------
556 /*! \brief Called when a new player is disconnected
557  *  \param event : Event providing the information.
558  *
559  *  Format of the data :
560  *  Byte 0
561  *       --------------
562  *  Size |    1       |
563  *  Data | player id *|
564  *       --------------
565  */
disconnectedPlayer(Event * event)566 void ClientLobby::disconnectedPlayer(Event* event)
567 {
568     if (!checkDataSize(event, 1)) return;
569 
570     NetworkString &data = event->data();
571     unsigned disconnected_player_count = data.getUInt8();
572     uint32_t host_id = data.getUInt32();
573     m_peers_votes.erase(host_id);
574     // If in-game world exists the kart rewinder will know which player
575     // disconnects
576     bool in_game_world = World::getWorld() &&
577         RaceEventManager::get() &&
578         RaceEventManager::get()->isRunning() &&
579         !RaceEventManager::get()->isRaceOver();
580 
581     if (!in_game_world)
582         SFXManager::get()->quickSound("appear");
583     for (unsigned i = 0; i < disconnected_player_count; i++)
584     {
585         std::string name;
586         data.decodeString(&name);
587         if (in_game_world)
588             continue;
589         core::stringw player_name = StringUtils::utf8ToWide(name);
590         core::stringw msg = _("%s disconnected.", player_name);
591         // Use the friend icon to avoid an error-like message
592         MessageQueue::add(MessageQueue::MT_FRIEND, msg);
593     }
594     if (!GUIEngine::isNoGraphics())
595     {
596         TracksScreen::getInstance()->removeVote(host_id);
597         TracksScreen::getInstance()->updatePlayerVotes();
598     }
599 }   // disconnectedPlayer
600 
601 //-----------------------------------------------------------------------------
602 /*! \brief Called when the server accepts the connection.
603  *  \param event : Event providing the information.
604  */
connectionAccepted(Event * event)605 void ClientLobby::connectionAccepted(Event* event)
606 {
607     // At least 8 bytes should remain now
608     if (!checkDataSize(event, 8)) return;
609 
610     NetworkString &data = event->data();
611     // Accepted
612     // ========
613     Log::info("ClientLobby", "The server accepted the connection.");
614 
615     static bool shown_msg = false;
616     if (!shown_msg)
617     {
618         shown_msg = true;
619         // I18N: Message shown in network lobby to tell user that
620         // player name is clickable
621         core::stringw msg = _("Press player name in the list for player"
622             " management and ranking information.");
623         MessageQueue::add(MessageQueue::MT_GENERIC, msg);
624     }
625 
626     uint32_t host_id = data.getUInt32();
627     STKHost::get()->setMyHostId(host_id);
628     if (auto sl = LobbyProtocol::getByType<ServerLobby>(PT_CHILD))
629         sl->setClientServerHostId(host_id);
630 
631     assert(!NetworkConfig::get()->isAddingNetworkPlayers());
632     uint32_t server_version = data.getUInt32();
633     NetworkConfig::get()->setJoinedServerVersion(server_version);
634     assert(server_version != 0);
635     m_auto_started = false;
636     m_state.store(CONNECTED);
637 
638     unsigned list_caps = data.getUInt16();
639     std::set<std::string> caps;
640     for (unsigned i = 0; i < list_caps; i++)
641     {
642         std::string cap;
643         data.decodeString(&cap);
644         caps.insert(cap);
645     }
646     NetworkConfig::get()->setServerCapabilities(caps);
647 
648     float auto_start_timer = data.getFloat();
649     int state_frequency_in_server = data.getUInt32();
650     NetworkConfig::get()->setStateFrequency(state_frequency_in_server);
651     if (!GUIEngine::isNoGraphics() &&
652         auto_start_timer != std::numeric_limits<float>::max())
653         NetworkingLobby::getInstance()->setStartingTimerTo(auto_start_timer);
654     m_server_enabled_chat = data.getUInt8() == 1;
655     if (NetworkConfig::get()->getServerCapabilities().find("report_player") !=
656         NetworkConfig::get()->getServerCapabilities().end())
657         m_server_enabled_report_player = data.getUInt8() == 1;
658 }   // connectionAccepted
659 
660 //-----------------------------------------------------------------------------
handleServerInfo(Event * event)661 void ClientLobby::handleServerInfo(Event* event)
662 {
663     // At least 6 bytes should remain now
664     if (!checkDataSize(event, 6)) return;
665 
666     core::stringw str, total_lines;
667     if (!m_first_connect)
668     {
669         total_lines = L"--------------------";
670         total_lines += L"\n";
671     }
672     m_first_connect = false;
673 
674     NetworkString &data = event->data();
675     // Add server info
676     uint8_t u_data;
677     data.decodeStringW(&str);
678 
679     //I18N: In the networking lobby
680     total_lines += _("Server name: %s", str);
681     total_lines += L"\n";
682 
683     u_data = data.getUInt8();
684     const core::stringw& difficulty_name =
685         RaceManager::get()->getDifficultyName((RaceManager::Difficulty)u_data);
686     RaceManager::get()->setDifficulty((RaceManager::Difficulty)u_data);
687     //I18N: In the networking lobby
688     total_lines += _("Difficulty: %s", difficulty_name);
689     total_lines += L"\n";
690 
691     unsigned max_player = data.getUInt8();
692     //I18N: In the networking lobby
693     total_lines += _("Max players: %d", (int)max_player);
694     total_lines += L"\n";
695 
696     // Reserved for extra spectators
697     u_data = data.getUInt8();
698     u_data = data.getUInt8();
699     auto game_mode = ServerConfig::getLocalGameMode(u_data);
700     RaceManager::get()->setMinorMode(game_mode.first);
701     // We use single mode in network even it's grand prix
702     RaceManager::get()->setMajorMode(RaceManager::MAJOR_MODE_SINGLE);
703 
704     //I18N: In the networking lobby
705     core::stringw mode_name = ServerConfig::getModeName(u_data);
706     total_lines += _("Game mode: %s", mode_name);
707     total_lines += L"\n";
708 
709     uint8_t extra_server_info = data.getUInt8();
710     bool grand_prix_started = false;
711     m_game_setup->resetExtraServerInfo();
712     switch (extra_server_info)
713     {
714         case 0:
715             break;
716         case 1:
717         {
718             u_data = data.getUInt8();
719             core::stringw tl = _("Time limit");
720             core::stringw gl = _("Goals limit");
721             core::stringw sgt = u_data == 0 ? tl : gl;
722             m_game_setup->setSoccerGoalTarget(u_data != 0);
723             //I18N: In the networking lobby
724             total_lines += _("Soccer game type: %s", sgt);
725             total_lines += L"\n";
726             break;
727         }
728         case 2:
729         {
730             unsigned cur_gp_track = data.getUInt8();
731             grand_prix_started = cur_gp_track != 0;
732             unsigned total_gp_track = data.getUInt8();
733             m_game_setup->setGrandPrixTrack(total_gp_track);
734             total_lines += _("Grand prix progress: %d / %d", cur_gp_track,
735                 total_gp_track);
736             total_lines += L"\n";
737             break;
738         }
739     }
740     // Auto start info
741     unsigned min_players = data.getUInt8();
742     float start_timeout = data.getFloat();
743     if (!GUIEngine::isNoGraphics())
744     {
745         NetworkingLobby::getInstance()->initAutoStartTimer(grand_prix_started,
746             min_players, start_timeout, max_player);
747     }
748 
749     // MOTD
750     core::stringw motd;
751     data.decodeString16(&motd);
752     if (!motd.empty())
753         total_lines += motd;
754 
755     // Remove last newline added, network lobby will add it back later after
756     // removing old server info (with chat)
757     if (total_lines[total_lines.size() - 1] == L'\n')
758         total_lines.erase(total_lines.size() - 1);
759 
760     if (!GUIEngine::isNoGraphics())
761         NetworkingLobby::getInstance()->addMoreServerInfo(total_lines);
762 
763     bool server_config = data.getUInt8() == 1;
764     if (!GUIEngine::isNoGraphics())
765         NetworkingLobby::getInstance()->toggleServerConfigButton(server_config);
766     m_server_live_joinable = data.getUInt8() == 1;
767     NetworkConfig::get()->setTuxHitboxAddon(m_server_live_joinable);
768 }   // handleServerInfo
769 
770 //-----------------------------------------------------------------------------
updatePlayerList(Event * event)771 void ClientLobby::updatePlayerList(Event* event)
772 {
773     if (!checkDataSize(event, 1)) return;
774     NetworkString& data = event->data();
775     bool waiting = data.getUInt8() == 1;
776     if (m_waiting_for_game && !waiting)
777     {
778         // The waiting game finished
779         SFXManager::get()->quickSound("wee");
780     }
781 
782     m_waiting_for_game = waiting;
783     unsigned player_count = data.getUInt8();
784     core::stringw total_players;
785     m_lobby_players.clear();
786     bool client_server_owner = false;
787     for (unsigned i = 0; i < player_count; i++)
788     {
789         LobbyPlayer lp = {};
790         lp.m_host_id = data.getUInt32();
791         lp.m_online_id = data.getUInt32();
792         uint8_t local_id = data.getUInt8();
793         lp.m_handicap = HANDICAP_NONE;
794         lp.m_local_player_id = local_id;
795         data.decodeStringW(&lp.m_user_name);
796         total_players += lp.m_user_name;
797         uint8_t boolean_combine = data.getUInt8();
798         bool is_peer_waiting_for_game = (boolean_combine & 1) == 1;
799         bool is_spectator = ((boolean_combine >> 1) & 1) == 1;
800         bool is_peer_server_owner = ((boolean_combine >> 2) & 1) == 1;
801         bool ready = ((boolean_combine >> 3) & 1) == 1;
802         bool ai = ((boolean_combine >> 4) & 1) == 1;
803         // icon to be used, see NetworkingLobby::loadedFromFile
804         lp.m_icon_id = is_peer_server_owner ? 0 :
805             lp.m_online_id != 0 /*if online account*/ ? 1 : 2;
806         if (ai)
807             lp.m_icon_id = 6;
808         if (waiting && !is_peer_waiting_for_game)
809             lp.m_icon_id = 3;
810         if (is_spectator)
811             lp.m_icon_id = 5;
812         if (ready)
813             lp.m_icon_id = 4;
814         lp.m_handicap = (HandicapLevel)data.getUInt8();
815         if (lp.m_handicap != HANDICAP_NONE)
816         {
817             lp.m_user_name = _("%s (handicapped)", lp.m_user_name);
818         }
819         lp.m_kart_team = (KartTeam)data.getUInt8();
820         // No handicap for AI peer
821         if (!ai && lp.m_host_id == STKHost::get()->getMyHostId())
822         {
823             if (is_peer_server_owner)
824                 client_server_owner = true;
825             auto& local_players = NetworkConfig::get()->getNetworkPlayers();
826             std::get<2>(local_players.at(local_id)) = lp.m_handicap;
827         }
828         data.decodeString(&lp.m_country_code);
829         m_lobby_players.push_back(lp);
830     }
831     STKHost::get()->setAuthorisedToControl(client_server_owner);
832 
833     // Notification sound for new player
834     if (!m_total_players.empty() &&
835         total_players.size() > m_total_players.size())
836         SFXManager::get()->quickSound("energy_bar_full");
837     m_total_players = total_players;
838 
839     if (!GUIEngine::isNoGraphics())
840         NetworkingLobby::getInstance()->updatePlayers();
841 }   // updatePlayerList
842 
843 //-----------------------------------------------------------------------------
handleBadTeam()844 void ClientLobby::handleBadTeam()
845 {
846     SFXManager::get()->quickSound("anvil");
847     //I18N: Display when all players are in red or blue team, which the race
848     //will not be allowed to start
849     core::stringw msg = _("All players joined red or blue team.");
850     MessageQueue::add(MessageQueue::MT_ERROR, msg);
851 }   // handleBadTeam
852 
853 //-----------------------------------------------------------------------------
handleBadConnection()854 void ClientLobby::handleBadConnection()
855 {
856     SFXManager::get()->quickSound("anvil");
857     core::stringw msg = _("Bad network connection is detected.");
858     MessageQueue::add(MessageQueue::MT_ERROR, msg);
859 }   // handleBadConnection
860 
861 //-----------------------------------------------------------------------------
becomingServerOwner()862 void ClientLobby::becomingServerOwner()
863 {
864     if (STKHost::get()->isClientServer())
865         return;
866 
867     SFXManager::get()->quickSound("wee");
868     //I18N: Display when a player is allow to control the server
869     core::stringw msg = _("You are now the owner of server.");
870     MessageQueue::add(MessageQueue::MT_GENERIC, msg);
871 }   // becomingServerOwner
872 
873 //-----------------------------------------------------------------------------
handleChat(Event * event)874 void ClientLobby::handleChat(Event* event)
875 {
876     if (!UserConfigParams::m_lobby_chat)
877         return;
878     SFXManager::get()->quickSound("plopp");
879     core::stringw message;
880     event->data().decodeString16(&message);
881     Log::info("ClientLobby", "%s", StringUtils::wideToUtf8(message).c_str());
882     if (GUIEngine::isNoGraphics())
883         return;
884     if (message.size() > 0)
885     {
886         if (GUIEngine::getCurrentScreen() == NetworkingLobby::getInstance())
887             NetworkingLobby::getInstance()->addMoreServerInfo(message);
888         else if (UserConfigParams::m_race_chat)
889             MessageQueue::add(MessageQueue::MT_GENERIC, message);
890     }
891 }   // handleChat
892 
893 //-----------------------------------------------------------------------------
894 /*! \brief Called when the server refuses the connection.
895  *  \param event : Event providing the information.
896  *
897  *  Format of the data :
898  *  Byte 0
899  *       ----------------
900  *  Size |      1       |
901  *  Data | refusal code |
902  *       ----------------
903  */
connectionRefused(Event * event)904 void ClientLobby::connectionRefused(Event* event)
905 {
906     if (!checkDataSize(event, 1)) return;
907     const NetworkString &data = event->data();
908     switch ((RejectReason)data.getUInt8()) // the second byte
909     {
910     case RR_BUSY:
911         STKHost::get()->setErrorMessage(
912             _("Connection refused: Server is busy."));
913         break;
914     case RR_BANNED:
915     {
916         core::stringw msg =
917             _("Connection refused: You are banned from the server.");
918         core::stringw reason;
919         data.decodeStringW(&reason);
920         if (!reason.empty())
921         {
922             msg += L"\n";
923             msg += reason;
924         }
925         STKHost::get()->setErrorMessage(msg);
926         break;
927     }
928     case RR_INCORRECT_PASSWORD:
929         m_server->setReconnectWhenQuitLobby(true);
930         STKHost::get()->setErrorMessage(
931             _("Connection refused: Server password is incorrect."));
932         break;
933     case RR_INCOMPATIBLE_DATA:
934         STKHost::get()->setErrorMessage(
935             _("Connection refused: Game data is incompatible."));
936         break;
937     case RR_TOO_MANY_PLAYERS:
938         STKHost::get()->setErrorMessage(
939             _("Connection refused: Server is full."));
940         break;
941     case RR_INVALID_PLAYER:
942         STKHost::get()->setErrorMessage(
943             _("Connection refused: Invalid player connecting."));
944         break;
945     }
946     STKHost::get()->disconnectAllPeers(false/*timeout_waiting*/);
947     STKHost::get()->requestShutdown();
948 }   // connectionRefused
949 
950 //-----------------------------------------------------------------------------
951 
952 /*! \brief Called when the server broadcasts to start the race to all clients.
953  *  \param event : Event providing the time the client should start game.
954  */
startGame(Event * event)955 void ClientLobby::startGame(Event* event)
956 {
957     World::getWorld()->setPhase(WorldStatus::SERVER_READY_PHASE);
958     uint64_t start_time = event->data().getUInt64();
959     powerup_manager->setRandomSeed(start_time);
960 
961     unsigned check_structure_count = event->data().getUInt8();
962     LinearWorld* lw = dynamic_cast<LinearWorld*>(World::getWorld());
963     if (lw)
964         lw->handleServerCheckStructureCount(check_structure_count);
965 
966     NetworkItemManager* nim = dynamic_cast<NetworkItemManager*>
967         (Track::getCurrentTrack()->getItemManager());
968     assert(nim);
969     nim->restoreCompleteState(event->data());
970 
971     core::stringw err_msg = _("Failed to start the network game.");
972     // Different stk process thread may have different stk host
973     STKHost* stk_host = STKHost::get();
974     joinStartGameThread();
975     m_start_game_thread = std::thread([start_time, stk_host, this, err_msg]()
976         {
977             const uint64_t cur_time = stk_host->getNetworkTimer();
978             if (!(start_time > cur_time))
979             {
980                 Log::error("ClientLobby", "Network timer is too slow to catch "
981                     "up, you must have a poor network.");
982                 stk_host->setErrorMessage(err_msg);
983                 stk_host->requestShutdown();
984                 return;
985             }
986             int sleep_time = (int)(start_time - cur_time);
987             //Log::info("ClientLobby", "Start game after %dms", sleep_time);
988             StkTime::sleep(sleep_time);
989             //Log::info("ClientLobby", "Started at %lf", StkTime::getRealTime());
990             m_state.store(RACING);
991         });
992 }   // startGame
993 
994 //-----------------------------------------------------------------------------
995 /*! \brief Called when the kart selection starts.
996  *  \param event : Event providing the information (no additional information
997  *                 in this case).
998  */
startSelection(Event * event)999 void ClientLobby::startSelection(Event* event)
1000 {
1001     SFXManager::get()->quickSound("wee");
1002     const NetworkString& data = event->data();
1003     startVotingPeriod(data.getFloat());
1004     bool skip_kart_screen = data.getUInt8() == 1;
1005     m_server_auto_game_time = data.getUInt8() == 1;
1006     m_server_enabled_track_voting = data.getUInt8() == 1;
1007     const unsigned kart_num = data.getUInt16();
1008     const unsigned track_num = data.getUInt16();
1009     m_available_karts.clear();
1010     m_available_tracks.clear();
1011     for (unsigned i = 0; i < kart_num; i++)
1012     {
1013         std::string kart;
1014         data.decodeString(&kart);
1015         m_available_karts.insert(kart);
1016     }
1017     for (unsigned i = 0; i < track_num; i++)
1018     {
1019         std::string track;
1020         data.decodeString(&track);
1021         m_available_tracks.insert(track);
1022     }
1023 
1024     // In case the user opened a user info dialog
1025     GUIEngine::ModalDialog::dismiss();
1026     GUIEngine::ScreenKeyboard::dismiss();
1027 
1028     if (!GUIEngine::isNoGraphics())
1029     {
1030         NetworkKartSelectionScreen* screen =
1031             NetworkKartSelectionScreen::getInstance();
1032         screen->setAvailableKartsFromServer(m_available_karts);
1033         screen->setLiveJoin(false);
1034     }
1035     // In case of auto-connect or continue a grand prix, use random karts
1036     // (or previous kart) from server and go to track selection
1037     if ((NetworkConfig::get()->isAutoConnect() || skip_kart_screen) &&
1038         m_server_enabled_track_voting)
1039     {
1040         input_manager->setMasterPlayerOnly(true);
1041         for (auto& p : NetworkConfig::get()->getNetworkPlayers())
1042         {
1043             StateManager::get()
1044                 ->createActivePlayer(std::get<1>(p), std::get<0>(p));
1045         }
1046         input_manager->getDeviceManager()->setAssignMode(ASSIGN);
1047         if (!GUIEngine::isNoGraphics())
1048         {
1049             TracksScreen::getInstance()->setQuitServer();
1050             TracksScreen::getInstance()->setNetworkTracks();
1051             TracksScreen::getInstance()->push();
1052         }
1053     }
1054     else if (!GUIEngine::isNoGraphics())
1055     {
1056         NetworkKartSelectionScreen::getInstance()->push();
1057     }
1058 
1059     if (!GUIEngine::isNoGraphics())
1060     {
1061         TracksScreen *ts = TracksScreen::getInstance();
1062         ts->resetVote();
1063     }
1064     m_state.store(SELECTING_ASSETS);
1065     Log::info("ClientLobby", "Selection starts now");
1066 }   // startSelection
1067 
1068 //-----------------------------------------------------------------------------
1069 
1070 /*! \brief Called when all karts have finished the race.
1071  *  \param event : Event providing the information.
1072  *
1073  *  Format of the data :
1074  *  Byte 0           1
1075  *       -------------------------------
1076  *  Size |     1     |     1     |     |
1077  *  Data | Kart 1 ID | kart id 2 | ... |
1078  *       -------------------------------
1079  */
raceFinished(Event * event)1080 void ClientLobby::raceFinished(Event* event)
1081 {
1082     NetworkString &data = event->data();
1083     Log::info("ClientLobby", "Server notified that the race is finished.");
1084     LinearWorld* lw = dynamic_cast<LinearWorld*>(World::getWorld());
1085     if (m_game_setup->isGrandPrix())
1086     {
1087         int t = data.getUInt32();
1088         core::stringw kart_name;
1089         data.decodeStringW(&kart_name);
1090         lw->setFastestLapTicks(t);
1091         lw->setFastestKartName(kart_name);
1092         RaceManager::get()->configGrandPrixResultFromNetwork(data);
1093     }
1094     else if (RaceManager::get()->modeHasLaps())
1095     {
1096         int t = data.getUInt32();
1097         core::stringw kart_name;
1098         data.decodeStringW(&kart_name);
1099         lw->setFastestLapTicks(t);
1100         lw->setFastestKartName(kart_name);
1101     }
1102 
1103     if (lw)
1104     {
1105         // Eliminate all karts which have not finished the race, it can happen
1106         // if the last player leave the game instead of crossing the finish
1107         // line
1108         lw->updateRacePosition();
1109         for (unsigned i = 0; i < lw->getNumKarts(); i++)
1110         {
1111             AbstractKart* k = lw->getKart(i);
1112             if (!k->hasFinishedRace() && !k->isEliminated())
1113             {
1114                 core::stringw player_name = k->getController()->getName();
1115                 core::stringw msg = _("%s left the game.", player_name);
1116                 MessageQueue::add(MessageQueue::MT_FRIEND, msg);
1117                 World::getWorld()->eliminateKart(i,
1118                     false/*notify_of_elimination*/);
1119                 k->finishedRace(World::getWorld()->getTime(),
1120                     true/*from_server*/);
1121             }
1122         }
1123     }
1124 
1125     m_ranking_changes.clear();
1126     // Ranking changes from server
1127     if (NetworkConfig::get()->getServerCapabilities().find("ranking_changes")
1128         != NetworkConfig::get()->getServerCapabilities().end())
1129     {
1130         bool has_ranking_changes = (data.getUInt8() & 1) != 0;
1131         if (has_ranking_changes)
1132         {
1133             unsigned count = data.getUInt8();
1134             for (unsigned i = 0; i < count; i++)
1135                 m_ranking_changes.push_back(data.getFloat());
1136         }
1137     }
1138 
1139     // stop race protocols
1140     RaceEventManager::get()->stop();
1141     ProtocolManager::lock()->findAndTerminate(PROTOCOL_GAME_EVENTS);
1142     ProtocolManager::lock()->findAndTerminate(PROTOCOL_CONTROLLER_EVENTS);
1143     m_state.store(RACE_FINISHED);
1144 }   // raceFinished
1145 
1146 //-----------------------------------------------------------------------------
1147 /** Called when the server informs the clients to exit the race result screen.
1148  *  It exits the race, and goes back to the lobby.
1149  */
backToLobby(Event * event)1150 void ClientLobby::backToLobby(Event *event)
1151 {
1152     // In case the user opened a user info dialog
1153     GUIEngine::ModalDialog::dismiss();
1154     GUIEngine::ScreenKeyboard::dismiss();
1155 
1156     NetworkConfig::get()->clearActivePlayersForClient();
1157     setup();
1158     m_auto_started = false;
1159     m_state.store(CONNECTED);
1160 
1161     if (RaceEventManager::get())
1162     {
1163         RaceEventManager::get()->stop();
1164         ProtocolManager::lock()->findAndTerminate(PROTOCOL_GAME_EVENTS);
1165     }
1166     auto gp = GameProtocol::lock();
1167     if (gp)
1168     {
1169         auto lock = gp->acquireWorldDeletingMutex();
1170         ProtocolManager::lock()->findAndTerminate(PROTOCOL_CONTROLLER_EVENTS);
1171         exitGameState();
1172     }
1173     else
1174         exitGameState();
1175 
1176     NetworkString &data = event->data();
1177     core::stringw msg;
1178     MessageQueue::MessageType mt = MessageQueue::MT_ERROR;
1179 
1180     switch ((BackLobbyReason)data.getUInt8()) // the second byte
1181     {
1182     case BLR_NO_GAME_FOR_LIVE_JOIN:
1183         // I18N: Error message shown if live join or spectate failed in network
1184         msg = _("The game has ended, you can't live join or spectate anymore.");
1185         break;
1186     case BLR_NO_PLACE_FOR_LIVE_JOIN:
1187         // I18N: Error message shown if live join failed in network
1188         msg = _("No remaining place in the arena - live join disabled.");
1189         break;
1190     case BLR_ONE_PLAYER_IN_RANKED_MATCH:
1191         // I18N: Error message shown if only 1 player remains in network
1192         msg = _("Only 1 player remaining, returning to lobby.");
1193         break;
1194     case BLR_SERVER_ONWER_QUITED_THE_GAME:
1195         // I18N: Error message shown when all players will go back to lobby
1196         // when server owner quited the game
1197         if (!STKHost::get()->isClientServer())
1198             msg = _("Server owner quit the game.");
1199         break;
1200     case BLR_SPECTATING_NEXT_GAME:
1201         // I18N: Status shown to player when he will be spectating the next game
1202         msg = _("You will be spectating the next game.");
1203         mt = MessageQueue::MT_GENERIC;
1204         break;
1205     default:
1206         break;
1207     }
1208     if (!msg.empty())
1209     {
1210         if (mt == MessageQueue::MT_GENERIC)
1211             SFXManager::get()->quickSound("plopp");
1212         else
1213             SFXManager::get()->quickSound("anvil");
1214         MessageQueue::add(mt, msg);
1215     }
1216 }   // backToLobby
1217 
1218 //-----------------------------------------------------------------------------
1219 /** Callback when the world is loaded. The client will inform the server
1220  *  that the players on this host are ready to start the race. It is called by
1221  *  the RaceManager after the world is loaded.
1222  */
finishedLoadingWorld()1223 void ClientLobby::finishedLoadingWorld()
1224 {
1225     NetworkString* ns = getNetworkString(1);
1226     ns->setSynchronous(m_server_send_live_load_world);
1227     ns->addUInt8(LE_CLIENT_LOADED_WORLD);
1228     sendToServer(ns, true);
1229     delete ns;
1230 }   // finishedLoadingWorld
1231 
1232 //-----------------------------------------------------------------------------
liveJoinAcknowledged(Event * event)1233 void ClientLobby::liveJoinAcknowledged(Event* event)
1234 {
1235     World* w = World::getWorld();
1236     if (!w)
1237         return;
1238 
1239     const NetworkString& data = event->data();
1240     m_start_live_game_time = data.getUInt64();
1241     powerup_manager->setRandomSeed(m_start_live_game_time);
1242 
1243     unsigned check_structure_count = event->data().getUInt8();
1244     LinearWorld* lw = dynamic_cast<LinearWorld*>(World::getWorld());
1245     if (lw)
1246         lw->handleServerCheckStructureCount(check_structure_count);
1247 
1248     m_start_live_game_time = data.getUInt64();
1249     m_last_live_join_util_ticks = data.getUInt32();
1250     for (unsigned i = 0; i < w->getNumKarts(); i++)
1251     {
1252         AbstractKart* k = w->getKart(i);
1253         if (k->getController()->isLocalPlayerController())
1254             k->setLiveJoinKart(m_last_live_join_util_ticks);
1255     }
1256 
1257     NetworkItemManager* nim = dynamic_cast<NetworkItemManager*>
1258         (Track::getCurrentTrack()->getItemManager());
1259     assert(nim);
1260     nim->restoreCompleteState(data);
1261     w->restoreCompleteState(data);
1262 
1263     if (RaceManager::get()->supportsLiveJoining() && data.size() > 0)
1264     {
1265         // Get and update the players list 1 more time in case the was
1266         // player connection or disconnection
1267         std::vector<std::shared_ptr<NetworkPlayerProfile> > players =
1268             decodePlayers(data);
1269         w->resetElimination();
1270         for (unsigned i = 0; i < players.size(); i++)
1271         {
1272             AbstractKart* k = w->getKart(i);
1273             if (k->getController()->isLocalPlayerController())
1274                 continue;
1275             k->reset();
1276             // Only need to change non local player karts
1277             RemoteKartInfo& rki = RaceManager::get()->getKartInfo(i);
1278             rki.copyFrom(players[i], players[i]->getLocalPlayerId());
1279             if (rki.isReserved())
1280             {
1281                 World::getWorld()->eliminateKart(i,
1282                     false/*notify_of_elimination*/);
1283                 k->setPosition(
1284                     World::getWorld()->getCurrentNumKarts() + 1);
1285                 k->finishedRace(World::getWorld()->getTime(),
1286                     true/*from_server*/);
1287             }
1288             else
1289             {
1290                 // Will be reset once again after live join is finished
1291                 addLiveJoiningKart(i, rki, 0/*live_join_util_ticks*/);
1292                 k->getNode()->setVisible(false);
1293             }
1294         }
1295     }
1296 }   // liveJoinAcknowledged
1297 
1298 //-----------------------------------------------------------------------------
finishLiveJoin()1299 void ClientLobby::finishLiveJoin()
1300 {
1301     m_start_live_game_time = std::numeric_limits<uint64_t>::max();
1302     World* w = World::getWorld();
1303     if (!w)
1304         return;
1305     Log::info("ClientLobby", "Live join started at %lf",
1306         StkTime::getRealTime());
1307 
1308     w->setLiveJoinWorld(false);
1309     w->endLiveJoinWorld(m_last_live_join_util_ticks);
1310     for (unsigned i = 0; i < w->getNumKarts(); i++)
1311     {
1312         AbstractKart* k = w->getKart(i);
1313         if (!k->getController()->isLocalPlayerController() &&
1314             !k->isEliminated())
1315             k->getNode()->setVisible(true);
1316     }
1317     m_state.store(RACING);
1318 }   // finishLiveJoin
1319 
1320 //-----------------------------------------------------------------------------
requestKartInfo(uint8_t kart_id)1321 void ClientLobby::requestKartInfo(uint8_t kart_id)
1322 {
1323     NetworkString* ns = getNetworkString(1);
1324     ns->setSynchronous(true);
1325     ns->addUInt8(LE_KART_INFO).addUInt8(kart_id);
1326     sendToServer(ns, true/*reliable*/);
1327     delete ns;
1328 }   // requestKartInfo
1329 
1330 //-----------------------------------------------------------------------------
handleKartInfo(Event * event)1331 void ClientLobby::handleKartInfo(Event* event)
1332 {
1333     World* w = World::getWorld();
1334     if (!w)
1335         return;
1336 
1337     const NetworkString& data = event->data();
1338     int live_join_util_ticks = data.getUInt32();
1339     uint8_t kart_id = data.getUInt8();
1340     core::stringw player_name;
1341     data.decodeStringW(&player_name);
1342     uint32_t host_id = data.getUInt32();
1343     float kart_color = data.getFloat();
1344     uint32_t online_id = data.getUInt32();
1345     HandicapLevel h = (HandicapLevel)data.getUInt8();
1346     uint8_t local_id = data.getUInt8();
1347     std::string kart_name;
1348     data.decodeString(&kart_name);
1349     std::string country_code;
1350     data.decodeString(&country_code);
1351 
1352     RemoteKartInfo& rki = RaceManager::get()->getKartInfo(kart_id);
1353     rki.setPlayerName(player_name);
1354     rki.setHostId(host_id);
1355     rki.setDefaultKartColor(kart_color);
1356     rki.setOnlineId(online_id);
1357     rki.setHandicap(h);
1358     rki.setLocalPlayerId(local_id);
1359     rki.setKartName(kart_name);
1360     rki.setCountryCode(country_code);
1361     addLiveJoiningKart(kart_id, rki, live_join_util_ticks);
1362 
1363     core::stringw msg;
1364     if (RaceManager::get()->teamEnabled())
1365     {
1366         if (w->getKartTeam(kart_id) == KART_TEAM_RED)
1367         {
1368             // I18N: Show when player join red team of the started game in
1369             // network
1370             msg = _("%s joined the red team.", player_name);
1371         }
1372         else
1373         {
1374             // I18N: Show when player join blue team of the started game in
1375             // network
1376             msg = _("%s joined the blue team.", player_name);
1377         }
1378     }
1379     else
1380     {
1381         // I18N: Show when player join the started game in network
1382         msg = _("%s joined the game.", player_name);
1383     }
1384     SFXManager::get()->quickSound("energy_bar_full");
1385     MessageQueue::add(MessageQueue::MT_FRIEND, msg);
1386 }   // handleKartInfo
1387 
1388 //-----------------------------------------------------------------------------
startLiveJoinKartSelection()1389 void ClientLobby::startLiveJoinKartSelection()
1390 {
1391     NetworkKartSelectionScreen::getInstance()->setLiveJoin(true);
1392     std::vector<int> all_k =
1393         kart_properties_manager->getKartsInGroup("standard");
1394     std::set<std::string> karts;
1395     for (int kart : all_k)
1396     {
1397         const KartProperties* kp = kart_properties_manager->getKartById(kart);
1398         if (!kp->isAddon())
1399             karts.insert(kp->getIdent());
1400     }
1401     NetworkKartSelectionScreen::getInstance()
1402         ->setAvailableKartsFromServer(karts);
1403     NetworkKartSelectionScreen::getInstance()->push();
1404 }   // startLiveJoinKartSelection
1405 
1406 // ----------------------------------------------------------------------------
sendChat(irr::core::stringw text,KartTeam team)1407 void ClientLobby::sendChat(irr::core::stringw text, KartTeam team)
1408 {
1409     text = text.trim().removeChars(L"\n\r");
1410     if (text.size() > 0)
1411     {
1412         NetworkString* chat = getNetworkString();
1413         chat->addUInt8(LobbyProtocol::LE_CHAT);
1414 
1415         core::stringw name;
1416         PlayerProfile* player = PlayerManager::getCurrentPlayer();
1417         if (PlayerManager::getCurrentOnlineState() ==
1418             PlayerProfile::OS_SIGNED_IN)
1419             name = PlayerManager::getCurrentOnlineProfile()->getUserName();
1420         else
1421             name = player->getName();
1422         chat->encodeString16(name + L": " + text, 1000/*max_len*/);
1423 
1424         if (team != KART_TEAM_NONE)
1425             chat->addUInt8(team);
1426 
1427         STKHost::get()->sendToServer(chat, true);
1428         delete chat;
1429     }
1430 }   // sendChat
1431 
1432 // ----------------------------------------------------------------------------
changeSpectateTarget(PlayerAction action,int value,Input::InputType type) const1433 void ClientLobby::changeSpectateTarget(PlayerAction action, int value,
1434                                        Input::InputType type) const
1435 {
1436     Camera* cam = Camera::getActiveCamera();
1437     if (!cam)
1438         return;
1439 
1440     // Only 1 local player will be able to change target, and this will replace
1441     // the end camera with normal
1442     if (cam->getType() != Camera::CM_TYPE_NORMAL)
1443         Camera::changeCamera(0, Camera::CM_TYPE_NORMAL);
1444 
1445     // Update if the camera again beacuse when race finished cam will be
1446     // changed above and invalid
1447     cam = Camera::getActiveCamera();
1448     if (!cam)
1449         return;
1450 
1451     // Copied from EventHandler::processGUIAction
1452     const bool pressed_down = value > Input::MAX_VALUE * 2 / 3;
1453 
1454     if (!pressed_down)
1455         return;
1456 
1457     if (action == PA_PAUSE_RACE)
1458     {
1459         StateManager::get()->escapePressed();
1460         return;
1461     }
1462     if (action == PA_LOOK_BACK)
1463     {
1464         if (cam->getMode() == Camera::CM_NORMAL)
1465             cam->setMode(Camera::CM_REVERSE);
1466         else
1467             cam->setMode(Camera::CM_NORMAL);
1468         return;
1469     }
1470     if (action == PA_ACCEL)
1471     {
1472         cam->setNextSpectatorMode();
1473         return;
1474     }
1475 
1476     WorldWithRank* wwr = dynamic_cast<WorldWithRank*>(World::getWorld());
1477     if (!wwr)
1478         return;
1479     std::vector<AbstractKart*> karts;
1480     for (unsigned i = 0; i < wwr->getNumKarts(); i++)
1481         karts.push_back(wwr->getKartAtDrawingPosition(i + 1));
1482 
1483     const int num_karts = (int)karts.size();
1484     int current_idx = -1;
1485     if (cam->getKart())
1486     {
1487         auto it = std::find(karts.begin(), karts.end(), cam->getKart());
1488         if (it != karts.end())
1489             current_idx = (int)std::distance(karts.begin(), it);
1490     }
1491     if (current_idx < 0 || current_idx >= num_karts)
1492         return;
1493 
1494     bool up = false;
1495     if (action == PA_STEER_LEFT)
1496         up = false;
1497     else if (action == PA_STEER_RIGHT)
1498         up = true;
1499     else
1500         return;
1501     for (int i = 0; i < num_karts; i++)
1502     {
1503         current_idx = up ? current_idx + 1 : current_idx - 1;
1504         // Handle looping
1505         if (current_idx == -1)
1506             current_idx = num_karts - 1;
1507         else if (current_idx == num_karts)
1508             current_idx = 0;
1509 
1510         if (!karts[current_idx]->isEliminated())
1511         {
1512             cam->setKart(karts[current_idx]);
1513             break;
1514         }
1515     }
1516 }   // changeSpectateTarget
1517 
1518 // ----------------------------------------------------------------------------
addSpectateHelperMessage() const1519 void ClientLobby::addSpectateHelperMessage() const
1520 {
1521     auto& local_players = NetworkConfig::get()->getNetworkPlayers();
1522     if (local_players.empty())
1523         return;
1524     InputDevice* id = std::get<0>(local_players[0]);
1525     if (!id)
1526         return;
1527     DeviceConfig* dc = id->getConfiguration();
1528     if (!dc)
1529         return;
1530     core::stringw left = dc->getBindingAsString(PA_STEER_LEFT);
1531     core::stringw right = dc->getBindingAsString(PA_STEER_RIGHT);
1532     core::stringw back = dc->getBindingAsString(PA_LOOK_BACK);
1533     core::stringw accel = dc->getBindingAsString(PA_ACCEL);
1534 
1535     // I18N: Message shown in game to tell the player it's possible to change
1536     // the camera target in spectate mode of network
1537     core::stringw msg = _("Press <%s> or <%s> to change the targeted player, "
1538         "<%s> or <%s> for the camera position.", left, right, back, accel);
1539     MessageQueue::add(MessageQueue::MT_GENERIC, msg);
1540 }   // addSpectateHelperMessage
1541 
1542 // ----------------------------------------------------------------------------
reportSuccess(Event * event)1543 void ClientLobby::reportSuccess(Event* event)
1544 {
1545     bool succeeded = false;
1546     core::stringw reporting_name;
1547     succeeded = event->data().getUInt8() == 1;
1548     if (succeeded)
1549         event->data().decodeStringW(&reporting_name);
1550     if (succeeded && !reporting_name.empty())
1551     {
1552         // I18N: Tell player he has successfully report this named player
1553         core::stringw msg = _("Successfully reported %s.", reporting_name);
1554         MessageQueue::add(MessageQueue::MT_GENERIC, msg);
1555     }
1556 }   // reportSuccess
1557 
1558 // ----------------------------------------------------------------------------
handleClientCommand(const std::string & cmd)1559 void ClientLobby::handleClientCommand(const std::string& cmd)
1560 {
1561 #ifndef SERVER_ONLY
1562     auto argv = StringUtils::split(cmd, ' ');
1563     if (argv.size() == 0)
1564         return;
1565     if (argv[0] == "installaddon" && argv.size() == 2)
1566         AddonsPack::install(argv[1]);
1567     else if (argv[0] == "uninstalladdon" && argv.size() == 2)
1568         AddonsPack::uninstall(argv[1]);
1569     else if (argv[0] == "music" && argv.size() == 2)
1570     {
1571         int vol = atoi(argv[1].c_str());
1572         if (vol > 10)
1573             vol = 10;
1574         else if (vol < 0)
1575             vol = 0;
1576         if (vol == 0 && UserConfigParams::m_music)
1577         {
1578             UserConfigParams::m_music = false;
1579             music_manager->stopMusic();
1580         }
1581         else if (vol != 0 && !UserConfigParams::m_music)
1582         {
1583             UserConfigParams::m_music = true;
1584             music_manager->startMusic();
1585             music_manager->setMasterMusicVolume((float)vol / 10);
1586         }
1587         else
1588             music_manager->setMasterMusicVolume((float)vol / 10);
1589     }
1590     else if (argv[0] == "liststkaddon")
1591     {
1592         if (argv.size() > 3)
1593         {
1594             NetworkingLobby::getInstance()->addMoreServerInfo(
1595                 L"Usage: /liststkaddon [option][addon prefix letter(s) to find]");
1596             NetworkingLobby::getInstance()->addMoreServerInfo(
1597                 L"available options: -kart, -track, -arena.");
1598         }
1599         else
1600         {
1601             std::string msg = "";
1602             for (unsigned i = 0; i < addons_manager->getNumAddons(); i++)
1603             {
1604                 // addon_ (6 letters)
1605                 const Addon& addon = addons_manager->getAddon(i);
1606                 const std::string& addon_id = addon.getId();
1607                 if (addon.testStatus(Addon::AS_APPROVED))
1608                 {
1609                     std::string type = "";
1610                     std::string text = "";
1611                     if(argv.size() > 1)
1612                     {
1613                         if(argv[1].compare("-track") == 0 ||
1614                            argv[1].compare("-arena") == 0 ||
1615                            argv[1].compare("-kart" ) == 0)
1616                             type = argv[1].substr(1);
1617                         if((argv.size() == 2 && type.empty()) || argv.size() == 3)
1618                             text = argv[argv.size()-1];
1619                     }
1620 
1621                     if(!type.empty() && addon.getType()!=type)
1622                         continue; // only show given type
1623                     if(!text.empty() && addon_id.find(text, 6) == std::string::npos)
1624                         continue; // only show matched
1625 
1626                     msg += addon_id.substr(6);
1627                     msg += ", ";
1628                 }
1629             }
1630             if (msg.empty())
1631                 NetworkingLobby::getInstance()->addMoreServerInfo(L"Addon not found");
1632             else
1633             {
1634                 msg = msg.substr(0, msg.size() - 2);
1635                 NetworkingLobby::getInstance()->addMoreServerInfo(
1636                     StringUtils::utf8ToWide
1637                     (std::string("STK addon: ") + msg));
1638             }
1639         }
1640     }
1641     else if (argv[0] == "listlocaladdon")
1642     {
1643         if (argv.size() > 3)
1644         {
1645             NetworkingLobby::getInstance()->addMoreServerInfo(
1646                 L"Usage: /listlocaladdon [option][addon prefix letter(s) to find]");
1647             NetworkingLobby::getInstance()->addMoreServerInfo(
1648                 L"available options: -kart, -track/-arena, -skin.");
1649         }
1650         else
1651         {
1652             std::string type = "";
1653             std::string text = "";
1654             if(argv.size() > 1)
1655             {
1656                 if(argv[1].compare("-track") == 0 ||
1657                    argv[1].compare("-arena") == 0 ||
1658                    argv[1].compare("-kart" ) == 0 ||
1659                    argv[1].compare("-skin" ) == 0)
1660                     type = argv[1].substr(1);
1661                 if((argv.size() == 2 && type.empty()) || argv.size() == 3)
1662                     text = argv[argv.size()-1];
1663             }
1664 
1665             std::set<std::string> total_addons;
1666             if(type.empty() || // not specify addon type
1667                (!type.empty() && type.compare("kart") == 0)) // list kart addon
1668             {
1669                 for (unsigned i = 0;
1670                      i < kart_properties_manager->getNumberOfKarts(); i++)
1671                 {
1672                     const KartProperties* kp =
1673                         kart_properties_manager->getKartById(i);
1674                     if (kp->isAddon())
1675                         total_addons.insert(kp->getIdent());
1676                 }
1677             }
1678             if(type.empty() || // not specify addon type
1679                (!type.empty() && (type.compare("track") == 0 || type.compare("arena") == 0)))
1680             {
1681                 for (unsigned i = 0; i < track_manager->getNumberOfTracks(); i++)
1682                 {
1683                     const Track* track = track_manager->getTrack(i);
1684                     if (track->isAddon())
1685                         total_addons.insert(track->getIdent());
1686                 }
1687             }
1688             if(type.empty() || // not specify addon type
1689                (!type.empty() && type.compare("skin") == 0))
1690             {
1691                 std::set<std::string> addon_skin_files;
1692                 std::string skin_folder = file_manager->getAddonsFile("skins/");
1693                 file_manager->listFiles(addon_skin_files/*out*/, skin_folder,
1694                                         false/*make full path*/);
1695                 for (auto& skin : addon_skin_files)
1696                 {
1697                     if (skin == "." || skin == "..")
1698                         continue;
1699                     std::string stkskin = skin_folder + skin + "/stkskin.xml";
1700                     if (file_manager->fileExists(stkskin))
1701                         total_addons.insert(Addon::createAddonId(skin));
1702                 }
1703             }
1704             std::string msg = "";
1705             for (auto& addon : total_addons)
1706             {
1707                 // addon_ (6 letters)
1708                 if (!text.empty() && addon.find(text, 6) == std::string::npos)
1709                     continue;
1710 
1711                 msg += addon.substr(6);
1712                 msg += ", ";
1713             }
1714             if (msg.empty())
1715                 NetworkingLobby::getInstance()->addMoreServerInfo(L"Addon not found");
1716             else
1717             {
1718                 msg = msg.substr(0, msg.size() - 2);
1719                 NetworkingLobby::getInstance()->addMoreServerInfo(
1720                     StringUtils::utf8ToWide(
1721                     std::string("Local addon: ") + msg));
1722             }
1723         }
1724     }
1725     else
1726     {
1727         // Send for server command
1728         NetworkString* cmd_ns = getNetworkString(1);
1729         const std::string& language = UserConfigParams::m_language;
1730         cmd_ns->addUInt8(LE_COMMAND).encodeString(language).encodeString(cmd);
1731         sendToServer(cmd_ns, /*reliable*/true);
1732         delete cmd_ns;
1733     }
1734 #endif
1735 }   // handleClientCommand
1736 
1737 // ----------------------------------------------------------------------------
getKartsTracksNetworkString(BareNetworkString * ns)1738 void ClientLobby::getKartsTracksNetworkString(BareNetworkString* ns)
1739 {
1740     auto all_k = kart_properties_manager->getAllAvailableKarts();
1741     auto all_t = track_manager->getAllTrackIdentifiers();
1742     if (all_k.size() >= 65536)
1743         all_k.resize(65535);
1744     if (all_t.size() >= 65536)
1745         all_t.resize(65535);
1746     ns->addUInt16((uint16_t)all_k.size()).addUInt16((uint16_t)all_t.size());
1747     for (const std::string& kart : all_k)
1748     {
1749         ns->encodeString(kart);
1750     }
1751     for (const std::string& track : all_t)
1752     {
1753         ns->encodeString(track);
1754     }
1755 }   // getKartsTracksNetworkString
1756 
1757 // ----------------------------------------------------------------------------
updateAssetsToServer()1758 void ClientLobby::updateAssetsToServer()
1759 {
1760     NetworkString* ns = getNetworkString(1);
1761     ns->addUInt8(LE_ASSETS_UPDATE);
1762     getKartsTracksNetworkString(ns);
1763     sendToServer(ns, /*reliable*/true);
1764     delete ns;
1765 }   // updateAssetsToServer
1766