From 4d065f67e65b520c6a1595cd891b48be3760628f Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 8 Dec 2016 09:02:10 +1100 Subject: [PATCH 001/413] Started to introduce GameProtocol (which will combine several individual protocols. --- sources.cmake | 2 +- .../controller/local_player_controller.cpp | 11 +- src/network/network_string.hpp | 7 + .../protocols/controller_events_protocol.cpp | 136 ------------------ .../protocols/controller_events_protocol.hpp | 47 ------ src/network/protocols/game_protocol.cpp | 130 +++++++++++++++++ src/network/protocols/game_protocol.hpp | 71 +++++++++ src/network/protocols/lobby_protocol.cpp | 4 +- src/network/race_event_manager.cpp | 11 -- src/network/race_event_manager.hpp | 2 - src/network/stk_host.cpp | 2 +- 11 files changed, 217 insertions(+), 206 deletions(-) delete mode 100644 src/network/protocols/controller_events_protocol.cpp delete mode 100644 src/network/protocols/controller_events_protocol.hpp create mode 100644 src/network/protocols/game_protocol.cpp create mode 100644 src/network/protocols/game_protocol.hpp diff --git a/sources.cmake b/sources.cmake index d4f28ae4d..ba4868d71 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,5 +1,5 @@ # Modify this file to change the last-modified date when you add/remove a file. -# This will then trigger a new cmake run automatically. +# This will then trigger a new cmake run automatically. file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp") file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp") file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") diff --git a/src/karts/controller/local_player_controller.cpp b/src/karts/controller/local_player_controller.cpp index 9aea483c4..d0018038f 100644 --- a/src/karts/controller/local_player_controller.cpp +++ b/src/karts/controller/local_player_controller.cpp @@ -38,7 +38,7 @@ #include "karts/rescue_animation.hpp" #include "modes/world.hpp" #include "network/network_config.hpp" -#include "network/race_event_manager.hpp" +#include "network/protocols/game_protocol.hpp" #include "race/history.hpp" #include "states_screens/race_gui_base.hpp" #include "tracks/track.hpp" @@ -144,13 +144,12 @@ void LocalPlayerController::action(PlayerAction action, int value) PlayerController::action(action, value); // If this is a client, send the action to the server - if (World::getWorld()->isNetworkWorld() && - NetworkConfig::get()->isClient() && - RaceEventManager::getInstance()->isRunning() ) + if (World::getWorld()->isNetworkWorld() && + NetworkConfig::get()->isClient() ) { - RaceEventManager::getInstance()->controllerAction(this, action, value); + GameProtocol::getInstance()->controllerAction(m_kart->getWorldKartId(), + action, value); } - } // action //----------------------------------------------------------------------------- diff --git a/src/network/network_string.hpp b/src/network/network_string.hpp index 0c521107a..a7aba4289 100644 --- a/src/network/network_string.hpp +++ b/src/network/network_string.hpp @@ -353,6 +353,13 @@ public: m_current_offset = 5; // ignore type and token } // NetworkString + // ------------------------------------------------------------------------ + /** Empties the string, but does not reset the pre-allocated size. */ + void clear() + { + m_buffer.erase(m_buffer.begin() + 5, m_buffer.end()); + m_current_offset = 5; + } // clear // ------------------------------------------------------------------------ /** Returns the protocol type of this message. */ ProtocolType getProtocolType() const diff --git a/src/network/protocols/controller_events_protocol.cpp b/src/network/protocols/controller_events_protocol.cpp deleted file mode 100644 index ca0be4d84..000000000 --- a/src/network/protocols/controller_events_protocol.cpp +++ /dev/null @@ -1,136 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2015 Supertuxkart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/protocols/controller_events_protocol.hpp" - -#include "modes/world.hpp" -#include "karts/abstract_kart.hpp" -#include "karts/controller/controller.hpp" -#include "network/event.hpp" -#include "network/network_config.hpp" -#include "network/network_player_profile.hpp" -#include "network/game_setup.hpp" -#include "network/network_config.hpp" -#include "network/protocol_manager.hpp" -#include "network/stk_host.hpp" -#include "network/stk_peer.hpp" -#include "utils/log.hpp" - -//----------------------------------------------------------------------------- - -ControllerEventsProtocol::ControllerEventsProtocol() - : Protocol( PROTOCOL_CONTROLLER_EVENTS) -{ -} // ControllerEventsProtocol - -//----------------------------------------------------------------------------- - -ControllerEventsProtocol::~ControllerEventsProtocol() -{ -} // ~ControllerEventsProtocol - -//----------------------------------------------------------------------------- - -bool ControllerEventsProtocol::notifyEventAsynchronous(Event* event) -{ - if(!checkDataSize(event, 13)) return true; - - NetworkString &data = event->data(); - float time = data.getFloat(); - - uint8_t client_index = -1; - while (data.size() >= 9) - { - uint8_t kart_id = data.getUInt8(); - if (kart_id >=World::getWorld()->getNumKarts()) - { - Log::warn("ControllerEventProtocol", "No valid kart id (%s).", - kart_id); - continue; - } - uint8_t serialized_1 = data.getUInt8(); - uint8_t serialized_2 = data.getUInt8(); - uint8_t serialized_3 = data.getUInt8(); - PlayerAction action = (PlayerAction)(data.getUInt8()); - int action_value = data.getUInt32(); - Log::info("ControllerEventsProtocol", "KartID %d action %d value %d", - kart_id, action, action_value); - Controller *controller = World::getWorld()->getKart(kart_id) - ->getController(); - KartControl *controls = controller->getControls(); - controls->setBrake( (serialized_1 & 0x40)!=0); - controls->setNitro( (serialized_1 & 0x20)!=0); - controls->setRescue( (serialized_1 & 0x10)!=0); - controls->setFire( (serialized_1 & 0x08)!=0); - controls->setLookBack((serialized_1 & 0x04)!=0); - controls->setSkidControl(KartControl::SkidControl(serialized_1 & 0x03)); - - controller->action(action, action_value); - } - if (data.size() > 0 ) - { - Log::warn("ControllerEventProtocol", - "The data seems corrupted. Remains %d", data.size()); - } - if (NetworkConfig::get()->isServer()) - { - // Send update to all clients except the original sender. - STKHost::get()->sendPacketExcept(event->getPeer(), - &data, false); - } // if server - return true; -} // notifyEventAsynchronous - -//----------------------------------------------------------------------------- -/** Called from the local kart controller when an action (like steering, - * acceleration, ...) was triggered. It compresses the current kart control - * state and sends a message with the new info to the server. - * \param controller The controller that triggered the action. - * \param action Which action was triggered. - * \param value New value for the given action. - */ -void ControllerEventsProtocol::controllerAction(Controller* controller, - PlayerAction action, int value) -{ - assert(!NetworkConfig::get()->isServer()); - - KartControl* controls = controller->getControls(); - uint8_t serialized_1 = 0; - serialized_1 |= (controls->getBrake()==true); - serialized_1 <<= 1; - serialized_1 |= (controls->getNitro()==true); - serialized_1 <<= 1; - serialized_1 |= (controls->getRescue()==true); - serialized_1 <<= 1; - serialized_1 |= (controls->getFire()==true); - serialized_1 <<= 1; - serialized_1 |= (controls->getLookBack()==true); - serialized_1 <<= 2; - serialized_1 += controls->getSkidControl(); - uint8_t serialized_2 = (uint8_t)(controls->getAccel()*255.0); - uint8_t serialized_3 = (uint8_t)(controls->getSteer()*127.0); - - NetworkString *ns = getNetworkString(13); - ns->addFloat(World::getWorld()->getTime()); - ns->addUInt8(controller->getKart()->getWorldKartId()); - ns->addUInt8(serialized_1).addUInt8(serialized_2).addUInt8(serialized_3); - ns->addUInt8((uint8_t)(action)).addUInt32(value); - sendToServer(ns, false); // send message to server - delete ns; - - Log::info("ControllerEventsProtocol", "Action %d value %d", action, value); -} // controllerAction diff --git a/src/network/protocols/controller_events_protocol.hpp b/src/network/protocols/controller_events_protocol.hpp deleted file mode 100644 index f357c3c46..000000000 --- a/src/network/protocols/controller_events_protocol.hpp +++ /dev/null @@ -1,47 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2015 Supertuxkart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - -#ifndef CONTROLLER_EVENTS_PROTOCOL_HPP -#define CONTROLLER_EVENTS_PROTOCOL_HPP - -#include "network/protocol.hpp" - -#include "input/input.hpp" -#include "utils/cpp2011.hpp" - -class Controller; -class STKPeer; - -class ControllerEventsProtocol : public Protocol -{ - -public: - ControllerEventsProtocol(); - virtual ~ControllerEventsProtocol(); - - virtual bool notifyEventAsynchronous(Event* event) OVERRIDE; - virtual void update(float dt) OVERRIDE {}; - virtual void setup() OVERRIDE {}; - virtual void asynchronousUpdate() OVERRIDE {} - - void controllerAction(Controller* controller, PlayerAction action, - int value); - -}; // class ControllerEventsProtocol - -#endif // CONTROLLER_EVENTS_PROTOCOL_HPP diff --git a/src/network/protocols/game_protocol.cpp b/src/network/protocols/game_protocol.cpp new file mode 100644 index 000000000..a7d9df069 --- /dev/null +++ b/src/network/protocols/game_protocol.cpp @@ -0,0 +1,130 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2015 Supertuxkart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/protocols/game_protocol.hpp" + +#include "modes/world.hpp" +#include "karts/abstract_kart.hpp" +#include "karts/controller/controller.hpp" +#include "network/event.hpp" +#include "network/network_config.hpp" +#include "network/network_player_profile.hpp" +#include "network/game_setup.hpp" +#include "network/network_config.hpp" +#include "network/network_string.hpp" +#include "network/protocol_manager.hpp" +#include "network/stk_host.hpp" +#include "network/stk_peer.hpp" +#include "utils/log.hpp" + +//----------------------------------------------------------------------------- +/** Constructor. Allocates the buffer for events to send to the server. */ +GameProtocol::GameProtocol() + : Protocol( PROTOCOL_CONTROLLER_EVENTS) +{ + m_data_to_send = getNetworkString(); +} // GameProtocol + +//----------------------------------------------------------------------------- +GameProtocol::~GameProtocol() +{ + delete m_data_to_send; +} // ~GameProtocol + +//----------------------------------------------------------------------------- +bool GameProtocol::notifyEventAsynchronous(Event* event) +{ + if(!checkDataSize(event, 1)) return true; + + NetworkString &data = event->data(); + uint8_t count = data.getUInt8(); + for (unsigned int i = 0; i < count; i++) + { + float time = data.getFloat(); + uint8_t kart_id = data.getUInt8(); + PlayerAction action = (PlayerAction)(data.getUInt8()); + int value = data.getUInt32(); + Log::info("GameProtocol", "Action at %f: %d %d %d", + time, kart_id, action, value); + assert(kart_id < World::getWorld()->getNumKarts()); + Controller *controller = World::getWorld()->getKart(kart_id) + ->getController(); + controller->action(action, value); + } + + if (data.size() > 0 ) + { + Log::warn("ControllerEventProtocol", + "The data seems corrupted. Remains %d", data.size()); + } + if (NetworkConfig::get()->isServer()) + { + // Send update to all clients except the original sender. + STKHost::get()->sendPacketExcept(event->getPeer(), + &data, false); + } // if server + return true; +} // notifyEventAsynchronous + +//----------------------------------------------------------------------------- +/** Synchronous update - will send all commands collected during the last + * frame (and could optional only send messages every N frames). */ +void GameProtocol::update(float dt) +{ + if (m_all_actions.size() == 0) return; // nothing to do + + // Clear left-over data from previous frame. This way the network + // string will increase till it reaches maximum size necessary + m_data_to_send->clear(); + m_data_to_send->addUInt8(m_all_actions.size()); + + // Add all actions + for (auto a : m_all_actions) + { + m_data_to_send->addFloat(a.m_time); + m_data_to_send->addUInt8(a.m_kart_id); + m_data_to_send->addUInt8((uint8_t)(a.m_action)).addUInt32(a.m_value); + } // for a in m_all_actions + + // FIXME: for now send reliable + sendToServer(m_data_to_send, /*reliable*/ true); + m_all_actions.clear(); +} // update + +//----------------------------------------------------------------------------- +/** Called from the local kart controller when an action (like steering, + * acceleration, ...) was triggered. It compresses the current kart control + * state and sends a message with the new info to the server. + * \param Kart id that triggered the action. + * \param action Which action was triggered. + * \param value New value for the given action. + */ +void GameProtocol::controllerAction(int kart_id, PlayerAction action, + int value) +{ + assert(NetworkConfig::get()->isClient()); + Action a; + a.m_kart_id = kart_id; + a.m_action = action; + a.m_value = value; + a.m_time = World::getWorld()->getTime(); + + m_all_actions.push_back(a); + + + Log::info("GameProtocol", "Action %d value %d", action, value); +} // controllerAction diff --git a/src/network/protocols/game_protocol.hpp b/src/network/protocols/game_protocol.hpp new file mode 100644 index 000000000..7f9dcf63c --- /dev/null +++ b/src/network/protocols/game_protocol.hpp @@ -0,0 +1,71 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2015 Supertuxkart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +#ifndef GAME_PROTOCOL_HPP +#define GAME_PROTOCOL_HPP + +#include "network/protocol.hpp" + +#include "input/input.hpp" // for PlayerAction +#include "utils/cpp2011.hpp" +#include "utils/singleton.hpp" + +#include + +class NetworkString; + + +class GameProtocol : public Protocol + , public Singleton +{ +private: + + /** The type of game events to be forwarded to the server. */ + enum { GP_CONTROLLER_ACTION = 0x01}; + + /** A network string that collects all information from the server to be sent + * next. */ + NetworkString *m_data_to_send; + + // Dummy data structure to save all kart actions. + struct Action + { + float m_time; + int m_kart_id; + PlayerAction m_action; + int m_value; + }; // struct Action + + // List of all kart actions to send to the server + std::vector m_all_actions; +public: + GameProtocol(); + virtual ~GameProtocol(); + + virtual bool notifyEventAsynchronous(Event* event) OVERRIDE; + virtual void update(float dt) OVERRIDE; + void controllerAction(int kart_id, PlayerAction action, + int value); + // ------------------------------------------------------------------------ + virtual void setup() OVERRIDE {}; + // ------------------------------------------------------------------------ + virtual void asynchronousUpdate() OVERRIDE {} + +}; // class GameProtocol + +#endif // GAME_PROTOCOL_HPP diff --git a/src/network/protocols/lobby_protocol.cpp b/src/network/protocols/lobby_protocol.cpp index d6c3cc5f8..4cb0e722b 100644 --- a/src/network/protocols/lobby_protocol.cpp +++ b/src/network/protocols/lobby_protocol.cpp @@ -24,7 +24,7 @@ #include "modes/world.hpp" #include "network/network_player_profile.hpp" #include "network/protocol_manager.hpp" -#include "network/protocols/controller_events_protocol.hpp" +#include "network/protocols/game_protocol.hpp" #include "network/protocols/game_events_protocol.hpp" #include "network/protocols/kart_update_protocol.hpp" #include "network/protocols/latency_protocol.hpp" @@ -125,7 +125,7 @@ void LobbyProtocol::loadWorld() m_game_setup->getRaceConfig()->loadWorld(); World::getWorld()->setNetworkWorld(true); (new KartUpdateProtocol())->requestStart(); - (new ControllerEventsProtocol())->requestStart(); + GameProtocol::getInstance()->requestStart(); (new GameEventsProtocol())->requestStart(); } // loadWorld diff --git a/src/network/race_event_manager.cpp b/src/network/race_event_manager.cpp index bc86d853c..e802a8f98 100644 --- a/src/network/race_event_manager.cpp +++ b/src/network/race_event_manager.cpp @@ -5,7 +5,6 @@ #include "modes/world.hpp" #include "network/network_config.hpp" #include "network/protocol_manager.hpp" -#include "network/protocols/controller_events_protocol.hpp" #include "network/protocols/game_events_protocol.hpp" @@ -85,13 +84,3 @@ void RaceEventManager::collectedItem(Item *item, AbstractKart *kart) protocol->collectedItem(item,kart); } // collectedItem -// ---------------------------------------------------------------------------- -void RaceEventManager::controllerAction(Controller* controller, - PlayerAction action, int value) -{ - ControllerEventsProtocol* protocol = static_cast( - ProtocolManager::getInstance()->getProtocol(PROTOCOL_CONTROLLER_EVENTS)); - if (protocol) - protocol->controllerAction(controller, action, value); -} // controllerAction - diff --git a/src/network/race_event_manager.hpp b/src/network/race_event_manager.hpp index 9a5febe1e..3fff5064f 100755 --- a/src/network/race_event_manager.hpp +++ b/src/network/race_event_manager.hpp @@ -53,8 +53,6 @@ public: bool isRaceOver(); void collectedItem(Item *item, AbstractKart *kart); - void controllerAction(Controller* controller, PlayerAction action, - int value); void kartFinishedRace(AbstractKart *kart, float time); // ------------------------------------------------------------------------ /** Returns if this instance is in running state or not. */ diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index 0583a88a9..1c961add5 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -230,7 +230,7 @@ void STKHost::create() * at that stage as well. * * Once the countdown is 0 (or below), the Synchronization Protocol will - * start the protocols: KartUpdateProtocol, ControllerEventsProtocol, + * start the protocols: KartUpdateProtocol, GameProtocol, * GameEventsProtocol. Then the LatencyProtocol is terminated * which indicates to the main loop to start the actual game. */ From 04d1d4817ac7c7951b48fe481f89332dcff2b192 Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 9 Dec 2016 09:00:20 +1100 Subject: [PATCH 002/413] Removed KartUpdate protocol (which breakes online play). --- sources.cmake | 2 +- .../protocols/kart_update_protocol.cpp | 147 ------------------ .../protocols/kart_update_protocol.hpp | 43 ----- src/network/protocols/lobby_protocol.cpp | 2 - 4 files changed, 1 insertion(+), 193 deletions(-) delete mode 100644 src/network/protocols/kart_update_protocol.cpp delete mode 100644 src/network/protocols/kart_update_protocol.hpp diff --git a/sources.cmake b/sources.cmake index ba4868d71..13db008ff 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,5 +1,5 @@ # Modify this file to change the last-modified date when you add/remove a file. -# This will then trigger a new cmake run automatically. +# This will then trigger a new cmake run automatically. file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp") file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp") file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") diff --git a/src/network/protocols/kart_update_protocol.cpp b/src/network/protocols/kart_update_protocol.cpp deleted file mode 100644 index dce04265e..000000000 --- a/src/network/protocols/kart_update_protocol.cpp +++ /dev/null @@ -1,147 +0,0 @@ -#include "network/protocols/kart_update_protocol.hpp" - -#include "karts/abstract_kart.hpp" -#include "karts/controller/controller.hpp" -#include "modes/world.hpp" -#include "network/event.hpp" -#include "network/network_config.hpp" -#include "network/protocol_manager.hpp" -#include "utils/time.hpp" - -KartUpdateProtocol::KartUpdateProtocol() : Protocol(PROTOCOL_KART_UPDATE) -{ -} // KartUpdateProtocol - -// ---------------------------------------------------------------------------- -KartUpdateProtocol::~KartUpdateProtocol() -{ -} // ~KartUpdateProtocol - -// ---------------------------------------------------------------------------- -void KartUpdateProtocol::setup() -{ - // Allocate arrays to store one position and rotation for each kart - // (which is the update information from the server to the client). - m_next_positions.resize(World::getWorld()->getNumKarts()); - m_next_quaternions.resize(World::getWorld()->getNumKarts()); - - // This flag keeps track if valid data for an update is in - // the arrays - m_was_updated = false; - - m_previous_time = 0; -} // setup - -// ---------------------------------------------------------------------------- -/** Store the update events in the queue. Since the events are handled in the - * synchronous notify function, there is no lock necessary to - */ -bool KartUpdateProtocol::notifyEvent(Event* event) -{ - // It might be possible that we still receive messages after - // the game was exited, so make sure we still have a world. - if (event->getType() != EVENT_TYPE_MESSAGE || !World::getWorld()) - return true; - NetworkString &ns = event->data(); - if (ns.size() < 33) - { - Log::info("KartUpdateProtocol", "Message too short."); - return true; - } - float time = ns.getFloat(); - while(ns.size() >= 29) - { - uint8_t kart_id = ns.getUInt8(); - Vec3 xyz = ns.getVec3(); - btQuaternion quat = ns.getQuat(); - m_next_positions [kart_id] = xyz; - m_next_quaternions[kart_id] = quat; - } // while ns.size()>29 - - // Set the flag that a new update was received - m_was_updated = true; - return true; -} // notifyEvent - -// ---------------------------------------------------------------------------- -/** Sends regular update events from the server to all clients and from the - * clients to the server (FIXME - is that actually necessary??) - * Then it applies all update events that have been received in notifyEvent. - * This two-part implementation means that if the server should send two - * or more updates before this client handles them, only the last one will - * actually be handled (i.e. outdated kart position updates are discarded). - */ -void KartUpdateProtocol::update(float dt) -{ - if (!World::getWorld()) - return; - - double current_time = StkTime::getRealTime(); - if (current_time > m_previous_time + 0.1) // 10 updates per second - { - m_previous_time = current_time; - if (NetworkConfig::get()->isServer()) - { - World *world = World::getWorld(); - NetworkString *ns = getNetworkString(4+world->getNumKarts()*29); - ns->setSynchronous(true); - ns->addFloat( world->getTime() ); - for (unsigned int i = 0; i < world->getNumKarts(); i++) - { - AbstractKart* kart = world->getKart(i); - Vec3 xyz = kart->getXYZ(); - ns->addUInt8( kart->getWorldKartId()); - ns->add(xyz).add(kart->getRotation()); - Log::verbose("KartUpdateProtocol", - "Sending %d's positions %f %f %f", - kart->getWorldKartId(), xyz[0], xyz[1], xyz[2]); - } - sendMessageToPeersChangingToken(ns, /*reliable*/false); - delete ns; - } - else - { - NetworkString *ns = - getNetworkString(4+29*race_manager->getNumLocalPlayers()); - ns->setSynchronous(true); - ns->addFloat(World::getWorld()->getTime()); - for(unsigned int i=0; igetNumLocalPlayers(); i++) - { - AbstractKart *kart = World::getWorld()->getLocalPlayerKart(i); - const Vec3 &xyz = kart->getXYZ(); - ns->addUInt8(kart->getWorldKartId()); - ns->add(xyz).add(kart->getRotation()); - Log::verbose("KartUpdateProtocol", - "Sending %d's positions %f %f %f", - kart->getWorldKartId(), xyz[0], xyz[1], xyz[2]); - } - sendToServer(ns, /*reliable*/false); - delete ns; - } // if server - } // if (current_time > time + 0.1) - - - // Now handle all update events that have been received. - // There is no lock necessary, since receiving new positions is done in - // notifyEvent, which is called from the same thread that calls this - // function. - if(m_was_updated) - { - for (unsigned id = 0; id < m_next_positions.size(); id++) - { - AbstractKart *kart = World::getWorld()->getKart(id); - if (!kart->getController()->isLocalPlayerController()) - { - btTransform transform = kart->getBody() - ->getInterpolationWorldTransform(); - transform.setOrigin(m_next_positions[id]); - transform.setRotation(m_next_quaternions[id]); - kart->getBody()->setCenterOfMassTransform(transform); - Log::verbose("KartUpdateProtocol", "Update kart %i pos", - id); - } // if not local player - } // for id < num_karts - m_was_updated = false; // mark that all updates were applied - } // if m_was_updated -} // update - diff --git a/src/network/protocols/kart_update_protocol.hpp b/src/network/protocols/kart_update_protocol.hpp deleted file mode 100644 index 04d83f488..000000000 --- a/src/network/protocols/kart_update_protocol.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef KART_UPDATE_PROTOCOL_HPP -#define KART_UPDATE_PROTOCOL_HPP - -#include "network/protocol.hpp" -#include "utils/cpp2011.hpp" -#include "utils/vec3.hpp" - -#include "LinearMath/btQuaternion.h" - -#include -#include "pthread.h" - -class AbstractKart; - -class KartUpdateProtocol : public Protocol -{ -private: - - /** Stores the last updated position for a kart. */ - std::vector m_next_positions; - - /** Stores the last updated rotation for a kart. */ - std::vector m_next_quaternions; - - /** True if a new update for the kart positions was received. */ - bool m_was_updated; - - /** Time the last kart update was sent. Used to send updates with - * a fixed frequency. */ - double m_previous_time; - -public: - KartUpdateProtocol(); - virtual ~KartUpdateProtocol(); - - virtual bool notifyEvent(Event* event) OVERRIDE; - virtual void setup() OVERRIDE; - virtual void update(float dt) OVERRIDE; - virtual void asynchronousUpdate() OVERRIDE {}; - -}; // KartUpdateProtocol - -#endif // KART_UPDATE_PROTOCOL_HPP diff --git a/src/network/protocols/lobby_protocol.cpp b/src/network/protocols/lobby_protocol.cpp index 4cb0e722b..74d1435a0 100644 --- a/src/network/protocols/lobby_protocol.cpp +++ b/src/network/protocols/lobby_protocol.cpp @@ -26,7 +26,6 @@ #include "network/protocol_manager.hpp" #include "network/protocols/game_protocol.hpp" #include "network/protocols/game_events_protocol.hpp" -#include "network/protocols/kart_update_protocol.hpp" #include "network/protocols/latency_protocol.hpp" #include "network/race_event_manager.hpp" #include "network/stk_host.hpp" @@ -124,7 +123,6 @@ void LobbyProtocol::loadWorld() // Load the actual world. m_game_setup->getRaceConfig()->loadWorld(); World::getWorld()->setNetworkWorld(true); - (new KartUpdateProtocol())->requestStart(); GameProtocol::getInstance()->requestStart(); (new GameEventsProtocol())->requestStart(); From a8d3b6b0dfc6daaa8cb2f21b323f85d4c676d8cf Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 9 Dec 2016 09:18:56 +1100 Subject: [PATCH 003/413] Fixed compiler warning. --- src/network/protocols/game_protocol.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/protocols/game_protocol.cpp b/src/network/protocols/game_protocol.cpp index a7d9df069..282321126 100644 --- a/src/network/protocols/game_protocol.cpp +++ b/src/network/protocols/game_protocol.cpp @@ -90,7 +90,7 @@ void GameProtocol::update(float dt) // Clear left-over data from previous frame. This way the network // string will increase till it reaches maximum size necessary m_data_to_send->clear(); - m_data_to_send->addUInt8(m_all_actions.size()); + m_data_to_send->addUInt8( uint8_t( m_all_actions.size() ) ); // Add all actions for (auto a : m_all_actions) From 0a2ea4bd086f187286d153127846fb393407125b Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 9 Dec 2016 16:39:37 +1100 Subject: [PATCH 004/413] Removed unused functions. --- src/modes/world.cpp | 16 ---------------- src/modes/world.hpp | 2 -- 2 files changed, 18 deletions(-) diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 293a060f2..7f0f0bdda 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -129,7 +129,6 @@ World::World() : WorldStatus(), m_clear_color(255,100,101,140) m_schedule_pause = false; m_schedule_unpause = false; m_schedule_exit_race = false; - m_self_destruct = false; m_schedule_tutorial = false; m_is_network_world = false; m_weather = NULL; @@ -840,12 +839,6 @@ void World::updateWorld(float dt) m_schedule_unpause = false; } - if (m_self_destruct) - { - delete this; - return; - } - // Don't update world if a menu is shown or the race is over. if( getPhase() == FINISH_PHASE || getPhase() == IN_GAME_MENU_PHASE ) @@ -1276,15 +1269,6 @@ void World::unpause() } } // pause -//----------------------------------------------------------------------------- -/** Call when the world needs to be deleted but you can't do it immediately - * because you are e.g. within World::update() - */ -void World::delayedSelfDestruct() -{ - m_self_destruct = true; -} // delayedSelfDestruct - //----------------------------------------------------------------------------- void World::escapePressed() { diff --git a/src/modes/world.hpp b/src/modes/world.hpp index fedeb4e79..06181dd24 100644 --- a/src/modes/world.hpp +++ b/src/modes/world.hpp @@ -357,8 +357,6 @@ public: * quadgraph. Override to change value. */ virtual bool useChecklineRequirements() const { return false; } // ------------------------------------------------------------------------ - void delayedSelfDestruct(); - // ------------------------------------------------------------------------ virtual void escapePressed(); // ------------------------------------------------------------------------ virtual void loadCustomModels() {} From b73d08d08996d3bf5b58d6666331d7143c033073 Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 9 Dec 2016 16:39:59 +1100 Subject: [PATCH 005/413] Removed unnecessary cast. --- src/main_loop.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 1e0f1daca..e647d2a15 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -141,8 +141,8 @@ float MainLoop::getLimitedDt() void MainLoop::updateRace(float dt) { // The race event manager will update world in case of an online race - if (RaceEventManager::getInstance()->isRunning()) - RaceEventManager::getInstance()->update(dt); + if (RaceEventManager::getInstance()->isRunning()) + RaceEventManager::getInstance()->update(dt); else World::getWorld()->updateWorld(dt); } // updateRace From 95ca3fcac1efb9a9d74f4d12ee262e43b7ffcb33 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 14 Dec 2016 17:23:22 +1100 Subject: [PATCH 006/413] Try to fix rare GUI crash, likely caused by pushing a screen in the network thread while the gui is being drawn. Made the messages causing this synchronous, i.e. executed by the main thread. --- src/network/protocols/client_lobby.cpp | 2 +- src/network/protocols/server_lobby.cpp | 28 +++++++++++++++++++++++-- src/network/protocols/server_lobby.hpp | 1 + src/states_screens/networking_lobby.cpp | 1 + 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index 8941a449b..16ffc9ab5 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -237,6 +237,7 @@ bool ClientLobby::notifyEvent(Event* event) message_type); switch(message_type) { + case LE_START_SELECTION: startSelection(event); break; case LE_KART_SELECTION_UPDATE: kartSelectionUpdate(event); break; case LE_LOAD_WORLD: loadWorld(); break; case LE_RACE_FINISHED: raceFinished(event); break; @@ -266,7 +267,6 @@ bool ClientLobby::notifyEventAsynchronous(Event* event) case LE_NEW_PLAYER_CONNECTED: newPlayer(event); break; case LE_PLAYER_DISCONNECTED : disconnectedPlayer(event); break; case LE_START_RACE: startGame(event); break; - case LE_START_SELECTION: startSelection(event); break; case LE_CONNECTION_REFUSED: connectionRefused(event); break; case LE_CONNECTION_ACCEPTED: connectionAccepted(event); break; case LE_KART_SELECTION_REFUSED: kartSelectionRefused(event); break; diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 144ba5e5b..bfc7bbd76 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -122,6 +122,29 @@ void ServerLobby::setup() } // setup +//----------------------------------------------------------------------------- +bool ServerLobby::notifyEvent(Event* event) +{ + assert(m_game_setup); // assert that the setup exists + if (event->getType() != EVENT_TYPE_MESSAGE) + return false; + + NetworkString &data = event->data(); + assert(data.size()); // message not empty + uint8_t message_type; + message_type = data.getUInt8(); + Log::info("ServerLobby", "Synchronous message received with type %d.", + message_type); + switch (message_type) + { + case LE_REQUEST_BEGIN: startSelection(event); break; + default: Log::error("ServerLobby", "Unknown message type %d - ignored.", + message_type); + break; + } // switch message_type + return true; +} // notifyEvent + //----------------------------------------------------------------------------- bool ServerLobby::notifyEventAsynchronous(Event* event) @@ -138,7 +161,6 @@ bool ServerLobby::notifyEventAsynchronous(Event* event) switch(message_type) { case LE_CONNECTION_REQUESTED: connectionRequested(event); break; - case LE_REQUEST_BEGIN: startSelection(event); break; case LE_KART_SELECTION: kartSelectionRequested(event); break; case LE_CLIENT_LOADED_WORLD: finishedLoadingWorldClient(event); break; case LE_STARTED_RACE: startedRaceOnClient(event); break; @@ -357,7 +379,9 @@ void ServerLobby::startSelection(const Event *event) } const std::vector &peers = STKHost::get()->getPeers(); NetworkString *ns = getNetworkString(1); - // start selection + // Start selection - must be synchronous since the receiver pushes + // a new screen, which must be donefrom the main thread. + ns->setSynchronous(true); ns->addUInt8(LE_START_SELECTION); sendMessageToPeersChangingToken(ns, /*reliable*/true); delete ns; diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index d358a545f..0121cc1dd 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -76,6 +76,7 @@ public: virtual ~ServerLobby(); virtual bool notifyEventAsynchronous(Event* event) OVERRIDE; + virtual bool notifyEvent(Event* event) OVERRIDE; virtual void setup() OVERRIDE; virtual void update(float dt) OVERRIDE; virtual void asynchronousUpdate() OVERRIDE {}; diff --git a/src/states_screens/networking_lobby.cpp b/src/states_screens/networking_lobby.cpp index 736c47dea..2a3a68b84 100644 --- a/src/states_screens/networking_lobby.cpp +++ b/src/states_screens/networking_lobby.cpp @@ -170,6 +170,7 @@ void NetworkingLobby::eventCallback(Widget* widget, const std::string& name, { // Send a message to the server to start NetworkString start(PROTOCOL_LOBBY_ROOM); + start.setSynchronous(true); start.addUInt8(LobbyProtocol::LE_REQUEST_BEGIN); STKHost::get()->sendToServer(&start, true); } From 77c14152c230983776446e55a45de1d9c85b7cda Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 15 Dec 2016 08:12:04 +1100 Subject: [PATCH 007/413] The server now uses the RewindManager to receive network events and takes the kart input from this queue of events. --- src/items/attachment.cpp | 2 +- src/karts/controller/kart_control.cpp | 16 +++--- .../controller/local_player_controller.cpp | 6 ++- src/modes/world.cpp | 2 +- src/network/protocols/game_protocol.cpp | 51 ++++++++++++++---- src/network/protocols/game_protocol.hpp | 9 +++- src/network/protocols/lobby_protocol.cpp | 2 + src/network/race_event_manager.cpp | 4 ++ src/network/rewind_manager.cpp | 40 ++++++++++++-- src/network/rewind_manager.hpp | 52 +++++++++---------- 10 files changed, 131 insertions(+), 53 deletions(-) diff --git a/src/items/attachment.cpp b/src/items/attachment.cpp index a6a32d44b..f2486399c 100644 --- a/src/items/attachment.cpp +++ b/src/items/attachment.cpp @@ -191,7 +191,7 @@ void Attachment::set(AttachmentType type, float time, { BareNetworkString *buffer = new BareNetworkString(2); saveState(buffer); - rwm->addEvent(this, buffer); + rwm->addEvent(this, buffer, /*confirmed*/true); } #endif } // set diff --git a/src/karts/controller/kart_control.cpp b/src/karts/controller/kart_control.cpp index 8075a68f2..7ba61b354 100644 --- a/src/karts/controller/kart_control.cpp +++ b/src/karts/controller/kart_control.cpp @@ -75,7 +75,7 @@ void KartControl::setSteer(float f) // Save full status BareNetworkString *buffer = new BareNetworkString(getLength()); copyToBuffer(buffer); - RewindManager::get()->addEvent(this, buffer); + RewindManager::get()->addEvent(this, buffer, true); } } // setSteer @@ -90,7 +90,7 @@ void KartControl::setAccel(float f) { BareNetworkString *buffer = new BareNetworkString(getLength()); copyToBuffer(buffer); - RewindManager::get()->addEvent(this, buffer); + RewindManager::get()->addEvent(this, buffer, true); } } // setAccel @@ -106,7 +106,7 @@ void KartControl::setBrake(bool b) // Only store the buttons in this case BareNetworkString *buffer = new BareNetworkString(1); buffer->addUInt8(getButtonsCompressed()); - RewindManager::get()->addEvent(this, buffer); + RewindManager::get()->addEvent(this, buffer, true); } } // setBrake // ---------------------------------------------------------------------------- @@ -120,7 +120,7 @@ void KartControl::setNitro(bool b) { BareNetworkString *buffer = new BareNetworkString(1); buffer->addUInt8(getButtonsCompressed()); - RewindManager::get()->addEvent(this, buffer); + RewindManager::get()->addEvent(this, buffer, true); } } // setNitro @@ -135,7 +135,7 @@ void KartControl::setSkidControl(SkidControl sc) { BareNetworkString *buffer = new BareNetworkString(1); buffer->addUInt8(getButtonsCompressed()); - RewindManager::get()->addEvent(this, buffer); + RewindManager::get()->addEvent(this, buffer, true); } } // seSkidControl @@ -150,7 +150,7 @@ void KartControl::setRescue(bool b) { BareNetworkString *buffer = new BareNetworkString(1); buffer->addUInt8(getButtonsCompressed()); - RewindManager::get()->addEvent(this, buffer); + RewindManager::get()->addEvent(this, buffer, true); } } // setRescue @@ -165,7 +165,7 @@ void KartControl::setFire(bool b) { BareNetworkString *buffer = new BareNetworkString(1); buffer->addUInt8(getButtonsCompressed()); - RewindManager::get()->addEvent(this, buffer); + RewindManager::get()->addEvent(this, buffer, true); } } // setFire @@ -180,6 +180,6 @@ void KartControl::setLookBack(bool b) { BareNetworkString *buffer = new BareNetworkString(1); buffer->addUInt8(getButtonsCompressed()); - RewindManager::get()->addEvent(this, buffer); + RewindManager::get()->addEvent(this, buffer, true); } } // setLookBack diff --git a/src/karts/controller/local_player_controller.cpp b/src/karts/controller/local_player_controller.cpp index d08d3d972..7bc300185 100644 --- a/src/karts/controller/local_player_controller.cpp +++ b/src/karts/controller/local_player_controller.cpp @@ -39,6 +39,7 @@ #include "modes/world.hpp" #include "network/network_config.hpp" #include "network/protocols/game_protocol.hpp" +#include "network/rewind_manager.hpp" #include "race/history.hpp" #include "states_screens/race_gui_base.hpp" #include "tracks/track.hpp" @@ -143,9 +144,10 @@ void LocalPlayerController::action(PlayerAction action, int value) { PlayerController::action(action, value); - // If this is a client, send the action to the server + // If this is a client, send the action to networking layer if (World::getWorld()->isNetworkWorld() && - NetworkConfig::get()->isClient() ) + NetworkConfig::get()->isClient() && + !RewindManager::get()->isRewinding() ) { GameProtocol::getInstance()->controllerAction(m_kart->getWorldKartId(), action, value); diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 97c947537..e72904a09 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -1022,7 +1022,7 @@ void World::update(float dt) void World::updateTime(const float dt) { WorldStatus::updateTime(dt); - RewindManager::get()->setCurrentTime(getTime(), dt); + RewindManager::get()->setCurrentTime(getTime()); } // updateTime // ---------------------------------------------------------------------------- diff --git a/src/network/protocols/game_protocol.cpp b/src/network/protocols/game_protocol.cpp index 282321126..9f3f9eab0 100644 --- a/src/network/protocols/game_protocol.cpp +++ b/src/network/protocols/game_protocol.cpp @@ -19,7 +19,7 @@ #include "modes/world.hpp" #include "karts/abstract_kart.hpp" -#include "karts/controller/controller.hpp" +#include "karts/controller/player_controller.hpp" #include "network/event.hpp" #include "network/network_config.hpp" #include "network/network_player_profile.hpp" @@ -27,6 +27,7 @@ #include "network/network_config.hpp" #include "network/network_string.hpp" #include "network/protocol_manager.hpp" +#include "network/rewind_manager.hpp" #include "network/stk_host.hpp" #include "network/stk_peer.hpp" #include "utils/log.hpp" @@ -56,14 +57,14 @@ bool GameProtocol::notifyEventAsynchronous(Event* event) { float time = data.getFloat(); uint8_t kart_id = data.getUInt8(); + assert(kart_id < World::getWorld()->getNumKarts()); PlayerAction action = (PlayerAction)(data.getUInt8()); int value = data.getUInt32(); Log::info("GameProtocol", "Action at %f: %d %d %d", time, kart_id, action, value); - assert(kart_id < World::getWorld()->getNumKarts()); - Controller *controller = World::getWorld()->getKart(kart_id) - ->getController(); - controller->action(action, value); + BareNetworkString *s = new BareNetworkString(3); + s->addUInt8(kart_id).addUInt8(action).addUInt16(value); + RewindManager::get()->addEvent(this, s, /*confirmed*/ true, time); } if (data.size() > 0 ) @@ -107,8 +108,8 @@ void GameProtocol::update(float dt) //----------------------------------------------------------------------------- /** Called from the local kart controller when an action (like steering, - * acceleration, ...) was triggered. It compresses the current kart control - * state and sends a message with the new info to the server. + * acceleration, ...) was triggered. It sends a message with the new info + * to the server and informs the rewind manager to store the event. * \param Kart id that triggered the action. * \param action Which action was triggered. * \param value New value for the given action. @@ -116,6 +117,8 @@ void GameProtocol::update(float dt) void GameProtocol::controllerAction(int kart_id, PlayerAction action, int value) { + // Store the action in the list of actions that will be sent to the + // server next. assert(NetworkConfig::get()->isClient()); Action a; a.m_kart_id = kart_id; @@ -125,6 +128,36 @@ void GameProtocol::controllerAction(int kart_id, PlayerAction action, m_all_actions.push_back(a); - - Log::info("GameProtocol", "Action %d value %d", action, value); + // Store the event in the rewind manager, which is responsible + // for freeing the allocated memory + BareNetworkString *s = new BareNetworkString(4); + s->addUInt8(kart_id).addUInt8(action).addUInt16(uint16_t(value)); + RewindManager::get()->addEvent(this, s, /*confirmed*/true, + World::getWorld()->getTime() ); + + Log::info("GameProtocol", "Action at %f: %d value %d", + World::getWorld()->getTime(), action, value); } // controllerAction + +// ---------------------------------------------------------------------------- +/** Called from the RewindManager when rolling back. + * \param buffer Pointer to the saved state information. + */ +void GameProtocol::undo(BareNetworkString *buffer) +{ + +} // undo + +// ---------------------------------------------------------------------------- +/** Called from the RewindManager after a rollback to replay the stored + * events. + * \param buffer Pointer to the saved state information. + */ +void GameProtocol::rewind(BareNetworkString *buffer) +{ + int kart_id = buffer->getUInt8(); + PlayerAction action = PlayerAction(buffer->getUInt8()); + int value = buffer->getUInt16(); + Controller *c = World::getWorld()->getKart(kart_id)->getController(); + c->action(action, value); +} // rewind diff --git a/src/network/protocols/game_protocol.hpp b/src/network/protocols/game_protocol.hpp index 7f9dcf63c..7065917da 100644 --- a/src/network/protocols/game_protocol.hpp +++ b/src/network/protocols/game_protocol.hpp @@ -19,6 +19,7 @@ #ifndef GAME_PROTOCOL_HPP #define GAME_PROTOCOL_HPP +#include "network/event_rewinder.hpp" #include "network/protocol.hpp" #include "input/input.hpp" // for PlayerAction @@ -27,10 +28,11 @@ #include +class BareNetworkString; class NetworkString; - class GameProtocol : public Protocol + , public EventRewinder , public Singleton { private: @@ -59,13 +61,16 @@ public: virtual bool notifyEventAsynchronous(Event* event) OVERRIDE; virtual void update(float dt) OVERRIDE; + void controllerAction(int kart_id, PlayerAction action, int value); + virtual void undo(BareNetworkString *buffer) OVERRIDE; + virtual void rewind(BareNetworkString *buffer) OVERRIDE; // ------------------------------------------------------------------------ virtual void setup() OVERRIDE {}; // ------------------------------------------------------------------------ virtual void asynchronousUpdate() OVERRIDE {} - + }; // class GameProtocol #endif // GAME_PROTOCOL_HPP diff --git a/src/network/protocols/lobby_protocol.cpp b/src/network/protocols/lobby_protocol.cpp index 74d1435a0..b084be5d9 100644 --- a/src/network/protocols/lobby_protocol.cpp +++ b/src/network/protocols/lobby_protocol.cpp @@ -28,6 +28,7 @@ #include "network/protocols/game_events_protocol.hpp" #include "network/protocols/latency_protocol.hpp" #include "network/race_event_manager.hpp" +#include "network/rewind_manager.hpp" #include "network/stk_host.hpp" #include "race/race_manager.hpp" #include "states_screens/state_manager.hpp" @@ -56,6 +57,7 @@ LobbyProtocol::~LobbyProtocol() void LobbyProtocol::loadWorld() { Log::info("LobbyProtocol", "Ready !"); + RewindManager::setEnable(true); // Race startup sequence // --------------------- diff --git a/src/network/race_event_manager.cpp b/src/network/race_event_manager.cpp index e802a8f98..51a69ad39 100644 --- a/src/network/race_event_manager.cpp +++ b/src/network/race_event_manager.cpp @@ -6,6 +6,7 @@ #include "network/network_config.hpp" #include "network/protocol_manager.hpp" #include "network/protocols/game_events_protocol.hpp" +#include "network/rewind_manager.hpp" RaceEventManager::RaceEventManager() @@ -29,6 +30,9 @@ void RaceEventManager::update(float dt) if(!ProtocolManager::getInstance()) return; + // Replay all recorded events up to the current time: + RewindManager::get()->playEventsTill(World::getWorld()->getTime()); + World::getWorld()->updateWorld(dt); // if the race is over diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 7d33a764a..1e9ca5a1c 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -83,6 +83,7 @@ void RewindManager::reset() m_overall_state_size = 0; m_state_frequency = 0.1f; // save 10 states a second m_last_saved_state = -9999.9f; // forces initial state save + m_next_event = 0; if(!m_enable_rewind_manager) return; @@ -108,6 +109,12 @@ void RewindManager::reset() } // reset // ---------------------------------------------------------------------------- +/** Inserts a RewindInfo object in the list of all events at the correct time. + * If there are several RewindInfo at the exact same time, state RewindInfo + * will be insert at the front, and event and time info at the end of the + * RewindInfo with the same time. + * \param ri The RewindInfo object to insert. + */ void RewindManager::insertRewindInfo(RewindInfo *ri) { #ifdef REWIND_SEARCH_STATS @@ -211,7 +218,8 @@ unsigned int RewindManager::findFirstIndex(float target_time) const * \param buffer Pointer to the event data. */ void RewindManager::addEvent(EventRewinder *event_rewinder, - BareNetworkString *buffer) + BareNetworkString *buffer, bool confirmed, + float time ) { if(m_is_rewinding) { @@ -219,8 +227,11 @@ void RewindManager::addEvent(EventRewinder *event_rewinder, Log::error("RewindManager", "Adding event when rewinding"); return; } - RewindInfo *ri = new RewindInfoEvent(getCurrentTime(), event_rewinder, - buffer, /*is confirmed*/true); + Log::verbose("time", "wolrd %f rewind %f", World::getWorld()->getTime(), getCurrentTime()); + if (time < 0) + time = getCurrentTime(); + RewindInfo *ri = new RewindInfoEvent(time, event_rewinder, + buffer, confirmed); insertRewindInfo(ri); } // addEvent @@ -270,6 +281,29 @@ void RewindManager::saveStates() m_last_saved_state = time; } // saveStates +// ---------------------------------------------------------------------------- +/** Replays all events from the last event played till the specified time. + * \param time Up to (and inclusive) which time events will be replayed. + */ +void RewindManager::playEventsTill(float time) +{ + assert(!m_is_rewinding); + m_is_rewinding = true; + while (m_next_event < m_rewind_info.size()) + { + RewindInfo *ri = m_rewind_info[m_next_event]; + if (ri->getTime() > time) + { + m_is_rewinding = false; + return; + } + m_next_event++; + if(ri->isEvent()) + ri->rewind(); + } + m_is_rewinding = false; +} // playEventsTill + // ---------------------------------------------------------------------------- /** Rewinds to the specified time. * \param t Time to rewind to. diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index 653072e47..7e2cc498d 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -93,6 +93,9 @@ private: AllRewindInfo m_rewind_info; + /** Index of the next event to be used when playing events. */ + unsigned int m_next_event; + /** Overall amount of memory allocated by states. */ unsigned int m_overall_state_size; @@ -111,9 +114,6 @@ private: * events later. */ float m_current_time; - /** The current time step size. */ - float m_time_step; - #define REWIND_SEARCH_STATS #ifdef REWIND_SEARCH_STATS @@ -134,33 +134,13 @@ public: static RewindManager *create(); static void destroy(); // ------------------------------------------------------------------------ - /** Sets the time that is to be used for all further states or events, - * and the time step size. This is necessary so that states/events before - * and after World::m_time is increased have the same time stamp. - * \param t Time. - * \param dt Time step size. - */ - void setCurrentTime(float t, float dt) - { - m_current_time = t; - m_time_step = dt; - } // setCurrentTime - - // ------------------------------------------------------------------------ - /** Returns the current time. */ - float getCurrentTime() const { return m_current_time; } - // ------------------------------------------------------------------------ - float getCurrentTimeStep() const { return m_time_step; } - // ------------------------------------------------------------------------ /** En- or disables rewinding. */ - static void setEnable(bool m) { m_enable_rewind_manager = m;} - + static void setEnable(bool m) { m_enable_rewind_manager = m; } // ------------------------------------------------------------------------ /** Returns if rewinding is enabled or not. */ static bool isEnabled() { return m_enable_rewind_manager; } - // ------------------------------------------------------------------------ - /** Returns the singleton. This function will not automatically create + /** Returns the singleton. This function will not automatically create * the singleton. */ static RewindManager *get() { @@ -168,12 +148,30 @@ public: return m_rewind_manager; } // get - // ------------------------------------------------------------------------ + // Non-static functtion declarations: void reset(); void saveStates(); void rewindTo(float target_time); - void addEvent(EventRewinder *event_rewinder, BareNetworkString *buffer); + void playEventsTill(float time); + void addEvent(EventRewinder *event_rewinder, BareNetworkString *buffer, + bool confirmed, float time = -1.0f); + // ------------------------------------------------------------------------ + /** Sets the time that is to be used for all further states or events, + * and the time step size. This is necessary so that states/events before + * and after World::m_time is increased have the same time stamp. + * \param t Time. + * \param dt Time step size. + */ + void setCurrentTime(float t) + { + m_current_time = t; + } // setCurrentTime + + // ------------------------------------------------------------------------ + /** Returns the current time. */ + float getCurrentTime() const { return m_current_time; } + // ------------------------------------------------------------------------ /** Adds a Rewinder to the list of all rewinders. * \return true If rewinding is enabled, false otherwise. From 5cde8f785f1746b93889d59520301af5e05254d8 Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 16 Dec 2016 23:26:34 +1100 Subject: [PATCH 008/413] Fix crash in normal race (RaceEventManager NULL). --- src/items/item_manager.cpp | 2 +- src/karts/kart.cpp | 5 ++--- src/main_loop.cpp | 3 ++- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/items/item_manager.cpp b/src/items/item_manager.cpp index 24bf8f4dc..ce0e519d8 100644 --- a/src/items/item_manager.cpp +++ b/src/items/item_manager.cpp @@ -324,7 +324,7 @@ void ItemManager::checkItemHit(AbstractKart* kart) if((*i)->hitKart(kart->getXYZ(), kart)) { // if we're not playing online, pick the item. - if (!RaceEventManager::getInstance()->isRunning()) + if (!NetworkConfig::get()->isNetworking()) collectedItem(*i, kart); else if (NetworkConfig::get()->isServer()) { diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index b4d250c22..b9c1e49c4 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -67,7 +67,6 @@ #include "modes/soccer_world.hpp" #include "modes/world.hpp" #include "network/network_config.hpp" -#include "network/race_event_manager.hpp" #include "network/rewind_manager.hpp" #include "physics/btKart.hpp" #include "physics/btKartRaycast.hpp" @@ -1477,8 +1476,8 @@ void Kart::update(float dt) // Check if any item was hit. // check it if we're not in a network world, or if we're on the server // (when network mode is on) - if (!RaceEventManager::getInstance()->isRunning() || - NetworkConfig::get()->isServer()) + if(!NetworkConfig::get()->isNetworking() || + NetworkConfig::get()->isServer() ) ItemManager::get()->checkItemHit(this); static video::SColor pink(255, 255, 133, 253); diff --git a/src/main_loop.cpp b/src/main_loop.cpp index e647d2a15..84f329a8e 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -141,7 +141,8 @@ float MainLoop::getLimitedDt() void MainLoop::updateRace(float dt) { // The race event manager will update world in case of an online race - if (RaceEventManager::getInstance()->isRunning()) + if ( RaceEventManager::getInstance() && + RaceEventManager::getInstance()->isRunning() ) RaceEventManager::getInstance()->update(dt); else World::getWorld()->updateWorld(dt); From 1e372d6e77f032dc6ea6e76edfeea3e06f7c961a Mon Sep 17 00:00:00 2001 From: hiker Date: Sat, 17 Dec 2016 10:32:21 +1100 Subject: [PATCH 009/413] Added separate queue for storing network events (to reduce synchronisation); replaced vector with std::list in preparation for making the RewindManager threadsafe. --- src/network/protocols/game_protocol.cpp | 2 +- src/network/rewind_manager.cpp | 166 +++++++++++++++--------- src/network/rewind_manager.hpp | 21 ++- 3 files changed, 126 insertions(+), 63 deletions(-) diff --git a/src/network/protocols/game_protocol.cpp b/src/network/protocols/game_protocol.cpp index 9f3f9eab0..820774e25 100644 --- a/src/network/protocols/game_protocol.cpp +++ b/src/network/protocols/game_protocol.cpp @@ -64,7 +64,7 @@ bool GameProtocol::notifyEventAsynchronous(Event* event) time, kart_id, action, value); BareNetworkString *s = new BareNetworkString(3); s->addUInt8(kart_id).addUInt8(action).addUInt16(value); - RewindManager::get()->addEvent(this, s, /*confirmed*/ true, time); + RewindManager::get()->addNetworkEvent(this, s, time); } if (data.size() > 0 ) diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 1e9ca5a1c..cdad50941 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -62,10 +62,11 @@ RewindManager::RewindManager() RewindManager::~RewindManager() { // Destroying the - for(unsigned int i=0; igetTime() > t) + while(i!=m_rewind_info.rend() && (*i)->getTime() > t) { #ifdef REWIND_SEARCH_STATS m_count_of_comparisons++; @@ -140,10 +150,10 @@ void RewindManager::insertRewindInfo(RewindInfo *ri) return; } - else // is a state + else // is a state or time { // If there are several infos for the same time t, - // a state must be inserted first + // a state or time entry must be inserted first AllRewindInfo::reverse_iterator i = m_rewind_info.rbegin(); while(i!=m_rewind_info.rend() && (*i)->getTime() >= t) { @@ -166,7 +176,8 @@ void RewindManager::insertRewindInfo(RewindInfo *ri) * \param time Time for which an index is searched. * \return Index in m_rewind_info after which to add rewind data. */ -unsigned int RewindManager::findFirstIndex(float target_time) const +RewindManager::AllRewindInfo::reverse_iterator + RewindManager::findFirstIndex(float target_time) { // For now do a linear search, even though m_rewind_info is sorted // I would expect that most insertions will be towards the (very) @@ -179,25 +190,25 @@ unsigned int RewindManager::findFirstIndex(float target_time) const #ifdef REWIND_SEARCH_STATS m_count_of_searches++; #endif - int index = m_rewind_info.size()-1; - int index_last_state = -1; - while(index>=0) + AllRewindInfo::reverse_iterator index = m_rewind_info.rbegin(); + AllRewindInfo::reverse_iterator index_last_state = m_rewind_info.rend(); + while(index!=m_rewind_info.rend()) { #ifdef REWIND_SEARCH_STATS m_count_of_comparisons++; #endif - if(m_rewind_info[index]->isState()) + if((*index)->isState()) { - if(m_rewind_info[index]->getTime()getTime()getTime()); + target_time, (*index_last_state)->getTime()); return index_last_state; // avoid compiler warning } // findFirstIndex @@ -235,6 +246,27 @@ void RewindManager::addEvent(EventRewinder *event_rewinder, insertRewindInfo(ri); } // addEvent +// ---------------------------------------------------------------------------- +/** Adds an event to the list of network rewind data. This function is + * threadsafe so can be called by the network thread. The data is synched + * to m_rewind_info by the main thread. The data to be stored must be + * allocated and not freed by the caller! + * \param time Time at which the event was recorded. + * \param buffer Pointer to the event data. + */ +void RewindManager::addNetworkEvent(EventRewinder *event_rewinder, + BareNetworkString *buffer, float time) +{ + RewindInfo *ri = new RewindInfoEvent(time, event_rewinder, + buffer, /*confirmed*/true); + + // For now keep the freshly received events unsorted, they will + // be sorted when merged into m_rewind_info + m_network_events.lock(); + m_network_events.getData().push_back(ri); + m_network_events.unlock(); +} // addEvent + // ---------------------------------------------------------------------------- /** Determines if a new state snapshot should be taken, and if so calls all * rewinder to do so. @@ -257,14 +289,15 @@ void RewindManager::saveStates() } // For now always create a snapshot. - for(unsigned int i=0; isaveState(); + BareNetworkString *buffer = (*i)->saveState(); if(buffer && buffer->size()>=0) { m_overall_state_size += buffer->size(); RewindInfo *ri = new RewindInfoState(getCurrentTime(), - m_all_rewinder[i], buffer, + *i, buffer, /*is_confirmed*/true); assert(ri); insertRewindInfo(ri); @@ -287,11 +320,37 @@ void RewindManager::saveStates() */ void RewindManager::playEventsTill(float time) { + if (m_next_event== m_rewind_info.end()) + return; + + /** First merge all newly received network events into the main event list */ + float current_time = (*m_next_event)->getTime(); + bool rewind_necessary = false; + + AllRewindInfo::const_iterator i; + m_network_events.lock(); + for (i = m_network_events.getData().begin(); + i != m_network_events.getData().end(); i++) + { + if ((*i)->getTime() < current_time) + rewind_necessary = true; + if (rewind_necessary) + { + Log::info("RewindManager", "Rewind necessary at %f because of event at %f", + current_time, (*i)->getTime()); + } + insertRewindInfo(*i); + } + // Note that clear does not destruct the elements (which are now pointed + // to by m_rewind_info). + m_network_events.getData().clear(); + m_network_events.unlock(); + assert(!m_is_rewinding); m_is_rewinding = true; - while (m_next_event < m_rewind_info.size()) + while (m_next_event !=m_rewind_info.end() ) { - RewindInfo *ri = m_rewind_info[m_next_event]; + RewindInfo *ri = *m_next_event; if (ri->getTime() > time) { m_is_rewinding = false; @@ -317,9 +376,10 @@ void RewindManager::rewindTo(float rewind_time) // First find the state to which we need to rewind // ------------------------------------------------ - unsigned int index = findFirstIndex(rewind_time); + AllRewindInfo::reverse_iterator rindex = findFirstIndex(rewind_time); + AllRewindInfo::iterator index = --(rindex.base()); - if(!m_rewind_info[index]->isState()) + if(!(*index)->isState()) { Log::error("RewindManager", "No state for rewind to %f, state %d.", rewind_time, index); @@ -328,16 +388,17 @@ void RewindManager::rewindTo(float rewind_time) // Then undo the rewind infos going backwards in time // -------------------------------------------------- - for(int i=m_rewind_info.size()-1; i>=(int)index; i--) + AllRewindInfo::reverse_iterator i; + for(i= m_rewind_info.rbegin(); i!=rindex; i++) { - m_rewind_info[i]->undo(); + (*i)->undo(); // Now all states after the time we rewind to are not confirmed // anymore. They need to be rewritten when going forward during // the rewind. - if(m_rewind_info[i]->isState() && - m_rewind_info[i]->getTime() > m_rewind_info[index]->getTime() ) - m_rewind_info[i]->setConfirmed(false); + if((*i)->isState() && + (*i)->getTime() > (*index)->getTime() ) + (*i)->setConfirmed(false); } // for i>state @@ -348,7 +409,7 @@ void RewindManager::rewindTo(float rewind_time) // Get the (first) full state to which we have to rewind RewindInfoState *state = - dynamic_cast(m_rewind_info[index]); + dynamic_cast(*index); // Store the time to which we have to replay to float exact_rewind_time = state->getTime(); @@ -365,29 +426,28 @@ void RewindManager::rewindTo(float rewind_time) { state->rewind(); index++; - if(index>=m_rewind_info.size()) break; - state = dynamic_cast(m_rewind_info[index]); + if(index==m_rewind_info.end()) break; + state = dynamic_cast(*index); } // Now go forward through the list of rewind infos: // ------------------------------------------------ - while( world->getTime() < current_time && - index < (int)m_rewind_info.size() ) + while( world->getTime() < current_time && index !=m_rewind_info.end() ) { // Now handle all states and events at the current time before // updating the world: - while(index < (int)m_rewind_info.size() && - m_rewind_info[index]->getTime()<=world->getTime()+0.001f) + while(index !=m_rewind_info.end() && + (*index)->getTime()<=world->getTime()+0.001f) { - if(m_rewind_info[index]->isState()) + if((*index)->isState()) { // TOOD: replace the old state with a new state. // For now just set it to confirmed - m_rewind_info[index]->setConfirmed(true); + (*index)->setConfirmed(true); } - else if(m_rewind_info[index]->isEvent()) + else if((*index)->isEvent()) { - m_rewind_info[index]->rewind(); + (*index)->rewind(); } index++; } @@ -413,27 +473,17 @@ void RewindManager::rewindTo(float rewind_time) * return a dt that would be bigger tham this value. * \return The time step size to use in the next simulation step. */ -float RewindManager::determineTimeStepSize(int next_state, float end_time) +float RewindManager::determineTimeStepSize(AllRewindInfo::iterator next_state, + float end_time) { // If there is a next state (which is known to have a different time) // use the time difference to determine the time step size. - if(next_state < (int)m_rewind_info.size()) - return m_rewind_info[next_state]->getTime() - World::getWorld()->getTime(); + if(next_state !=m_rewind_info.end()) + return (*next_state)->getTime() - World::getWorld()->getTime(); // Otherwise, i.e. we are rewinding the last state/event, take the // difference between that time and the world time at which the rewind // was triggered. - return end_time - m_rewind_info[next_state-1]->getTime(); + return end_time - (*(--next_state))->getTime(); - - - float dt = 1.0f/60.0f; - float t = World::getWorld()->getTime(); - if(m_rewind_info[next_state]->getTime() < t + dt) - { - // Since we have RewindInfo at that time, it is certain that - /// this time is before (or at) end_time, not after. - return m_rewind_info[next_state]->getTime()-t; - } - return t+dt < end_time ? dt : end_time - t; } // determineTimeStepSize diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index 7e2cc498d..95b90ea14 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -21,8 +21,10 @@ #include "network/rewinder.hpp" #include "utils/ptr_vector.hpp" +#include "utils/synchronised.hpp" #include +#include #include class RewindInfo; @@ -89,12 +91,20 @@ private: AllRewinder m_all_rewinder; /** Pointer to all saved states. */ - typedef std::vector AllRewindInfo; + typedef std::list AllRewindInfo; + /** The list of all events that are affected by a rewind. */ AllRewindInfo m_rewind_info; + /** The list of all events received from the network. They are stored + * in a separate thread (so this data structure is thread-save), and + * merged into m_rewind_info from the main thread. This design (as + * opposed to locking m_rewind_info) reduces the synchronisation + * between main thread and network thread. */ + Synchronised m_network_events; + /** Index of the next event to be used when playing events. */ - unsigned int m_next_event; + AllRewindInfo::const_iterator m_next_event; /** Overall amount of memory allocated by states. */ unsigned int m_overall_state_size; @@ -125,9 +135,10 @@ private: RewindManager(); ~RewindManager(); - unsigned int findFirstIndex(float time) const; + AllRewindInfo::reverse_iterator findFirstIndex(float time); void insertRewindInfo(RewindInfo *ri); - float determineTimeStepSize(int state, float max_time); + float determineTimeStepSize(AllRewindInfo::iterator state, float max_time); + public: // First static functions to manage rewinding. // =========================================== @@ -156,6 +167,8 @@ public: void playEventsTill(float time); void addEvent(EventRewinder *event_rewinder, BareNetworkString *buffer, bool confirmed, float time = -1.0f); + void addNetworkEvent(EventRewinder *event_rewinder, + BareNetworkString *buffer, float time); // ------------------------------------------------------------------------ /** Sets the time that is to be used for all further states or events, * and the time step size. This is necessary so that states/events before From 084869263d4fd3b404ab9ed03e1bc9311ac44ad8 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 20 Dec 2016 09:41:50 +1100 Subject: [PATCH 010/413] Use a sort function to sort the rewind info events and network events, so that the merge function can be used to combine them. The RewindManager is now threadsafe. --- src/network/rewind_manager.cpp | 134 +++++++++++++++++---------------- src/network/rewind_manager.hpp | 5 ++ 2 files changed, 74 insertions(+), 65 deletions(-) diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index cdad50941..313ac6aa6 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -27,6 +27,8 @@ #include "race/history.hpp" #include "utils/log.hpp" +#include + RewindManager* RewindManager::m_rewind_manager = NULL; bool RewindManager::m_enable_rewind_manager = false; @@ -106,7 +108,7 @@ void RewindManager::reset() delete *i; } m_rewind_info.clear(); - m_next_event = m_rewind_info.end(); + m_next_event = m_rewind_info.begin(); m_network_events.lock(); const AllRewindInfo &info = m_network_events.getData(); @@ -118,6 +120,24 @@ void RewindManager::reset() m_network_events.unlock(); } // reset +// ---------------------------------------------------------------------------- +/** A compare function used when sorting the event lists. It sorts events by + * time. In case of equal times, it sorts states and events first (since the + * state needs to be restored when replaying first before any other events). + */ +bool RewindManager::_RewindInfoCompare::operator()(const RewindInfo *ri1, + const RewindInfo *ri2) const +{ + if (ri1->getTime() < ri2->getTime()) return true; + if (ri1->getTime() > ri2->getTime()) return false; + + // Now the times are equal. In this case make sure that states and + // time events are 'smallest', i.e. sorted first (which is necessary for + // replay). Time and State events at the same time do not happen, so no + // further test are necessary + return ri1->isState() || ri1->isTime(); +} // RewindInfoCompare::operator() + // ---------------------------------------------------------------------------- /** Inserts a RewindInfo object in the list of all events at the correct time. * If there are several RewindInfo at the exact same time, state RewindInfo @@ -127,45 +147,10 @@ void RewindManager::reset() */ void RewindManager::insertRewindInfo(RewindInfo *ri) { - std::upper_bound(m_rewind_info.begin(), m_rewind_info.end(), ri); -#ifdef REWIND_SEARCH_STATS - m_count_of_searches++; -#endif - float t = ri->getTime(); - - if(ri->isEvent()) - { - // If there are several infos for the same time t, - // events must be inserted at the end - AllRewindInfo::reverse_iterator i = m_rewind_info.rbegin(); - while(i!=m_rewind_info.rend() && (*i)->getTime() > t) - { -#ifdef REWIND_SEARCH_STATS - m_count_of_comparisons++; -#endif - i++; - } - AllRewindInfo::iterator insert_point = i.base(); - m_rewind_info.insert(insert_point,ri); - return; - - } - else // is a state or time - { - // If there are several infos for the same time t, - // a state or time entry must be inserted first - AllRewindInfo::reverse_iterator i = m_rewind_info.rbegin(); - while(i!=m_rewind_info.rend() && (*i)->getTime() >= t) - { -#ifdef REWIND_SEARCH_STATS - m_count_of_comparisons++; -#endif - i++; - } - AllRewindInfo::iterator insert_point = i.base(); - m_rewind_info.insert(insert_point,ri); - return; - } + AllRewindInfo::iterator i = std::upper_bound(m_rewind_info.begin(), + m_rewind_info.end(), ri, + RewindManager::RewindInfoCompare); + m_rewind_info.insert(i, ri); } // insertRewindInfo // ---------------------------------------------------------------------------- @@ -260,10 +245,14 @@ void RewindManager::addNetworkEvent(EventRewinder *event_rewinder, RewindInfo *ri = new RewindInfoEvent(time, event_rewinder, buffer, /*confirmed*/true); - // For now keep the freshly received events unsorted, they will - // be sorted when merged into m_rewind_info + // Sort the incoming network events so that we can use list::merge + // to move the network events into the RewindInfo main list. m_network_events.lock(); - m_network_events.getData().push_back(ri); + AllRewindInfo::iterator i = + std::upper_bound(m_network_events.getData().begin(), + m_network_events.getData().end() , ri, + RewindManager::RewindInfoCompare ); + m_network_events.getData().insert(i, ri); m_network_events.unlock(); } // addEvent @@ -281,13 +270,19 @@ void RewindManager::saveStates() float time = World::getWorld()->getTime(); if(time - m_last_saved_state < m_state_frequency) { - // No full state necessary, add a dummy entry for the time - // which increases replay precision (same time step size) - RewindInfo *ri = new RewindInfoTime(getCurrentTime()); - insertRewindInfo(ri); + if (time > m_last_saved_state) + { + // No full state necessary, add a dummy entry for the time + // which increases replay precision (same time step size) + RewindInfo *ri = new RewindInfoTime(getCurrentTime()); + insertRewindInfo(ri); + m_last_saved_state = getCurrentTime(); + } return; } + bool was_empty = m_rewind_info.empty(); + // For now always create a snapshot. AllRewinder::const_iterator i; for(i=m_all_rewinder.begin(); i!=m_all_rewinder.end(); ++i) @@ -306,6 +301,8 @@ void RewindManager::saveStates() delete buffer; // NULL or 0 byte buffer } + if (was_empty && !m_rewind_info.empty()) + m_next_event = m_rewind_info.begin(); Log::verbose("RewindManager", "%f allocated %ld bytes search %d/%d=%f", World::getWorld()->getTime(), m_overall_state_size, m_count_of_comparisons, m_count_of_searches, @@ -319,31 +316,37 @@ void RewindManager::saveStates() * \param time Up to (and inclusive) which time events will be replayed. */ void RewindManager::playEventsTill(float time) -{ - if (m_next_event== m_rewind_info.end()) +{ + if(m_rewind_info.empty()) return; - - /** First merge all newly received network events into the main event list */ - float current_time = (*m_next_event)->getTime(); - bool rewind_necessary = false; - AllRewindInfo::const_iterator i; + /** First merge all newly received network events into the main + * event list */ + bool rewind_necessary = false; m_network_events.lock(); - for (i = m_network_events.getData().begin(); - i != m_network_events.getData().end(); i++) + if (!m_network_events.getData().empty()) { - if ((*i)->getTime() < current_time) - rewind_necessary = true; - if (rewind_necessary) + // Check if a rewind is necessary + if (m_network_events.getData().front()->getTime() < time) { Log::info("RewindManager", "Rewind necessary at %f because of event at %f", - current_time, (*i)->getTime()); + time, + m_network_events.getData().front()->getTime()); } - insertRewindInfo(*i); - } - // Note that clear does not destruct the elements (which are now pointed - // to by m_rewind_info). - m_network_events.getData().clear(); + // It is possible that m_next_event points m_rewind_info.end() + // and that new elements are added to the end of the list. In this + // case m_rewind_info end would still point to end(), but should + // point to the newly added element. To achieve this, we first + // decrement m_next_event (note that it was tested previously that + // the list is not empty), merge in the new events, and then + // increment m_next_event again. If elements have been added to + // the end of the list, m_next_event will now corretly point to them. + m_next_event--; + m_rewind_info.merge(m_network_events.getData(), + RewindManager::RewindInfoCompare); + m_next_event++; + } // if !m_network_events empty + m_network_events.unlock(); assert(!m_is_rewinding); @@ -377,6 +380,7 @@ void RewindManager::rewindTo(float rewind_time) // First find the state to which we need to rewind // ------------------------------------------------ AllRewindInfo::reverse_iterator rindex = findFirstIndex(rewind_time); + rindex--; AllRewindInfo::iterator index = --(rindex.base()); if(!(*index)->isState()) diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index 95b90ea14..cd428e606 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -138,6 +138,11 @@ private: AllRewindInfo::reverse_iterator findFirstIndex(float time); void insertRewindInfo(RewindInfo *ri); float determineTimeStepSize(AllRewindInfo::iterator state, float max_time); + // ------------------------------------------------------------------------ + struct _RewindInfoCompare + { + bool operator()(const RewindInfo *ri1, const RewindInfo *r2) const; + } RewindInfoCompare; // _RewindInfoCompare public: // First static functions to manage rewinding. From 069e916dc0c2683233d72bca6a12bd7fc8ce823e Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 30 Dec 2016 13:35:48 +1100 Subject: [PATCH 011/413] Added state updates, not fully working yet. --- src/network/network_string.hpp | 20 ++- src/network/protocols/game_protocol.cpp | 156 ++++++++++++++++++------ src/network/protocols/game_protocol.hpp | 10 +- src/network/rewind_manager.cpp | 107 ++++++++++++---- src/network/rewind_manager.hpp | 2 + 5 files changed, 233 insertions(+), 62 deletions(-) diff --git a/src/network/network_string.hpp b/src/network/network_string.hpp index a7aba4289..c24cfe128 100644 --- a/src/network/network_string.hpp +++ b/src/network/network_string.hpp @@ -154,6 +154,22 @@ public: /** Returns a byte pointer to the content of the network string. */ const char* getData() const { return (char*)(m_buffer.data()); }; + // ------------------------------------------------------------------------ + /** Returns a byte pointer to the unread remaining content of the network + * string. */ + char* getCurrentData() + { + return (char*)(m_buffer.data()+m_current_offset); + } // getCurrentData + + // ------------------------------------------------------------------------ + /** Returns a byte pointer to the unread remaining content of the network + * string. */ + const char* getCurrentData() const + { + return (char*)(m_buffer.data()+m_current_offset); + } // getCurrentData + // ------------------------------------------------------------------------ /** Returns the remaining length of the network string. */ unsigned int size() const { return (int)m_buffer.size()-m_current_offset; } @@ -164,7 +180,7 @@ public: { m_current_offset += n; assert(m_current_offset >=0 && - m_current_offset < (int)m_buffer.size()); + m_current_offset <= (int)m_buffer.size()); } // skip // ------------------------------------------------------------------------ /** Returns the send size, which is the full length of the buffer. A @@ -233,7 +249,7 @@ public: { return addFloat(f); } // add - // ------------------------------------------------------------------------ + // ------------------------------------------------------------------------ /** Adds the xyz components of a Vec3 to the string. */ BareNetworkString& add(const Vec3 &xyz) { diff --git a/src/network/protocols/game_protocol.cpp b/src/network/protocols/game_protocol.cpp index 820774e25..5e6700653 100644 --- a/src/network/protocols/game_protocol.cpp +++ b/src/network/protocols/game_protocol.cpp @@ -46,44 +46,10 @@ GameProtocol::~GameProtocol() delete m_data_to_send; } // ~GameProtocol -//----------------------------------------------------------------------------- -bool GameProtocol::notifyEventAsynchronous(Event* event) -{ - if(!checkDataSize(event, 1)) return true; - - NetworkString &data = event->data(); - uint8_t count = data.getUInt8(); - for (unsigned int i = 0; i < count; i++) - { - float time = data.getFloat(); - uint8_t kart_id = data.getUInt8(); - assert(kart_id < World::getWorld()->getNumKarts()); - PlayerAction action = (PlayerAction)(data.getUInt8()); - int value = data.getUInt32(); - Log::info("GameProtocol", "Action at %f: %d %d %d", - time, kart_id, action, value); - BareNetworkString *s = new BareNetworkString(3); - s->addUInt8(kart_id).addUInt8(action).addUInt16(value); - RewindManager::get()->addNetworkEvent(this, s, time); - } - - if (data.size() > 0 ) - { - Log::warn("ControllerEventProtocol", - "The data seems corrupted. Remains %d", data.size()); - } - if (NetworkConfig::get()->isServer()) - { - // Send update to all clients except the original sender. - STKHost::get()->sendPacketExcept(event->getPeer(), - &data, false); - } // if server - return true; -} // notifyEventAsynchronous - //----------------------------------------------------------------------------- /** Synchronous update - will send all commands collected during the last - * frame (and could optional only send messages every N frames). */ + * frame (and could optional only send messages every N frames). + */ void GameProtocol::update(float dt) { if (m_all_actions.size() == 0) return; // nothing to do @@ -91,7 +57,8 @@ void GameProtocol::update(float dt) // Clear left-over data from previous frame. This way the network // string will increase till it reaches maximum size necessary m_data_to_send->clear(); - m_data_to_send->addUInt8( uint8_t( m_all_actions.size() ) ); + m_data_to_send->addUInt8(GP_CONTROLLER_ACTION) + .addUInt8(uint8_t(m_all_actions.size())); // Add all actions for (auto a : m_all_actions) @@ -101,11 +68,31 @@ void GameProtocol::update(float dt) m_data_to_send->addUInt8((uint8_t)(a.m_action)).addUInt32(a.m_value); } // for a in m_all_actions - // FIXME: for now send reliable + // FIXME: for now send reliable sendToServer(m_data_to_send, /*reliable*/ true); m_all_actions.clear(); } // update +//----------------------------------------------------------------------------- +/** Called when a message from a remote GameProtocol is received. + */ +bool GameProtocol::notifyEventAsynchronous(Event* event) +{ + if(!checkDataSize(event, 1)) return true; + + NetworkString &data = event->data(); + uint8_t message_type = data.getUInt8(); + switch (message_type) + { + case GP_CONTROLLER_ACTION: handleControllerAction(event); break; + case GP_STATE: handleState(event); break; + default: Log::error("GameProtocol", + "Received unknown message type %d - ignored.", + message_type); break; + } // switch message_type + return true; +} // notifyEventAsynchronous + //----------------------------------------------------------------------------- /** Called from the local kart controller when an action (like steering, * acceleration, ...) was triggered. It sends a message with the new info @@ -139,6 +126,99 @@ void GameProtocol::controllerAction(int kart_id, PlayerAction action, World::getWorld()->getTime(), action, value); } // controllerAction +// ---------------------------------------------------------------------------- +/** Called when a controller event is receiver - either on the server from + * a client, or on a client from the server. It sorts the event into the + * RewindManager's network event queue. The server will also send this + * event immediately to all clients (except to the original sender). + */ +void GameProtocol::handleControllerAction(Event *event) +{ + NetworkString &data = event->data(); + uint8_t count = data.getUInt8(); + for (unsigned int i = 0; i < count; i++) + { + float time = data.getFloat(); + uint8_t kart_id = data.getUInt8(); + assert(kart_id < World::getWorld()->getNumKarts()); + + PlayerAction action = (PlayerAction)(data.getUInt8()); + int value = data.getUInt32(); + Log::info("GameProtocol", "Action at %f: %d %d %d", + time, kart_id, action, value); + BareNetworkString *s = new BareNetworkString(3); + s->addUInt8(kart_id).addUInt8(action).addUInt16(value); + RewindManager::get()->addNetworkEvent(this, s, time); + } + + if (data.size() > 0) + { + Log::warn("GameProtocol", + "Received invalid controller data - remains %d",data.size()); + } + if (NetworkConfig::get()->isServer()) + { + // Send update to all clients except the original sender. + STKHost::get()->sendPacketExcept(event->getPeer(), + &data, false); + } // if server + +} // handleControllerAction + +// ---------------------------------------------------------------------------- +/** Called by the server before assembling a new message containing the full + * state of the race to be sent to a client. + */ +void GameProtocol::startNewState() +{ + assert(NetworkConfig::get()->isServer()); + m_data_to_send->clear(); + m_data_to_send->addUInt8(GP_STATE).addFloat(World::getWorld()->getTime()); + Log::info("GameProtocol", "Sending new state at %f.", + World::getWorld()->getTime()); +} // startNewState + +// ---------------------------------------------------------------------------- +/** Called by a server to add data to the current state. + */ +void GameProtocol::addState(BareNetworkString *buffer) +{ + assert(NetworkConfig::get()->isServer()); + m_data_to_send->addUInt16(buffer->size()); + (*m_data_to_send) += *buffer; +} // addState +// ---------------------------------------------------------------------------- +/** Called when the last state information has been added and the message + * can be sent to the clients. + */ +void GameProtocol::sendState() +{ + assert(NetworkConfig::get()->isServer()); + sendMessageToPeersChangingToken(m_data_to_send, /*reliable*/true); +} // sendState + +// ---------------------------------------------------------------------------- +/** Called when a new full state is received form the server. + */ +void GameProtocol::handleState(Event *event) +{ + assert(NetworkConfig::get()->isClient()); + NetworkString &data = event->data(); + float time = data.getFloat(); + Log::info("GameProtocol", "Received at %f state from %f", + World::getWorld()->getTime(), time); + int index = 0; + while (data.size() > 0) + { + uint16_t count = data.getUInt16(); + BareNetworkString *state = new BareNetworkString(data.getCurrentData(), + count); + data.skip(count); + RewindManager::get()->addNetworkState(index, state, time); + index++; + } // while data.size()>0 +} // handleState + // ---------------------------------------------------------------------------- /** Called from the RewindManager when rolling back. * \param buffer Pointer to the saved state information. diff --git a/src/network/protocols/game_protocol.hpp b/src/network/protocols/game_protocol.hpp index 7065917da..d84925a62 100644 --- a/src/network/protocols/game_protocol.hpp +++ b/src/network/protocols/game_protocol.hpp @@ -38,7 +38,8 @@ class GameProtocol : public Protocol private: /** The type of game events to be forwarded to the server. */ - enum { GP_CONTROLLER_ACTION = 0x01}; + enum { GP_CONTROLLER_ACTION, + GP_STATE}; /** A network string that collects all information from the server to be sent * next. */ @@ -55,6 +56,9 @@ private: // List of all kart actions to send to the server std::vector m_all_actions; + + void handleControllerAction(Event *event); + void handleState(Event *event); public: GameProtocol(); virtual ~GameProtocol(); @@ -64,6 +68,10 @@ public: void controllerAction(int kart_id, PlayerAction action, int value); + void startNewState(); + void addState(BareNetworkString *buffer); + void sendState(); + virtual void undo(BareNetworkString *buffer) OVERRIDE; virtual void rewind(BareNetworkString *buffer) OVERRIDE; // ------------------------------------------------------------------------ diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 313ac6aa6..1dd0fdb4c 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -20,7 +20,9 @@ #include "graphics/irr_driver.hpp" #include "modes/world.hpp" +#include "network/network_config.hpp" #include "network/network_string.hpp" +#include "network/protocols/game_protocol.hpp" #include "network/rewinder.hpp" #include "network/rewind_info.hpp" #include "physics/physics.hpp" @@ -84,7 +86,7 @@ void RewindManager::reset() #endif m_is_rewinding = false; m_overall_state_size = 0; - m_state_frequency = 0.1f; // save 10 states a second + m_state_frequency = 0.5f; // save 10 states a second m_last_saved_state = -9999.9f; // forces initial state save if(!m_enable_rewind_manager) return; @@ -184,7 +186,7 @@ RewindManager::AllRewindInfo::reverse_iterator #endif if((*index)->isState()) { - if((*index)->getTime()getTime() <= target_time ) { return index; } @@ -223,7 +225,8 @@ void RewindManager::addEvent(EventRewinder *event_rewinder, Log::error("RewindManager", "Adding event when rewinding"); return; } - Log::verbose("time", "wolrd %f rewind %f", World::getWorld()->getTime(), getCurrentTime()); + Log::verbose("RewindManager", "Time world %f self-event %f", + World::getWorld()->getTime(), getCurrentTime()); if (time < 0) time = getCurrentTime(); RewindInfo *ri = new RewindInfoEvent(time, event_rewinder, @@ -254,8 +257,38 @@ void RewindManager::addNetworkEvent(EventRewinder *event_rewinder, RewindManager::RewindInfoCompare ); m_network_events.getData().insert(i, ri); m_network_events.unlock(); + Log::verbose("RewindManager", "Time world %f network-event %f", + World::getWorld()->getTime(), time); + } // addEvent +// ---------------------------------------------------------------------------- +/** Adds a state to the list of network rewind data. This function is + * threadsafe so can be called by the network thread. The data is synched + * to m_rewind_info by the main thread. The data to be stored must be + * allocated and not freed by the caller! + * \param time Time at which the event was recorded. + * \param buffer Pointer to the event data. + */ +void RewindManager::addNetworkState(int rewinder_index, + BareNetworkString *buffer, float time) +{ + RewindInfo *ri = new RewindInfoState(time, m_all_rewinder[rewinder_index], + buffer, /*confirmed*/true); + + // Sort the incoming network events so that we can use list::merge + // to move the network events into the RewindInfo main list. + m_network_events.lock(); + AllRewindInfo::iterator i = + std::upper_bound(m_network_events.getData().begin(), + m_network_events.getData().end(), ri, + RewindManager::RewindInfoCompare ); + m_network_events.getData().insert(i, ri); + m_network_events.unlock(); + Log::verbose("RewindManager", "Time world %f network-state %f", + World::getWorld()->getTime(), time); +} // addState + // ---------------------------------------------------------------------------- /** Determines if a new state snapshot should be taken, and if so calls all * rewinder to do so. @@ -266,17 +299,20 @@ void RewindManager::saveStates() if(!m_enable_rewind_manager || m_all_rewinder.size()==0 || m_is_rewinding ) return; - + if (NetworkConfig::get()->isClient()) + return; + float time = World::getWorld()->getTime(); if(time - m_last_saved_state < m_state_frequency) { + // Avoid saving a time event for the same time, which happens + // with t=0. if (time > m_last_saved_state) { // No full state necessary, add a dummy entry for the time // which increases replay precision (same time step size) RewindInfo *ri = new RewindInfoTime(getCurrentTime()); insertRewindInfo(ri); - m_last_saved_state = getCurrentTime(); } return; } @@ -284,6 +320,8 @@ void RewindManager::saveStates() bool was_empty = m_rewind_info.empty(); // For now always create a snapshot. + if (NetworkConfig::get()->isServer()) + GameProtocol::getInstance()->startNewState(); AllRewinder::const_iterator i; for(i=m_all_rewinder.begin(); i!=m_all_rewinder.end(); ++i) { @@ -296,10 +334,14 @@ void RewindManager::saveStates() /*is_confirmed*/true); assert(ri); insertRewindInfo(ri); + if(NetworkConfig::get()->isServer()) + GameProtocol::getInstance()->addState(buffer); } // size >= 0 else delete buffer; // NULL or 0 byte buffer } + if (NetworkConfig::get()->isServer()) + GameProtocol::getInstance()->sendState(); if (was_empty && !m_rewind_info.empty()) m_next_event = m_rewind_info.begin(); @@ -317,38 +359,57 @@ void RewindManager::saveStates() */ void RewindManager::playEventsTill(float time) { - if(m_rewind_info.empty()) + m_network_events.lock(); + if (m_rewind_info.empty() && m_network_events.getData().empty()) + { + m_network_events.unlock(); return; + } /** First merge all newly received network events into the main * event list */ bool rewind_necessary = false; - m_network_events.lock(); + float rewind_time = -1.0f; + if (!m_network_events.getData().empty()) { // Check if a rewind is necessary - if (m_network_events.getData().front()->getTime() < time) + rewind_time = m_network_events.getData().front()->getTime(); + Log::info("RewindManager", "At %f merging states from %f", + World::getWorld()->getTime(), rewind_time); + if (rewind_time < time) { - Log::info("RewindManager", "Rewind necessary at %f because of event at %f", - time, - m_network_events.getData().front()->getTime()); + + Log::info("RewindManager", + "Rewind necessary at %f because of event at %f", + time, rewind_time); + rewind_necessary = true; } // It is possible that m_next_event points m_rewind_info.end() // and that new elements are added to the end of the list. In this // case m_rewind_info end would still point to end(), but should // point to the newly added element. To achieve this, we first - // decrement m_next_event (note that it was tested previously that + // decrement m_next_eventr (note that it was tested previously that // the list is not empty), merge in the new events, and then // increment m_next_event again. If elements have been added to // the end of the list, m_next_event will now corretly point to them. - m_next_event--; + + bool adjust_next = !m_rewind_info.empty(); + if(adjust_next) m_next_event--; m_rewind_info.merge(m_network_events.getData(), RewindManager::RewindInfoCompare); - m_next_event++; + if (adjust_next) + m_next_event++; + else // rewind_info was empty, set pointer to first element + m_next_event = m_rewind_info.begin(); } // if !m_network_events empty m_network_events.unlock(); + if (rewind_necessary) + { + rewindTo(rewind_time); + } assert(!m_is_rewinding); m_is_rewinding = true; while (m_next_event !=m_rewind_info.end() ) @@ -373,22 +434,24 @@ void RewindManager::playEventsTill(float time) void RewindManager::rewindTo(float rewind_time) { assert(!m_is_rewinding); - m_is_rewinding = true; Log::info("rewind", "Rewinding to %f", rewind_time); history->doReplayHistory(History::HISTORY_NONE); // First find the state to which we need to rewind // ------------------------------------------------ AllRewindInfo::reverse_iterator rindex = findFirstIndex(rewind_time); - rindex--; - AllRewindInfo::iterator index = --(rindex.base()); + //rindex--; + AllRewindInfo::reverse_iterator xx = rindex; + ++xx; + AllRewindInfo::iterator index = --(xx.base()); - if(!(*index)->isState()) + if(!(*rindex)->isState()) { Log::error("RewindManager", "No state for rewind to %f, state %d.", rewind_time, index); return; } + m_is_rewinding = true; // Then undo the rewind infos going backwards in time // -------------------------------------------------- @@ -413,7 +476,7 @@ void RewindManager::rewindTo(float rewind_time) // Get the (first) full state to which we have to rewind RewindInfoState *state = - dynamic_cast(*index); + dynamic_cast(*rindex); // Store the time to which we have to replay to float exact_rewind_time = state->getTime(); @@ -430,6 +493,7 @@ void RewindManager::rewindTo(float rewind_time) { state->rewind(); index++; + //rindex--; if(index==m_rewind_info.end()) break; state = dynamic_cast(*index); } @@ -457,7 +521,7 @@ void RewindManager::rewindTo(float rewind_time) } float dt = determineTimeStepSize(index, current_time); world->updateWorld(dt); -#define SHOW_ROLLBACK +#undef SHOW_ROLLBACK #ifdef SHOW_ROLLBACK irr_driver->update(dt); #endif @@ -465,7 +529,8 @@ void RewindManager::rewindTo(float rewind_time) } m_is_rewinding = false; - + Log::info("RewindManager", "Rewind from %f to %f finished.", + rewind_time, World::getWorld()->getTime()); } // rewindTo // ---------------------------------------------------------------------------- diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index cd428e606..44a9ae793 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -174,6 +174,8 @@ public: bool confirmed, float time = -1.0f); void addNetworkEvent(EventRewinder *event_rewinder, BareNetworkString *buffer, float time); + void addNetworkState(int rewinder_index, BareNetworkString *buffer, + float time); // ------------------------------------------------------------------------ /** Sets the time that is to be used for all further states or events, * and the time step size. This is necessary so that states/events before From e5c5870564f1d49fcc66cd42d17e31fa592cb4f4 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 4 Jan 2017 08:11:17 +1100 Subject: [PATCH 012/413] Removed debug output. --- src/network/stk_host.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index 1c961add5..eff17d7ec 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -551,8 +551,6 @@ void* STKHost::mainLoop(void* self) // Create an STKEvent with the event data. This will also // create the peer if it doesn't exist already Event* stk_event = new Event(&event); - Log::verbose("STKHost", "Event of type %d received", - (int)(stk_event->getType())); STKPeer* peer = stk_event->getPeer(); if (stk_event->getType() == EVENT_TYPE_CONNECTED) { @@ -565,11 +563,13 @@ void* STKHost::mainLoop(void* self) { Network::logPacket(stk_event->data(), true); TransportAddress stk_addr(peer->getAddress()); +#ifdef DEBUG_MESSAGE_CONTENT Log::verbose("NetworkManager", "Message, Sender : %s, message:", stk_addr.toString(/*show port*/false).c_str()); Log::verbose("NetworkManager", "%s", stk_event->data().getLogMessage().c_str()); +#endif } // if message event // notify for the event now. From 2df97ebc395ea006f3feb662b236fdf6121a5f81 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 4 Jan 2017 08:18:41 +1100 Subject: [PATCH 013/413] Disable skipping of ready-set-go in artist debug mode when networking (can desynchronise client and server). --- src/modes/world_status.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index 4c0100d70..7fbde45e7 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -282,7 +282,8 @@ void WorldStatus::updateTime(const float dt) // In artist debug mode, when without opponents, skip the // ready/set/go counter faster - if (UserConfigParams::m_artist_debug_mode && + if (UserConfigParams::m_artist_debug_mode && + !NetworkConfig::get()->isNetworking() && race_manager->getNumberOfKarts() - race_manager->getNumSpareTireKarts() == 1 && race_manager->getTrackName() != "tutorial") From cd4a19e801ddfc829aaf361181e287938a88956d Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 4 Jan 2017 09:17:54 +1100 Subject: [PATCH 014/413] Reset snapshot frquency; fixed bug in rewind (which would stop with the last event, and not rewind all the way back to the current time). --- src/network/rewind_manager.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 1dd0fdb4c..5bcee6c3c 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -86,7 +86,7 @@ void RewindManager::reset() #endif m_is_rewinding = false; m_overall_state_size = 0; - m_state_frequency = 0.5f; // save 10 states a second + m_state_frequency = 0.1f; // save 10 states a second m_last_saved_state = -9999.9f; // forces initial state save if(!m_enable_rewind_manager) return; @@ -434,7 +434,8 @@ void RewindManager::playEventsTill(float time) void RewindManager::rewindTo(float rewind_time) { assert(!m_is_rewinding); - Log::info("rewind", "Rewinding to %f", rewind_time); + Log::info("rewind", "Rewinding to %f at %f", rewind_time, + StkTime::getRealTime()); history->doReplayHistory(History::HISTORY_NONE); // First find the state to which we need to rewind @@ -500,7 +501,7 @@ void RewindManager::rewindTo(float rewind_time) // Now go forward through the list of rewind infos: // ------------------------------------------------ - while( world->getTime() < current_time && index !=m_rewind_info.end() ) + while( world->getTime() < current_time ) { // Now handle all states and events at the current time before // updating the world: @@ -529,8 +530,9 @@ void RewindManager::rewindTo(float rewind_time) } m_is_rewinding = false; - Log::info("RewindManager", "Rewind from %f to %f finished.", - rewind_time, World::getWorld()->getTime()); + Log::info("RewindManager", "Rewind from %f to %f finished at %f.", + rewind_time, World::getWorld()->getTime(), + StkTime::getRealTime()); } // rewindTo // ---------------------------------------------------------------------------- From b3f6307107cb63de2a8853f2dbe484832de1f0d5 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 4 Jan 2017 09:23:57 +1100 Subject: [PATCH 015/413] Fixed crash when exiting a race early. --- src/network/protocols/game_protocol.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/network/protocols/game_protocol.cpp b/src/network/protocols/game_protocol.cpp index 5e6700653..88c7fd20e 100644 --- a/src/network/protocols/game_protocol.cpp +++ b/src/network/protocols/game_protocol.cpp @@ -202,6 +202,10 @@ void GameProtocol::sendState() */ void GameProtocol::handleState(Event *event) { + // Ignore events arriving when client has already exited + if (!World::getWorld()) + return; + assert(NetworkConfig::get()->isClient()); NetworkString &data = event->data(); float time = data.getFloat(); From 694131797989d441bb08454c64fc8986b7910874 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 4 Jan 2017 09:50:43 +1100 Subject: [PATCH 016/413] Increase server delay to reduce number of rewinds on server. --- src/network/protocols/server_lobby.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index bfc7bbd76..01fa90b32 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -231,13 +231,13 @@ void ServerLobby::update(float dt) break; case WAIT_FOR_WORLD_LOADED: // Note that m_server_has_loaded_world is called by the main thread - // (same a the thread updating this protocol) + // (same as the thread updating this protocol) m_client_ready_count.lock(); if (m_server_has_loaded_world && m_client_ready_count.getData() == m_game_setup->getPlayerCount()) { signalRaceStartToClients(); - m_server_delay = 0.02f; + m_server_delay = 0.1f; m_client_ready_count.getData() = 0; } m_client_ready_count.unlock(); From 2be59e7728d9d99353cfc67c32329d08c9b57a50 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 4 Jan 2017 09:51:36 +1100 Subject: [PATCH 017/413] Avoid infinite loop when showing the in-race menu and a rollback happens. --- src/network/race_event_manager.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/network/race_event_manager.cpp b/src/network/race_event_manager.cpp index 51a69ad39..3d591cbae 100644 --- a/src/network/race_event_manager.cpp +++ b/src/network/race_event_manager.cpp @@ -30,8 +30,11 @@ void RaceEventManager::update(float dt) if(!ProtocolManager::getInstance()) return; - // Replay all recorded events up to the current time: - RewindManager::get()->playEventsTill(World::getWorld()->getTime()); + // Replay all recorded events up to the current time (only if the + // timer isn't stopped, otherwise a potential rewind will trigger + // an infinite loop since world time does not increase) + if(World::getWorld()->getPhase()!=WorldStatus::IN_GAME_MENU_PHASE) + RewindManager::get()->playEventsTill(World::getWorld()->getTime()); World::getWorld()->updateWorld(dt); From a8be3f8068493cf2806e5702c283b5124a236582 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 11 Jan 2017 09:29:01 +1100 Subject: [PATCH 018/413] Removed unnecessary events crated by KartControl, added steer_left and steer_right values from the player controller to the state to fix steering synchronsiation problems. --- src/karts/controller/ai_base_controller.hpp | 6 ++ src/karts/controller/controller.hpp | 6 ++ src/karts/controller/ghost_controller.hpp | 3 + src/karts/controller/kart_control.cpp | 69 +------------------ src/karts/controller/kart_control.hpp | 4 +- .../controller/local_player_controller.cpp | 7 +- src/karts/controller/player_controller.cpp | 29 ++++++++ src/karts/controller/player_controller.hpp | 4 ++ src/karts/kart.cpp | 2 +- src/karts/kart_rewinder.cpp | 7 +- src/network/protocols/game_protocol.cpp | 30 +++++--- src/network/protocols/game_protocol.hpp | 4 +- src/network/rewind_manager.cpp | 8 ++- 13 files changed, 91 insertions(+), 88 deletions(-) diff --git a/src/karts/controller/ai_base_controller.hpp b/src/karts/controller/ai_base_controller.hpp index 20647e28d..033cbf369 100644 --- a/src/karts/controller/ai_base_controller.hpp +++ b/src/karts/controller/ai_base_controller.hpp @@ -20,6 +20,7 @@ #define HEADER_AI_BASE_CONTROLLER_HPP #include "karts/controller/controller.hpp" +#include "utils/cpp2011.hpp" class AIProperties; class Track; @@ -71,6 +72,7 @@ protected: /** This can be called to detect if the kart is stuck (i.e. repeatedly * hitting part of the track). */ bool isStuck() const { return m_stuck; } + // ------------------------------------------------------------------------ void determineTurnRadius(const Vec3 &end, Vec3 *center, float *radius) const; virtual void update (float delta); @@ -98,6 +100,10 @@ public: virtual bool isLocalPlayerController() const { return false; } virtual void action(PlayerAction action, int value) {}; virtual void skidBonusTriggered() {}; + // ------------------------------------------------------------------------ + /** Not used for AIs. */ + virtual void saveState(BareNetworkString *buffer) const OVERRIDE {} + virtual void rewindTo(BareNetworkString *buffer) OVERRIDE {} }; // AIBaseController diff --git a/src/karts/controller/controller.hpp b/src/karts/controller/controller.hpp index 747f9cc0c..da397dfc4 100644 --- a/src/karts/controller/controller.hpp +++ b/src/karts/controller/controller.hpp @@ -21,6 +21,8 @@ #include using namespace irr; +class BareNetworkString; + /** * \defgroup controller Karts/controller * Contains kart controllers, which are either human players or AIs @@ -31,6 +33,7 @@ using namespace irr; #include "states_screens/state_manager.hpp" class AbstractKart; +class BareNetworString; class Item; class KartControl; class Material; @@ -74,6 +77,9 @@ public: * rubber-banding. */ virtual bool isPlayerController () const = 0; virtual bool disableSlipstreamBonus() const = 0; + virtual void saveState(BareNetworkString *buffer) const = 0; + virtual void rewindTo(BareNetworkString *buffer) = 0; + // --------------------------------------------------------------------------- /** Sets the controller name for this controller. */ virtual void setControllerName(const std::string &name) diff --git a/src/karts/controller/ghost_controller.hpp b/src/karts/controller/ghost_controller.hpp index fe4937cf4..74e3ac6ac 100644 --- a/src/karts/controller/ghost_controller.hpp +++ b/src/karts/controller/ghost_controller.hpp @@ -58,6 +58,9 @@ public: virtual void action(PlayerAction action, int value) OVERRIDE; virtual void skidBonusTriggered() {}; virtual void newLap(int lap) {}; + virtual void saveState(BareNetworkString *buffer) const {}; + virtual void rewindTo(BareNetworkString *buffer) {}; + void addReplayTime(float time); // ------------------------------------------------------------------------ bool isReplayEnd() const diff --git a/src/karts/controller/kart_control.cpp b/src/karts/controller/kart_control.cpp index 7ba61b354..9173d896a 100644 --- a/src/karts/controller/kart_control.cpp +++ b/src/karts/controller/kart_control.cpp @@ -38,7 +38,7 @@ void KartControl::rewind(BareNetworkString *buffer) if(buffer->getTotalSize()>1) { // Full state including accel and steering was saved - setFromBuffer(buffer); + rewindTo(buffer); } else // only a button event was stored { @@ -67,119 +67,54 @@ void KartControl::set(const KartControl &c) /** Sets the current steering value. */ void KartControl::setSteer(float f) { - float old_steer = m_steer; m_steer = f; - if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() && - old_steer != m_steer ) - { - // Save full status - BareNetworkString *buffer = new BareNetworkString(getLength()); - copyToBuffer(buffer); - RewindManager::get()->addEvent(this, buffer, true); - } } // setSteer // ---------------------------------------------------------------------------- /** Sets the acceleration. */ void KartControl::setAccel(float f) { - float old_accel = m_accel; m_accel = f; - if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() && - old_accel != m_accel ) - { - BareNetworkString *buffer = new BareNetworkString(getLength()); - copyToBuffer(buffer); - RewindManager::get()->addEvent(this, buffer, true); - } } // setAccel // ---------------------------------------------------------------------------- /** Sets if the kart is braking. */ void KartControl::setBrake(bool b) { - bool old_brake = m_brake; m_brake = b; - if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() && - old_brake != m_brake ) - { - // Only store the buttons in this case - BareNetworkString *buffer = new BareNetworkString(1); - buffer->addUInt8(getButtonsCompressed()); - RewindManager::get()->addEvent(this, buffer, true); - } } // setBrake + // ---------------------------------------------------------------------------- /** Sets if the kart activates nitro. */ void KartControl::setNitro(bool b) { - bool old_nitro = m_nitro; m_nitro = b; - if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() && - old_nitro != m_nitro ) - { - BareNetworkString *buffer = new BareNetworkString(1); - buffer->addUInt8(getButtonsCompressed()); - RewindManager::get()->addEvent(this, buffer, true); - } } // setNitro // ---------------------------------------------------------------------------- /** Sets the skid control for this kart. */ void KartControl::setSkidControl(SkidControl sc) { - SkidControl old_skid = m_skid; m_skid = sc; - if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() && - old_skid != m_skid ) - { - BareNetworkString *buffer = new BareNetworkString(1); - buffer->addUInt8(getButtonsCompressed()); - RewindManager::get()->addEvent(this, buffer, true); - } } // seSkidControl // ---------------------------------------------------------------------------- /** Returns if this kart wants to get rescued. */ void KartControl::setRescue(bool b) { - bool old_rescue = m_rescue; m_rescue = b; - if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() && - old_rescue != m_rescue) - { - BareNetworkString *buffer = new BareNetworkString(1); - buffer->addUInt8(getButtonsCompressed()); - RewindManager::get()->addEvent(this, buffer, true); - } } // setRescue // ---------------------------------------------------------------------------- /** Sets if the kart wants to fire. */ void KartControl::setFire(bool b) { - bool old_fire = m_fire; m_fire = b; - if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() && - old_fire != m_fire ) - { - BareNetworkString *buffer = new BareNetworkString(1); - buffer->addUInt8(getButtonsCompressed()); - RewindManager::get()->addEvent(this, buffer, true); - } } // setFire // ---------------------------------------------------------------------------- /** Sets if the kart wants to look (and therefore also fires) backwards. */ void KartControl::setLookBack(bool b) { - bool old_look = m_look_back; m_look_back = b; - if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() && - old_look != m_look_back) - { - BareNetworkString *buffer = new BareNetworkString(1); - buffer->addUInt8(getButtonsCompressed()); - RewindManager::get()->addEvent(this, buffer, true); - } } // setLookBack diff --git a/src/karts/controller/kart_control.hpp b/src/karts/controller/kart_control.hpp index e4a2afec4..c7865c16d 100644 --- a/src/karts/controller/kart_control.hpp +++ b/src/karts/controller/kart_control.hpp @@ -101,7 +101,7 @@ public: static int getLength() { return 9; } // ------------------------------------------------------------------------ /** Copies the important data from this objects into a memory buffer. */ - void copyToBuffer(BareNetworkString *buffer) const + void saveState(BareNetworkString *buffer) const { buffer->add(m_steer); buffer->add(m_accel); @@ -110,7 +110,7 @@ public: // ------------------------------------------------------------------------ /** Restores this object from a previously saved memory buffer. */ - void setFromBuffer(BareNetworkString *buffer) + void rewindTo(BareNetworkString *buffer) { m_steer = buffer->getFloat(); m_accel = buffer->getFloat(); diff --git a/src/karts/controller/local_player_controller.cpp b/src/karts/controller/local_player_controller.cpp index 7bc300185..1cc3316b8 100644 --- a/src/karts/controller/local_player_controller.cpp +++ b/src/karts/controller/local_player_controller.cpp @@ -142,16 +142,17 @@ void LocalPlayerController::resetInputState() */ void LocalPlayerController::action(PlayerAction action, int value) { - PlayerController::action(action, value); - // If this is a client, send the action to networking layer if (World::getWorld()->isNetworkWorld() && NetworkConfig::get()->isClient() && !RewindManager::get()->isRewinding() ) { GameProtocol::getInstance()->controllerAction(m_kart->getWorldKartId(), - action, value); + action, value, + m_steer_val_l, + m_steer_val_r); } + PlayerController::action(action, value); } // action //----------------------------------------------------------------------------- diff --git a/src/karts/controller/player_controller.cpp b/src/karts/controller/player_controller.cpp index f462d2a4a..75ce0babe 100644 --- a/src/karts/controller/player_controller.cpp +++ b/src/karts/controller/player_controller.cpp @@ -93,6 +93,9 @@ void PlayerController::resetInputState() */ void PlayerController::action(PlayerAction action, int value) { + Log::info("action", "t %f action %d value %d val_l %d val_r %d val %d", + World::getWorld()->getTime(), action, value, + m_steer_val_l, m_steer_val_r, m_steer_val); switch (action) { case PA_STEER_LEFT: @@ -188,6 +191,15 @@ void PlayerController::action(PlayerAction action, int value) } // action +//----------------------------------------------------------------------------- +void PlayerController::actionFromNetwork(PlayerAction p_action, int value, + int value_l, int value_r) +{ + m_steer_val_l = value_l; + m_steer_val_r = value_r; + action(p_action, value); +} // actionFromNetwork + //----------------------------------------------------------------------------- /** Handles steering for a player kart. */ @@ -203,6 +215,8 @@ void PlayerController::steer(float dt, int steer_val) steer = 0; } + Log::info("steer", "t %f dt %f steer_val %d steer %f", + World::getWorld()->getTime(), dt, steer_val, steer); // Amount the steering is changed for digital devices. // If the steering is 'back to straight', a different steering // change speed is used. @@ -316,3 +330,18 @@ void PlayerController::handleZipper(bool play_sound) { m_kart->showZipperFire(); } // handleZipper + +//----------------------------------------------------------------------------- +void PlayerController::saveState(BareNetworkString *buffer) const +{ + buffer->addUInt32(m_steer_val).addUInt32(m_steer_val_l) + .addUInt32(m_steer_val_r); +} // copyToBuffer + +//----------------------------------------------------------------------------- +void PlayerController::rewindTo(BareNetworkString *buffer) +{ + m_steer_val = buffer->getUInt32(); + m_steer_val_l = buffer->getUInt32(); + m_steer_val_r = buffer->getUInt32(); +} // rewindTo \ No newline at end of file diff --git a/src/karts/controller/player_controller.hpp b/src/karts/controller/player_controller.hpp index 08640c9bb..024738854 100644 --- a/src/karts/controller/player_controller.hpp +++ b/src/karts/controller/player_controller.hpp @@ -45,10 +45,14 @@ public: virtual ~PlayerController (); virtual void update (float) OVERRIDE; virtual void action (PlayerAction action, int value) OVERRIDE; + virtual void actionFromNetwork(PlayerAction action, int value, + int value_l, int value_r); virtual void skidBonusTriggered() OVERRIDE; virtual void reset () OVERRIDE; virtual void handleZipper(bool play_sound) OVERRIDE; virtual void resetInputState(); + virtual void saveState(BareNetworkString *buffer) const OVERRIDE; + virtual void rewindTo(BareNetworkString *buffer) OVERRIDE; // ------------------------------------------------------------------------ virtual void collectedItem(const Item &item, int add_info=-1, float previous_energy=0 ) OVERRIDE diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index b9c1e49c4..86f7efb8c 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1225,7 +1225,7 @@ void Kart::update(float dt) // is used furthermore for engine power, camera distance etc updateSpeed(); - if(!history->replayHistory() && !RewindManager::get()->isRewinding()) + if(!history->replayHistory()) m_controller->update(dt); #undef DEBUG_CAMERA_SHAKE diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index 692cb1244..4a560e2ba 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -21,6 +21,7 @@ #include "items/attachment.hpp" #include "items/powerup.hpp" #include "karts/abstract_kart.hpp" +#include "karts/controller/controller.hpp" #include "karts/max_speed.hpp" #include "karts/skidding.hpp" #include "modes/world.hpp" @@ -78,7 +79,8 @@ BareNetworkString* KartRewinder::saveState() const // 2) Steering and other player controls // ------------------------------------- - getControls().copyToBuffer(buffer); + getControls().saveState(buffer); + getController()->saveState(buffer); // 3) Attachment // ------------- @@ -124,7 +126,8 @@ void KartRewinder::rewindToState(BareNetworkString *buffer) // 2) Steering and other controls // ------------------------------ - getControls().setFromBuffer(buffer); + getControls().rewindTo(buffer); + getController()->rewindTo(buffer); // 3) Attachment // ------------- diff --git a/src/network/protocols/game_protocol.cpp b/src/network/protocols/game_protocol.cpp index 88c7fd20e..554602242 100644 --- a/src/network/protocols/game_protocol.cpp +++ b/src/network/protocols/game_protocol.cpp @@ -65,7 +65,8 @@ void GameProtocol::update(float dt) { m_data_to_send->addFloat(a.m_time); m_data_to_send->addUInt8(a.m_kart_id); - m_data_to_send->addUInt8((uint8_t)(a.m_action)).addUInt32(a.m_value); + m_data_to_send->addUInt8((uint8_t)(a.m_action)).addUInt32(a.m_value) + .addUInt32(a.m_value_l).addUInt32(a.m_value_r); } // for a in m_all_actions // FIXME: for now send reliable @@ -102,7 +103,7 @@ bool GameProtocol::notifyEventAsynchronous(Event* event) * \param value New value for the given action. */ void GameProtocol::controllerAction(int kart_id, PlayerAction action, - int value) + int value, int val_l, int val_r) { // Store the action in the list of actions that will be sent to the // server next. @@ -111,6 +112,8 @@ void GameProtocol::controllerAction(int kart_id, PlayerAction action, a.m_kart_id = kart_id; a.m_action = action; a.m_value = value; + a.m_value_l = val_l; + a.m_value_r = val_r; a.m_time = World::getWorld()->getTime(); m_all_actions.push_back(a); @@ -118,7 +121,8 @@ void GameProtocol::controllerAction(int kart_id, PlayerAction action, // Store the event in the rewind manager, which is responsible // for freeing the allocated memory BareNetworkString *s = new BareNetworkString(4); - s->addUInt8(kart_id).addUInt8(action).addUInt16(uint16_t(value)); + s->addUInt8(kart_id).addUInt8(action).addUInt32(value) + .addUInt32(val_l).addUInt32(val_r); RewindManager::get()->addEvent(this, s, /*confirmed*/true, World::getWorld()->getTime() ); @@ -143,11 +147,14 @@ void GameProtocol::handleControllerAction(Event *event) assert(kart_id < World::getWorld()->getNumKarts()); PlayerAction action = (PlayerAction)(data.getUInt8()); - int value = data.getUInt32(); - Log::info("GameProtocol", "Action at %f: %d %d %d", - time, kart_id, action, value); + int value = data.getUInt32(); + int value_l = data.getUInt32(); + int value_r = data.getUInt32(); + Log::info("GameProtocol", "Action at %f: %d %d %d %d %d", + time, kart_id, action, value, value_l, value_r); BareNetworkString *s = new BareNetworkString(3); - s->addUInt8(kart_id).addUInt8(action).addUInt16(value); + s->addUInt8(kart_id).addUInt8(action).addUInt32(value) + .addUInt32(value_l).addUInt32(value_r); RewindManager::get()->addNetworkEvent(this, s, time); } @@ -241,7 +248,12 @@ void GameProtocol::rewind(BareNetworkString *buffer) { int kart_id = buffer->getUInt8(); PlayerAction action = PlayerAction(buffer->getUInt8()); - int value = buffer->getUInt16(); + int value = buffer->getUInt32(); + int value_l = buffer->getUInt32(); + int value_r = buffer->getUInt32(); Controller *c = World::getWorld()->getKart(kart_id)->getController(); - c->action(action, value); + PlayerController *pc = dynamic_cast(c); + assert(pc); + if(pc) + pc->actionFromNetwork(action, value, value_l, value_r); } // rewind diff --git a/src/network/protocols/game_protocol.hpp b/src/network/protocols/game_protocol.hpp index d84925a62..3f6d63e87 100644 --- a/src/network/protocols/game_protocol.hpp +++ b/src/network/protocols/game_protocol.hpp @@ -52,6 +52,8 @@ private: int m_kart_id; PlayerAction m_action; int m_value; + int m_value_l; + int m_value_r; }; // struct Action // List of all kart actions to send to the server @@ -67,7 +69,7 @@ public: virtual void update(float dt) OVERRIDE; void controllerAction(int kart_id, PlayerAction action, - int value); + int value, int val_l, int val_r); void startNewState(); void addState(BareNetworkString *buffer); void sendState(); diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 5bcee6c3c..a4ae34bd1 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -494,19 +494,21 @@ void RewindManager::rewindTo(float rewind_time) { state->rewind(); index++; - //rindex--; if(index==m_rewind_info.end()) break; state = dynamic_cast(*index); } // Now go forward through the list of rewind infos: // ------------------------------------------------ - while( world->getTime() < current_time ) + // Need to exit loop if in-game menu is open, since world clock + // will not be increased while the game is paused + while( world->getTime() < current_time && + World::getWorld()->getPhase()!=WorldStatus::IN_GAME_MENU_PHASE) { // Now handle all states and events at the current time before // updating the world: while(index !=m_rewind_info.end() && - (*index)->getTime()<=world->getTime()+0.001f) + (*index)->getTime()<=world->getTime()) { if((*index)->isState()) { From 33caf5ed4b5a24b13a10677245361274933501a5 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 18 Jan 2017 20:57:40 +1100 Subject: [PATCH 019/413] Fixed unit test. --- src/network/transport_address.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/transport_address.cpp b/src/network/transport_address.cpp index f4aa09e29..1a9c3cdd0 100644 --- a/src/network/transport_address.cpp +++ b/src/network/transport_address.cpp @@ -107,6 +107,6 @@ void TransportAddress::unitTesting() TransportAddress t17("128.0.0.0"); assert(t17.getIP() == (128 << 24)); - assert(t17.isLAN()); + assert(!t17.isLAN()); } // unitTesting \ No newline at end of file From ef1f48da50f262d9fa74efb4c3b4b862f389e913 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 19 Jan 2017 17:30:34 +1100 Subject: [PATCH 020/413] Refactored the queue handling to be a separate object from the RewindManager. Added unit tests. --- sources.cmake | 2 +- src/main.cpp | 4 + src/network/rewind_info.cpp | 8 +- src/network/rewind_info.hpp | 3 +- src/network/rewind_manager.cpp | 325 ++++--------------- src/network/rewind_manager.hpp | 37 +-- src/network/rewind_queue.cpp | 555 +++++++++++++++++++++++++++++++++ src/network/rewind_queue.hpp | 118 +++++++ 8 files changed, 742 insertions(+), 310 deletions(-) create mode 100755 src/network/rewind_queue.cpp create mode 100755 src/network/rewind_queue.hpp diff --git a/sources.cmake b/sources.cmake index b4eb86a7c..eea380245 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,5 +1,5 @@ # Modify this file to change the last-modified date when you add/remove a file. -# This will then trigger a new cmake run automatically. +# This will then trigger a new cmake run automatically. file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp") file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp") file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") diff --git a/src/main.cpp b/src/main.cpp index ac1549c60..f91ef9b1e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -210,6 +210,7 @@ #include "network/network_config.hpp" #include "network/network_string.hpp" #include "network/rewind_manager.hpp" +#include "network/rewind_queue.hpp" #include "network/servers_manager.hpp" #include "network/stk_host.hpp" #include "network/protocols/get_public_address.hpp" @@ -1976,6 +1977,9 @@ void runUnitTests() Log::info("UnitTest", "Fonts for translation"); font_manager->unitTesting(); + Log::info("UnitTest", "RewindQueue"); + RewindQueue::unitTesting(); + Log::info("UnitTest", "====================="); Log::info("UnitTest", "Testing successful "); Log::info("UnitTest", "====================="); diff --git a/src/network/rewind_info.cpp b/src/network/rewind_info.cpp index ec9989afb..e4da03ae9 100644 --- a/src/network/rewind_info.cpp +++ b/src/network/rewind_info.cpp @@ -41,14 +41,16 @@ RewindInfoState::RewindInfoState(float time, Rewinder *rewinder, BareNetworkString *buffer, bool is_confirmed) : RewindInfoRewinder(time, rewinder, buffer, is_confirmed) { - m_local_physics_time = Physics::getInstance()->getPhysicsWorld() - ->getLocalTime(); + // rewinder = NULL is used in unit testing, in which case no world exists + if(rewinder!=NULL) + m_local_physics_time = Physics::getInstance()->getPhysicsWorld() + ->getLocalTime(); } // RewindInfoState // ============================================================================ RewindInfoEvent::RewindInfoEvent(float time, EventRewinder *event_rewinder, BareNetworkString *buffer, bool is_confirmed) - : RewindInfo(time, is_confirmed) + : RewindInfo(time, is_confirmed) { m_event_rewinder = event_rewinder; m_buffer = buffer; diff --git a/src/network/rewind_info.hpp b/src/network/rewind_info.hpp index a789a7788..0f2fbc565 100644 --- a/src/network/rewind_info.hpp +++ b/src/network/rewind_info.hpp @@ -159,7 +159,8 @@ public: * It calls undoState in the rewinder. */ virtual void undo() { - m_rewinder->undoState(getBuffer()); + if(m_rewinder) // Unit testing uses NULL as rewinder + m_rewinder->undoState(getBuffer()); } // undo // ------------------------------------------------------------------------ /** Rewinds to this state. This is called while going forwards in time diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index a4ae34bd1..68632a73e 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -65,14 +65,6 @@ RewindManager::RewindManager() */ RewindManager::~RewindManager() { - // Destroying the - AllRewindInfo::const_iterator i; - - for(i=m_rewind_info.begin(); i!=m_rewind_info.end(); ++i) - { - delete *i; - } - m_rewind_info.clear(); } // ~RewindManager // ---------------------------------------------------------------------------- @@ -80,10 +72,6 @@ RewindManager::~RewindManager() */ void RewindManager::reset() { -#ifdef REWIND_SEARCH_STATS - m_count_of_comparisons = 0; - m_count_of_searches = 0; -#endif m_is_rewinding = false; m_overall_state_size = 0; m_state_frequency = 0.1f; // save 10 states a second @@ -104,111 +92,9 @@ void RewindManager::reset() delete rewinder; } - AllRewindInfo::const_iterator i; - for(i=m_rewind_info.begin(); i!=m_rewind_info.end(); i++) - { - delete *i; - } - m_rewind_info.clear(); - m_next_event = m_rewind_info.begin(); - - m_network_events.lock(); - const AllRewindInfo &info = m_network_events.getData(); - for (i = info.begin(); i != info.end(); ++i) - { - delete *i; - } - m_network_events.getData().clear(); - m_network_events.unlock(); + m_rewind_queue.reset(); } // reset -// ---------------------------------------------------------------------------- -/** A compare function used when sorting the event lists. It sorts events by - * time. In case of equal times, it sorts states and events first (since the - * state needs to be restored when replaying first before any other events). - */ -bool RewindManager::_RewindInfoCompare::operator()(const RewindInfo *ri1, - const RewindInfo *ri2) const -{ - if (ri1->getTime() < ri2->getTime()) return true; - if (ri1->getTime() > ri2->getTime()) return false; - - // Now the times are equal. In this case make sure that states and - // time events are 'smallest', i.e. sorted first (which is necessary for - // replay). Time and State events at the same time do not happen, so no - // further test are necessary - return ri1->isState() || ri1->isTime(); -} // RewindInfoCompare::operator() - -// ---------------------------------------------------------------------------- -/** Inserts a RewindInfo object in the list of all events at the correct time. - * If there are several RewindInfo at the exact same time, state RewindInfo - * will be insert at the front, and event and time info at the end of the - * RewindInfo with the same time. - * \param ri The RewindInfo object to insert. - */ -void RewindManager::insertRewindInfo(RewindInfo *ri) -{ - AllRewindInfo::iterator i = std::upper_bound(m_rewind_info.begin(), - m_rewind_info.end(), ri, - RewindManager::RewindInfoCompare); - m_rewind_info.insert(i, ri); -} // insertRewindInfo - -// ---------------------------------------------------------------------------- -/** Returns the first (i.e. lowest) index i in m_rewind_info which fulfills - * time(i) < target_time <= time(i+1) and is a state. This is the state - * from which a rewind can start - all states for the karts will be well - * defined. - * \param time Time for which an index is searched. - * \return Index in m_rewind_info after which to add rewind data. - */ -RewindManager::AllRewindInfo::reverse_iterator - RewindManager::findFirstIndex(float target_time) -{ - // For now do a linear search, even though m_rewind_info is sorted - // I would expect that most insertions will be towards the (very) - // end of the list, since rewinds should be for short periods of time. - // Note that after finding an entry in a binary search, you still - // have to do a linear search to find the last entry with the same - // time in order to minimise the later necessary memory move. - - // Gather some statistics about search for now: -#ifdef REWIND_SEARCH_STATS - m_count_of_searches++; -#endif - AllRewindInfo::reverse_iterator index = m_rewind_info.rbegin(); - AllRewindInfo::reverse_iterator index_last_state = m_rewind_info.rend(); - while(index!=m_rewind_info.rend()) - { -#ifdef REWIND_SEARCH_STATS - m_count_of_comparisons++; -#endif - if((*index)->isState()) - { - if( (*index)->getTime() <= target_time ) - { - return index; - } - index_last_state = index; - } - index++; - } - - if(index_last_state==m_rewind_info.rend()) - { - Log::fatal("RewindManager", - "Can't find any state when rewinding to %f - aborting.", - target_time); - } - - // Otherwise use the last found state - not much we can do in this case. - Log::error("RewindManager", - "Can't find state to rewind to for time %f, using %f.", - target_time, (*index_last_state)->getTime()); - return index_last_state; // avoid compiler warning -} // findFirstIndex - // ---------------------------------------------------------------------------- /** Adds an event to the rewind data. The data to be stored must be allocated * and not freed by the caller! @@ -225,13 +111,12 @@ void RewindManager::addEvent(EventRewinder *event_rewinder, Log::error("RewindManager", "Adding event when rewinding"); return; } + Log::verbose("RewindManager", "Time world %f self-event %f", World::getWorld()->getTime(), getCurrentTime()); if (time < 0) time = getCurrentTime(); - RewindInfo *ri = new RewindInfoEvent(time, event_rewinder, - buffer, confirmed); - insertRewindInfo(ri); + m_rewind_queue.addEvent(event_rewinder, buffer, confirmed, time); } // addEvent // ---------------------------------------------------------------------------- @@ -245,21 +130,9 @@ void RewindManager::addEvent(EventRewinder *event_rewinder, void RewindManager::addNetworkEvent(EventRewinder *event_rewinder, BareNetworkString *buffer, float time) { - RewindInfo *ri = new RewindInfoEvent(time, event_rewinder, - buffer, /*confirmed*/true); - - // Sort the incoming network events so that we can use list::merge - // to move the network events into the RewindInfo main list. - m_network_events.lock(); - AllRewindInfo::iterator i = - std::upper_bound(m_network_events.getData().begin(), - m_network_events.getData().end() , ri, - RewindManager::RewindInfoCompare ); - m_network_events.getData().insert(i, ri); - m_network_events.unlock(); + m_rewind_queue.addNetworkEvent(event_rewinder, buffer, time); Log::verbose("RewindManager", "Time world %f network-event %f", World::getWorld()->getTime(), time); - } // addEvent // ---------------------------------------------------------------------------- @@ -273,18 +146,8 @@ void RewindManager::addNetworkEvent(EventRewinder *event_rewinder, void RewindManager::addNetworkState(int rewinder_index, BareNetworkString *buffer, float time) { - RewindInfo *ri = new RewindInfoState(time, m_all_rewinder[rewinder_index], - buffer, /*confirmed*/true); - - // Sort the incoming network events so that we can use list::merge - // to move the network events into the RewindInfo main list. - m_network_events.lock(); - AllRewindInfo::iterator i = - std::upper_bound(m_network_events.getData().begin(), - m_network_events.getData().end(), ri, - RewindManager::RewindInfoCompare ); - m_network_events.getData().insert(i, ri); - m_network_events.unlock(); + m_rewind_queue.addNetworkState(m_all_rewinder[rewinder_index], buffer, + time); Log::verbose("RewindManager", "Time world %f network-state %f", World::getWorld()->getTime(), time); } // addState @@ -311,14 +174,11 @@ void RewindManager::saveStates() { // No full state necessary, add a dummy entry for the time // which increases replay precision (same time step size) - RewindInfo *ri = new RewindInfoTime(getCurrentTime()); - insertRewindInfo(ri); + m_rewind_queue.addTimeEvent(getCurrentTime()); } return; } - bool was_empty = m_rewind_info.empty(); - // For now always create a snapshot. if (NetworkConfig::get()->isServer()) GameProtocol::getInstance()->startNewState(); @@ -329,11 +189,8 @@ void RewindManager::saveStates() if(buffer && buffer->size()>=0) { m_overall_state_size += buffer->size(); - RewindInfo *ri = new RewindInfoState(getCurrentTime(), - *i, buffer, - /*is_confirmed*/true); - assert(ri); - insertRewindInfo(ri); + m_rewind_queue.addState(*i, buffer, /*confirmed*/true, + getCurrentTime()); if(NetworkConfig::get()->isServer()) GameProtocol::getInstance()->addState(buffer); } // size >= 0 @@ -343,12 +200,8 @@ void RewindManager::saveStates() if (NetworkConfig::get()->isServer()) GameProtocol::getInstance()->sendState(); - if (was_empty && !m_rewind_info.empty()) - m_next_event = m_rewind_info.begin(); - Log::verbose("RewindManager", "%f allocated %ld bytes search %d/%d=%f", - World::getWorld()->getTime(), m_overall_state_size, - m_count_of_comparisons, m_count_of_searches, - float(m_count_of_comparisons)/ float(m_count_of_searches) ); + Log::verbose("RewindManager", "%f allocated %ld", + World::getWorld()->getTime(), m_overall_state_size); m_last_saved_state = time; } // saveStates @@ -359,68 +212,39 @@ void RewindManager::saveStates() */ void RewindManager::playEventsTill(float time) { - m_network_events.lock(); - if (m_rewind_info.empty() && m_network_events.getData().empty()) - { - m_network_events.unlock(); + // No events, nothing to do + if (m_rewind_queue.isEmpty()) return; - } - /** First merge all newly received network events into the main - * event list */ - bool rewind_necessary = false; - float rewind_time = -1.0f; + bool needs_rewind; + float rewind_time; - if (!m_network_events.getData().empty()) - { - // Check if a rewind is necessary - rewind_time = m_network_events.getData().front()->getTime(); - Log::info("RewindManager", "At %f merging states from %f", + m_rewind_queue.mergeNetworkData(&needs_rewind, &rewind_time); + if(needs_rewind) + Log::info("RewindManager", "At %f merging states from %f needs rewind", World::getWorld()->getTime(), rewind_time); - if (rewind_time < time) - { + else + Log::info("RewindManager", "At %f no need for rewind", + World::getWorld()->getTime()); - Log::info("RewindManager", - "Rewind necessary at %f because of event at %f", - time, rewind_time); - rewind_necessary = true; - } - // It is possible that m_next_event points m_rewind_info.end() - // and that new elements are added to the end of the list. In this - // case m_rewind_info end would still point to end(), but should - // point to the newly added element. To achieve this, we first - // decrement m_next_eventr (note that it was tested previously that - // the list is not empty), merge in the new events, and then - // increment m_next_event again. If elements have been added to - // the end of the list, m_next_event will now corretly point to them. - bool adjust_next = !m_rewind_info.empty(); - if(adjust_next) m_next_event--; - m_rewind_info.merge(m_network_events.getData(), - RewindManager::RewindInfoCompare); - if (adjust_next) - m_next_event++; - else // rewind_info was empty, set pointer to first element - m_next_event = m_rewind_info.begin(); - } // if !m_network_events empty - - m_network_events.unlock(); - - if (rewind_necessary) + if (needs_rewind) { rewindTo(rewind_time); } + assert(!m_is_rewinding); m_is_rewinding = true; - while (m_next_event !=m_rewind_info.end() ) + + while(m_rewind_queue.hasMoreRewindInfo()) { - RewindInfo *ri = *m_next_event; + RewindInfo *ri = m_rewind_queue.getNext(); if (ri->getTime() > time) { m_is_rewinding = false; return; } - m_next_event++; + ++m_rewind_queue; if(ri->isEvent()) ri->rewind(); } @@ -438,37 +262,11 @@ void RewindManager::rewindTo(float rewind_time) StkTime::getRealTime()); history->doReplayHistory(History::HISTORY_NONE); - // First find the state to which we need to rewind - // ------------------------------------------------ - AllRewindInfo::reverse_iterator rindex = findFirstIndex(rewind_time); - //rindex--; - AllRewindInfo::reverse_iterator xx = rindex; - ++xx; - AllRewindInfo::iterator index = --(xx.base()); - - if(!(*rindex)->isState()) - { - Log::error("RewindManager", "No state for rewind to %f, state %d.", - rewind_time, index); - return; - } - m_is_rewinding = true; // Then undo the rewind infos going backwards in time // -------------------------------------------------- - AllRewindInfo::reverse_iterator i; - for(i= m_rewind_info.rbegin(); i!=rindex; i++) - { - (*i)->undo(); - - // Now all states after the time we rewind to are not confirmed - // anymore. They need to be rewritten when going forward during - // the rewind. - if((*i)->isState() && - (*i)->getTime() > (*index)->getTime() ) - (*i)->setConfirmed(false); - } // for i>state - + m_is_rewinding = true; + m_rewind_queue.undoUntil(rewind_time); // Rewind the required state(s) // ---------------------------- @@ -477,7 +275,7 @@ void RewindManager::rewindTo(float rewind_time) // Get the (first) full state to which we have to rewind RewindInfoState *state = - dynamic_cast(*rindex); + dynamic_cast(m_rewind_queue.getNext()); // Store the time to which we have to replay to float exact_rewind_time = state->getTime(); @@ -493,9 +291,9 @@ void RewindManager::rewindTo(float rewind_time) while(state && state->getTime()==exact_rewind_time) { state->rewind(); - index++; - if(index==m_rewind_info.end()) break; - state = dynamic_cast(*index); + ++m_rewind_queue; + if(!m_rewind_queue.hasMoreRewindInfo()) break; + state = dynamic_cast(m_rewind_queue.getNext()); } // Now go forward through the list of rewind infos: @@ -507,22 +305,27 @@ void RewindManager::rewindTo(float rewind_time) { // Now handle all states and events at the current time before // updating the world: - while(index !=m_rewind_info.end() && - (*index)->getTime()<=world->getTime()) + if (m_rewind_queue.hasMoreRewindInfo()) { - if((*index)->isState()) + RewindInfo *ri = m_rewind_queue.getNext(); + while (ri->getTime() <= world->getTime()) { - // TOOD: replace the old state with a new state. - // For now just set it to confirmed - (*index)->setConfirmed(true); - } - else if((*index)->isEvent()) - { - (*index)->rewind(); - } - index++; - } - float dt = determineTimeStepSize(index, current_time); + if (ri->isState()) + { + // TOOD: replace the old state with a new state. + // For now just set it to confirmed + ri->setConfirmed(true); + } + else if (ri->isEvent()) + { + ri->rewind(); + } + + ++m_rewind_queue; + } // while ri->getTime() <= world->getTime() + } // if m_rewind_queue.hasMoreRewindInfo() + + float dt = m_rewind_queue.determineNextDT(current_time); world->updateWorld(dt); #undef SHOW_ROLLBACK #ifdef SHOW_ROLLBACK @@ -537,26 +340,4 @@ void RewindManager::rewindTo(float rewind_time) StkTime::getRealTime()); } // rewindTo -// ---------------------------------------------------------------------------- -/** Determines the next time step size to use when recomputing the physics. - * The time step size is either 1/60 (default physics), or less, if there - * is an even to handle before that time. - * \param next_state The next state to replay. - * \param end_time The end time to which we must replay forward. Don't - * return a dt that would be bigger tham this value. - * \return The time step size to use in the next simulation step. - */ -float RewindManager::determineTimeStepSize(AllRewindInfo::iterator next_state, - float end_time) -{ - // If there is a next state (which is known to have a different time) - // use the time difference to determine the time step size. - if(next_state !=m_rewind_info.end()) - return (*next_state)->getTime() - World::getWorld()->getTime(); - - // Otherwise, i.e. we are rewinding the last state/event, take the - // difference between that time and the world time at which the rewind - // was triggered. - return end_time - (*(--next_state))->getTime(); - -} // determineTimeStepSize +// ---------------------------------------------------------------------------- \ No newline at end of file diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index 44a9ae793..c4439dafb 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -20,6 +20,7 @@ #define HEADER_REWIND_MANAGER_HPP #include "network/rewinder.hpp" +#include "network/rewind_queue.hpp" #include "utils/ptr_vector.hpp" #include "utils/synchronised.hpp" @@ -90,21 +91,8 @@ private: /** A list of all objects that can be rewound. */ AllRewinder m_all_rewinder; - /** Pointer to all saved states. */ - typedef std::list AllRewindInfo; - - /** The list of all events that are affected by a rewind. */ - AllRewindInfo m_rewind_info; - - /** The list of all events received from the network. They are stored - * in a separate thread (so this data structure is thread-save), and - * merged into m_rewind_info from the main thread. This design (as - * opposed to locking m_rewind_info) reduces the synchronisation - * between main thread and network thread. */ - Synchronised m_network_events; - - /** Index of the next event to be used when playing events. */ - AllRewindInfo::const_iterator m_next_event; + /** The queue that stores all rewind infos. */ + RewindQueue m_rewind_queue; /** Overall amount of memory allocated by states. */ unsigned int m_overall_state_size; @@ -124,25 +112,8 @@ private: * events later. */ float m_current_time; -#define REWIND_SEARCH_STATS - -#ifdef REWIND_SEARCH_STATS - /** Gather some statistics about how many comparisons we do, - * to find out if it's worth doing a binary search.*/ - mutable int m_count_of_comparisons; - mutable int m_count_of_searches; -#endif - RewindManager(); - ~RewindManager(); - AllRewindInfo::reverse_iterator findFirstIndex(float time); - void insertRewindInfo(RewindInfo *ri); - float determineTimeStepSize(AllRewindInfo::iterator state, float max_time); - // ------------------------------------------------------------------------ - struct _RewindInfoCompare - { - bool operator()(const RewindInfo *ri1, const RewindInfo *r2) const; - } RewindInfoCompare; // _RewindInfoCompare + ~RewindManager(); public: // First static functions to manage rewinding. diff --git a/src/network/rewind_queue.cpp b/src/network/rewind_queue.cpp new file mode 100755 index 000000000..30f05c12c --- /dev/null +++ b/src/network/rewind_queue.cpp @@ -0,0 +1,555 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/rewind_queue.hpp" + +#include "modes/world.hpp" +#include "network/rewind_info.hpp" +#include "network/rewind_manager.hpp" + +#include + +/** The constructor. + */ +RewindQueue::RewindQueue() +{ + reset(); +} // RewindQueue + +// ---------------------------------------------------------------------------- +/** Frees all saved state information. Note that the Rewinder data must be + * freed elsewhere. + */ +RewindQueue::~RewindQueue() +{ + // Destroying the + AllRewindInfo::const_iterator i; + + for(i=m_rewind_info.begin(); i!=m_rewind_info.end(); ++i) + { + delete *i; + } + m_rewind_info.clear(); +} // ~RewindQueue + +// ---------------------------------------------------------------------------- +/** Frees all saved state information and all destroyable rewinder. + */ +void RewindQueue::reset() +{ +#ifdef REWIND_SEARCH_STATS + m_count_of_comparisons = 0; + m_count_of_searches = 0; +#endif + + AllRewindInfo::const_iterator i; + for(i=m_rewind_info.begin(); i!=m_rewind_info.end(); i++) + { + delete *i; + } + m_rewind_info.clear(); + m_current = m_rewind_info.begin(); + + m_network_events.lock(); + const AllRewindInfo &info = m_network_events.getData(); + for (i = info.begin(); i != info.end(); ++i) + { + delete *i; + } + m_network_events.getData().clear(); + m_network_events.unlock(); +} // reset + +// ---------------------------------------------------------------------------- +/** A compare function used when sorting the event lists. It sorts events by + * time. In case of equal times, it sorts states and events first (since the + * state needs to be restored when replaying first before any other events). + */ +bool RewindQueue::_RewindInfoCompare::operator()(const RewindInfo *ri1, + const RewindInfo *ri2) const +{ + if (ri1->getTime() < ri2->getTime()) return true; + if (ri1->getTime() > ri2->getTime()) return false; + + // Now the times are equal. In this case make sure that states are sorted + // before times and they before events. If two identical types are found, + // sort them by pointer address (to guarantee that (a,b) and (b,a) give + // consistent results (otherwise e.g. VS in debug mode will detect + // inconsistencies and throw an exception). + if (ri1->isState()) + { + if (ri2->isState()) return ri1 < ri2; + return true; // state first, i.e smallest + } + if (ri1->isTime()) + { + if (ri2->isState()) return false; + if (ri2->isEvent()) return true; + return ri1 < ri2; + } + // Now ri1 must be event + if (ri2->isEvent()) return ri1 < ri2; + // ri2 is state or time which must come first + return false; + +} // RewindQueue::operator() + +// ---------------------------------------------------------------------------- +/** Inserts a RewindInfo object in the list of all events at the correct time. + * If there are several RewindInfo at the exact same time, state RewindInfo + * will be insert at the front, and event and time info at the end of the + * RewindInfo with the same time. + * \param ri The RewindInfo object to insert. + */ +void RewindQueue::insertRewindInfo(RewindInfo *ri) +{ + AllRewindInfo::iterator i = + std::upper_bound(m_rewind_info.begin(), + m_rewind_info.end(), ri, + RewindQueue::m_rewind_info_compare); + AllRewindInfo::iterator new_pos = m_rewind_info.insert(i, ri); + if (m_current == m_rewind_info.end()) + m_current = new_pos; +} // insertRewindInfo + +// ---------------------------------------------------------------------------- +/** Adds an event to the rewind data. The data to be stored must be allocated + * and not freed by the caller! + * \param time Time at which the event was recorded. + * \param buffer Pointer to the event data. + */ +void RewindQueue::addEvent(EventRewinder *event_rewinder, + BareNetworkString *buffer, bool confirmed, + float time ) +{ + RewindInfo *ri = new RewindInfoEvent(time, event_rewinder, + buffer, confirmed); + insertRewindInfo(ri); +} // addEvent + +// ---------------------------------------------------------------------------- +/** Adds a state from the local simulation. It is not thread-safe, so needs to + * be called from the main thread + */ +void RewindQueue::addState(Rewinder *rewinder, BareNetworkString *buffer, + bool confirmed, float time) +{ + RewindInfo *ri = new RewindInfoState(time, rewinder, buffer, confirmed); + assert(ri); + insertRewindInfo(ri); +} // addState + +// ---------------------------------------------------------------------------- +/** Adds an event to the list of network rewind data. This function is + * threadsafe so can be called by the network thread. The data is synched + * to m_rewind_info by the main thread. The data to be stored must be + * allocated and not freed by the caller! + * \param time Time at which the event was recorded. + * \param buffer Pointer to the event data. + */ +void RewindQueue::addNetworkEvent(EventRewinder *event_rewinder, + BareNetworkString *buffer, float time) +{ + RewindInfo *ri = new RewindInfoEvent(time, event_rewinder, + buffer, /*confirmed*/true); + + // Sort the incoming network events so that we can use list::merge + // to move the network events into the RewindInfo main list. + m_network_events.lock(); + AllRewindInfo::iterator i = + std::upper_bound(m_network_events.getData().begin(), + m_network_events.getData().end() , ri, + RewindQueue::m_rewind_info_compare ); + m_network_events.getData().insert(i, ri); + m_network_events.unlock(); +} // addEvent + +// ---------------------------------------------------------------------------- +/** Adds a state to the list of network rewind data. This function is + * threadsafe so can be called by the network thread. The data is synched + * to m_rewind_info by the main thread. The data to be stored must be + * allocated and not freed by the caller! + * \param time Time at which the event was recorded. + * \param buffer Pointer to the event data. + */ +void RewindQueue::addNetworkState(Rewinder *rewinder, + BareNetworkString *buffer, float time) +{ + RewindInfo *ri = new RewindInfoState(time, rewinder, + buffer, /*confirmed*/true); + + // Sort the incoming network events so that we can use list::merge + // to move the network events into the RewindInfo main list. + m_network_events.lock(); + AllRewindInfo::iterator i = + std::upper_bound(m_network_events.getData().begin(), + m_network_events.getData().end(), ri, + RewindQueue::m_rewind_info_compare ); + m_network_events.getData().insert(i, ri); + m_network_events.unlock(); +} // addState + +// ---------------------------------------------------------------------------- +/** Adds a (dummy) time event to store the time. This enables rewinding to + * replay using the same time step size, minimising errors. + */ +void RewindQueue::addTimeEvent(float time) +{ + RewindInfo *ri = new RewindInfoTime(time); + insertRewindInfo(ri); +} // addTimeEvent + +// ---------------------------------------------------------------------------- +/** Merges thread-safe all data received from the network with the current + * local rewind information. + * \param needs_rewind True if network rewind information was received which + * was in the past (of this simulation), so a rewind must be performed. + * \param rewind_time If needs_rewind is true, the time to which a rewind must + * be performed (at least). Otherwise undefined, but the value might + * be modified in this function. + */ +void RewindQueue::mergeNetworkData(bool *needs_rewind, float *rewind_time) +{ + *needs_rewind = false; + m_network_events.lock(); + if (m_rewind_info.empty() && m_network_events.getData().empty()) + { + m_network_events.unlock(); + return; + } + + /** First merge all newly received network events into the main + * event list */ + + *rewind_time = -1.0f; + bool adjust_next = false; + + if (!m_network_events.getData().empty()) + { + // Check if a rewind is necessary + *rewind_time = m_network_events.getData().front()->getTime(); + if (*rewind_time < World::getWorld()->getTime()) + { + *needs_rewind = true; + } + // It is possible that m_current points m_rewind_info.end() + // and that new elements are added to the end of the list. In this + // case m_rewind_info end would still point to end(), but should + // point to the newly added element. To achieve this, we first + // decrement m_current (note that it was tested previously that + // the list is not empty), merge in the new events, and then + // increment m_current again. If elements have been added to + // the end of the list, m_current will now corretly point to them. + + adjust_next = !m_rewind_info.empty(); + if (adjust_next) m_current--; + m_rewind_info.merge(m_network_events.getData(), m_rewind_info_compare); + + // RewindInfoCompare::RewindInfoCompare); + if (adjust_next) + m_current++; + else // rewind_info was empty, set pointer to first element + m_current = m_rewind_info.begin(); + } // if !m_network_events empty + + m_network_events.unlock(); + + +} // mergeNetworkData + +// ---------------------------------------------------------------------------- +bool RewindQueue::isEmpty() const +{ + if (m_current != m_rewind_info.end()) + return false; + + m_network_events.lock(); + bool no_network_events = m_network_events.getData().empty(); + m_network_events.unlock(); + + return no_network_events; +} // isEmpty + +// ---------------------------------------------------------------------------- +/** Returns true if there is at least one more RewindInfo available. + */ +bool RewindQueue::hasMoreRewindInfo() const +{ + return m_current != m_rewind_info.end(); +} // hasMoreRewindInfo + +// ---------------------------------------------------------------------------- +/** Determines the next time step size to use when recomputing the physics. + * The time step size is either 1/60 (default physics), or less, if there + * is an even to handle before that time. + * \param next_state The next state to replay. + * \param end_time The end time to which we must replay forward. Don't + * return a dt that would be bigger tham this value. + * \return The time step size to use in the next simulation step. + */ +float RewindQueue::determineNextDT(float end_time) +{ + // If there is a next state (which is known to have a different time) + // use the time difference to determine the time step size. + if(m_current !=m_rewind_info.end()) + return (*m_current)->getTime() - World::getWorld()->getTime(); + + // Otherwise, i.e. we are rewinding the last state/event, take the + // difference between that time and the world time at which the rewind + // was triggered. + return end_time - (*(--m_current))->getTime(); + +} // determineNextDT + +// ---------------------------------------------------------------------------- +/** Rewinds the rewind queue and undos all events/states stored. It stops + * when the first state is reached that was recorded before the undo_time. + * It sets the internal 'current' pointer to this state. + * \param undo_time To what at least events need to be undone. + */ +void RewindQueue::undoUntil(float undo_time) +{ + AllRewindInfo::iterator i = m_rewind_info.end(); + + while (i != m_rewind_info.begin()) + { + --i; + if ((*i)->isState() && + (*i)->getTime() <= undo_time) + { + // FIXME: we might have more than one state, so go back along + // states at the same time + m_current = i; + return; + } + + // Undo the effect of the event. + (*i)->undo(); + + // Any states saved after the undo_time are invalid. For now mark + // them as being unconfirmed. + if ((*i)->isState() && + (*i)->getTime() > undo_time) + { + (*i)->setConfirmed(false); + } + } // while i!=m_rewind_info.begin() + + Log::error("RewindManager", "No state for rewind to %f", + undo_time); + m_current = m_rewind_info.begin(); + +} // undoUntil + +// ---------------------------------------------------------------------------- +/** Tests the sorting order of events with the same time stamp: states must + * be first, then time info, then events. + */ +void RewindQueue::testingSortingOrderType(EventRewinder *rewinder, int types[3]) +{ + for(unsigned int i=0; i<3; i++) + { + switch (types[i]) + { + case 1: // Insert State + { + BareNetworkString *s = new BareNetworkString(); + s->addUInt8(1); + addState(NULL, s, true, 0.0f); + break; + } + case 2: + addTimeEvent(0.0f); + break; + case 3: + { + BareNetworkString *s = new BareNetworkString(); + s->addUInt8(2); + addEvent(rewinder, s, true, 0.0f); + break; + } + default: assert(false); + } // switch + } // for i =0; i<3 + + // This should go back to the first state + undoUntil(0.0); + + // Now test if the three events are sorted in the right order: + // State, then time, then event + assert(hasMoreRewindInfo()); + assert(!isEmpty()); + RewindInfo *ri = getNext(); + assert(ri->getTime() == 0.0f); + assert(ri->isState()); + RewindInfoState *ris = dynamic_cast(ri); + assert(ris->getBuffer()->getTotalSize() == 1); + assert(ris->getBuffer()->getUInt8() == 1); + + operator++(); + ri = getNext(); + assert(!isEmpty()); + assert(hasMoreRewindInfo()); + assert(ri->getTime() == 0.0f); + assert(ri->isTime()); + + operator++(); + ri = getNext(); + assert(!isEmpty()); + assert(hasMoreRewindInfo()); + assert(ri->getTime() == 0.0f); + assert(ri->isEvent()); + RewindInfoEvent *rie = dynamic_cast(ri); + assert(rie->getBuffer()->getTotalSize() == 1); + assert(rie->getBuffer()->getUInt8() == 2); + + operator++(); + assert(isEmpty()); + assert(!hasMoreRewindInfo()); + +} // testingSortingOrderType + +// ---------------------------------------------------------------------------- +/** Tests sorting of rewind infos according to their time. It assumes + * different times for all events. + * \param types The types for the three events. + * \param times The times at which the events happened. Must be >0 + * (since an additional state at t=0 is added to avoid + * warnings during undoUntil() ). + */ +void RewindQueue::testingSortingOrderTime(EventRewinder *rewinder, + int types[3], float times[3]) +{ + float min_rewind_time = std::min({ times[0], times[1], times[2] }); + + // Avoid warnings about 'no state found' when rewinding + // and there is indeed no state at the given time. + addState(NULL, new BareNetworkString(), true, 0); + for (unsigned int i = 0; i<3; i++) + { + switch (types[i]) + { + case 1: // Insert State + { + BareNetworkString *s = new BareNetworkString(); + s->addUInt8(1); + addState(NULL, s, true, times[i]); + break; + } + case 2: + addTimeEvent(times[i]); + break; + case 3: + { + BareNetworkString *s = new BareNetworkString(); + s->addUInt8(2); + addEvent(rewinder, s, true, times[i]); + break; + } + default: assert(false); + } // switch + + } // for i =0; i<3 + + // This should go back to the first state + undoUntil(min_rewind_time); + + RewindInfo *ri_prev = getNext(); + operator++(); + while (hasMoreRewindInfo()) + { + RewindInfo *ri = getNext(); + assert(ri_prev->getTime() < ri->getTime()); + ri_prev = ri; + operator++(); + } // while hasMoreRewindInfo + +} // testingSortingOrderTime + +// ---------------------------------------------------------------------------- +/** Unit tests for RewindQueue. It tests: + * - Sorting order of RewindInfos at the same time (i.e. state before time + * before events). + * - Sorting order of RewindInfos with different timestamps (and a mixture + * of types). + * - Special cases that triggered incorrect behaviour previously. + */ +void RewindQueue::unitTesting() +{ + // Some classes need the RewindManager (to register themselves with) + RewindManager::create(); + + // A dummy Rewinder and EventRewinder class since some of the calls being + // tested here need an instance. + class DummyRewinder : public Rewinder, public EventRewinder + { + public: + BareNetworkString* saveState() const { return NULL; } + virtual void undoEvent(BareNetworkString *s) {} + virtual void rewindToEvent(BareNetworkString *s) {} + virtual void rewindToState(BareNetworkString *s) {} + virtual void undoState(BareNetworkString *s) {} + virtual void undo(BareNetworkString *s) {} + virtual void rewind(BareNetworkString *s) {} + DummyRewinder() : Rewinder(true) {} + }; + DummyRewinder *dummy_rewinder = new DummyRewinder(); + + RewindQueue q0; + assert(q0.isEmpty()); + + // Test sorting of different RewindInfo with identical time stamps. + // ---------------------------------------------------------------- + int types[6][3] = { { 1,2,3 }, { 1,3,2 }, { 2,1,3 }, + { 2,3,1 }, { 3,1,2 }, { 3,2,1 } }; + float times[6][3] = { { 1,2,3 }, { 1,3,2 }, { 2,1,3 }, + { 2,3,1 }, { 3,1,2 }, { 3,2,1 } }; + for (unsigned int i = 0; i < 6; i++) + { + RewindQueue *rq = new RewindQueue(); + rq->testingSortingOrderType(dummy_rewinder, types[i]); + delete rq; + } + + // Test sorting of different RewindInfo at different times + // Checks all combinations of times and types. + // ------------------------------------------------------- + for (unsigned int i = 0; i < 6; i++) + { + for (unsigned int j = 0; j < 6; j++) + { + RewindQueue *rq = new RewindQueue(); + rq->testingSortingOrderTime(dummy_rewinder, types[i], times[j]); + delete rq; + } // for j in times + } // for i in types + + // Bugs seen before + // ---------------- + // 1) Current pointer was not reset from end of list when an event + // was added and the pointer was already at end of list + RewindQueue b1; + b1.addState(NULL, new BareNetworkString(), true, 1.0f); + ++b1; // Should now point at end of list + b1.hasMoreRewindInfo(); + b1.addEvent(NULL, new BareNetworkString(), true, 2.0f); + RewindInfo *ri = b1.getNext(); + assert(ri->getTime() == 2.0f); + assert(ri->isEvent()); + +} // unitTesting \ No newline at end of file diff --git a/src/network/rewind_queue.hpp b/src/network/rewind_queue.hpp new file mode 100755 index 000000000..a82b3b20a --- /dev/null +++ b/src/network/rewind_queue.hpp @@ -0,0 +1,118 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2017 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_REWIND_QUEUE_HPP +#define HEADER_REWIND_QUEUE_HPP + +#include "network/rewinder.hpp" +#include "utils/ptr_vector.hpp" +#include "utils/synchronised.hpp" + +#include +#include +#include + +class RewindInfo; +class EventRewinder; + +/** \ingroup network + */ + +class RewindQueue +{ +private: + + /** Pointer to all saved states. */ + typedef std::list AllRewindInfo; + + /** The list of all events that are affected by a rewind. */ + AllRewindInfo m_rewind_info; + + /** The list of all events received from the network. They are stored + * in a separate thread (so this data structure is thread-save), and + * merged into m_rewind_info from the main thread. This design (as + * opposed to locking m_rewind_info) reduces the synchronisation + * between main thread and network thread. */ + Synchronised m_network_events; + + /** Iterator to the next rewind info to be handled. */ + AllRewindInfo::iterator m_current; + +#define REWIND_SEARCH_STATS + +#ifdef REWIND_SEARCH_STATS + /** Gather some statistics about how many comparisons we do, + * to find out if it's worth doing a binary search.*/ + mutable int m_count_of_comparisons; + mutable int m_count_of_searches; +#endif + + void insertRewindInfo(RewindInfo *ri); + + struct _RewindInfoCompare + { + bool operator()(const RewindInfo *ri1, const RewindInfo *ri2) const; + } m_rewind_info_compare; + + void testingSortingOrderType(EventRewinder *rewinder, int types[3]); + void testingSortingOrderTime(EventRewinder *rewinder, int types[3], + float times[3] ); + +public: + static void unitTesting(); + + RewindQueue(); + ~RewindQueue(); + void reset(); + void addEvent(EventRewinder *event_rewinder, BareNetworkString *buffer, + bool confirmed, float time); + void addState(Rewinder *rewinder, BareNetworkString *buffer, + bool confirmed, float time); + void addNetworkEvent(EventRewinder *event_rewinder, + BareNetworkString *buffer, float time); + void addNetworkState(Rewinder *rewinder, BareNetworkString *buffer, + float time); + void addTimeEvent(float time); + void mergeNetworkData(bool *needs_rewind, float *rewind_time); + bool isEmpty() const; + bool hasMoreRewindInfo() const; + void undoUntil(float undo_time); + float determineNextDT(float max_time); + + // ------------------------------------------------------------------------ + RewindQueue::AllRewindInfo::iterator& operator++() + { + assert(m_current != m_rewind_info.end()); + m_current++; + return m_current; + } // operator++ + + // ------------------------------------------------------------------------ + /** Returns the next event. Caller must make sure that there is at least + * one more RewindInfo (see hasMoreRewindInfo()). */ + RewindInfo *getNext() + { + assert(m_current != m_rewind_info.end()); + return *m_current; + } // getNext + +}; // RewindQueue + + +#endif + From 414c51a55821e0e1659bec4b6c00a41126f403a4 Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 20 Jan 2017 10:46:37 +1100 Subject: [PATCH 021/413] Bugfix: loop did not test if there were RewindInfo available; simplified loop as well. --- src/network/rewind_manager.cpp | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 68632a73e..32fd6095f 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -305,25 +305,24 @@ void RewindManager::rewindTo(float rewind_time) { // Now handle all states and events at the current time before // updating the world: - if (m_rewind_queue.hasMoreRewindInfo()) + while (m_rewind_queue.hasMoreRewindInfo()) { RewindInfo *ri = m_rewind_queue.getNext(); - while (ri->getTime() <= world->getTime()) - { - if (ri->isState()) - { - // TOOD: replace the old state with a new state. - // For now just set it to confirmed - ri->setConfirmed(true); - } - else if (ri->isEvent()) - { - ri->rewind(); - } + if (ri->getTime() > world->getTime()) break; - ++m_rewind_queue; - } // while ri->getTime() <= world->getTime() - } // if m_rewind_queue.hasMoreRewindInfo() + if (ri->isState()) + { + // TOOD: replace the old state with a new state. + // For now just set it to confirmed + ri->setConfirmed(true); + } + else if (ri->isEvent()) + { + ri->rewind(); + } + + ++m_rewind_queue; + } // while ri->getTime() <= world->getTime() float dt = m_rewind_queue.determineNextDT(current_time); world->updateWorld(dt); From d038848c2e1214b888ea272f44fbfd7536bb3a4d Mon Sep 17 00:00:00 2001 From: hiker Date: Sun, 22 Jan 2017 17:41:47 +1100 Subject: [PATCH 022/413] If a client should have received a state in the future, use it (long term this should not happen, since the server is behind the clients). --- src/network/rewind_manager.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 32fd6095f..e11eceef4 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -247,6 +247,13 @@ void RewindManager::playEventsTill(float time) ++m_rewind_queue; if(ri->isEvent()) ri->rewind(); + else if (ri->isState()) + { + Log::warn("RewindManager", + "Client has received state in the future: at %f state %f", + World::getWorld()->getTime(), ri->getTime()); + ri->rewind(); + } } m_is_rewinding = false; } // playEventsTill From 9b38e401f477d342184cace92374349b72e7e2cf Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 23 Jan 2017 08:23:54 +1100 Subject: [PATCH 023/413] Store special time events for clients (otherwise incorrect or even way too large dt is used in rewind). --- src/karts/controller/player_controller.cpp | 2 ++ src/network/rewind_manager.cpp | 13 +++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) mode change 100644 => 100755 src/network/rewind_manager.cpp diff --git a/src/karts/controller/player_controller.cpp b/src/karts/controller/player_controller.cpp index 75ce0babe..62297c5ef 100644 --- a/src/karts/controller/player_controller.cpp +++ b/src/karts/controller/player_controller.cpp @@ -195,6 +195,8 @@ void PlayerController::action(PlayerAction action, int value) void PlayerController::actionFromNetwork(PlayerAction p_action, int value, int value_l, int value_r) { + if (m_steer_val_l != value_l || m_steer_val_r != value_r) + printf(""); m_steer_val_l = value_l; m_steer_val_r = value_r; action(p_action, value); diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp old mode 100644 new mode 100755 index e11eceef4..6ccdfae1c --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -95,7 +95,7 @@ void RewindManager::reset() m_rewind_queue.reset(); } // reset -// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- /** Adds an event to the rewind data. The data to be stored must be allocated * and not freed by the caller! * \param time Time at which the event was recorded. @@ -162,11 +162,13 @@ void RewindManager::saveStates() if(!m_enable_rewind_manager || m_all_rewinder.size()==0 || m_is_rewinding ) return; - if (NetworkConfig::get()->isClient()) - return; float time = World::getWorld()->getTime(); - if(time - m_last_saved_state < m_state_frequency) + + // Client doesn't save state (atm), so we need to store + // time infos to get the correct dt when rewinding + if(time - m_last_saved_state < m_state_frequency || + NetworkConfig::get()->isClient()) { // Avoid saving a time event for the same time, which happens // with t=0. @@ -176,6 +178,8 @@ void RewindManager::saveStates() // which increases replay precision (same time step size) m_rewind_queue.addTimeEvent(getCurrentTime()); } + if (NetworkConfig::get()->isClient()) + m_last_saved_state = time; return; } @@ -245,6 +249,7 @@ void RewindManager::playEventsTill(float time) return; } ++m_rewind_queue; + if(ri->isEvent()) ri->rewind(); else if (ri->isState()) From 241d31d8f98ba077b2c185a214cd3d59b52c2b06 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 24 Jan 2017 08:05:46 +1100 Subject: [PATCH 024/413] Added support for slowing down time on a client. This is used by a server to reduce number of rewinds. --- src/modes/world_status.cpp | 54 +++++++++++++++++++++++-- src/modes/world_status.hpp | 23 ++++++++--- src/network/protocols/game_protocol.cpp | 49 +++++++++++++++++++++- src/network/protocols/game_protocol.hpp | 13 +++++- 4 files changed, 127 insertions(+), 12 deletions(-) diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index 7fbde45e7..e6869385d 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -30,6 +30,7 @@ #include "network/network_config.hpp" #include "network/protocols/client_lobby.hpp" #include "network/protocols/server_lobby.hpp" +#include "network/rewind_manager.hpp" #include "network/race_event_manager.hpp" #include "tracks/track.hpp" @@ -61,6 +62,7 @@ WorldStatus::WorldStatus() void WorldStatus::reset() { m_time = 0.0f; + m_adjust_time_by = 0.0f; m_auxiliary_timer = 0.0f; m_count_up_timer = 0.0f; @@ -391,14 +393,23 @@ void WorldStatus::updateTime(const float dt) } IrrlichtDevice *device = irr_driver->getDevice(); + // If this is a client, the server might request an + // adjustment of this client's world clock (to reduce + // number of rewinds). + float actual_dt; + if (NetworkConfig::get()->isClient() && + !RewindManager::get()->isRewinding()) + actual_dt = adjustDT(dt); + else + actual_dt = dt; switch (m_clock_mode) { case CLOCK_CHRONO: if (!device->getTimer()->isStopped()) { - m_time += dt; - m_count_up_timer += dt; + m_time += actual_dt; + m_count_up_timer += actual_dt; } break; case CLOCK_COUNTDOWN: @@ -412,8 +423,8 @@ void WorldStatus::updateTime(const float dt) if (!device->getTimer()->isStopped()) { - m_time -= dt; - m_count_up_timer += dt; + m_time -= actual_dt; + m_count_up_timer += actual_dt; } if(m_time <= 0.0) @@ -427,6 +438,41 @@ void WorldStatus::updateTime(const float dt) } // switch m_phase } // update +//----------------------------------------------------------------------------- +/** If the server requests that a client's time should be adjusted, + * smoothly change the clock speed of this client to go a bit faster + * or slower till the overall adjustment was reached. + * \param dt Original time step size. + */ +float WorldStatus::adjustDT(float dt) +{ + // If request, adjust world time to go ahead (adjust>0) or + // slow down (<0). This is done in 5% of dt steps so that the + // user will not notice this. + const float FRACTION = 0.05f; // fraction of dt to be adjusted + float time_adjust; + if (m_adjust_time_by >= 0) // make it run faster + { + time_adjust = dt * FRACTION; + if (time_adjust > m_adjust_time_by) time_adjust = m_adjust_time_by; + if (m_adjust_time_by > 0) + Log::verbose("info", "At %f %f adjusting time by %f dt %f to dt %f for %f", + World::getWorld()->getTime(), StkTime::getRealTime(), + time_adjust, dt, dt + time_adjust, m_adjust_time_by); + } + else // m_adjust_time negative, i.e. will go slower + { + time_adjust = -dt * FRACTION; + if (time_adjust < m_adjust_time_by) time_adjust = m_adjust_time_by; + Log::verbose("info", "At %f %f adjusting time by %f dt %f to dt %f for %f", + World::getWorld()->getTime(), StkTime::getRealTime(), + time_adjust, dt, dt + time_adjust, m_adjust_time_by); + } + m_adjust_time_by -= time_adjust; + dt += time_adjust; + return dt; +} // adjustDT + //----------------------------------------------------------------------------- /** Called on the client when it receives a notification from the server that * all clients (and server) are ready to start the race. The server will diff --git a/src/modes/world_status.hpp b/src/modes/world_status.hpp index f98fa3a6c..9e0d18d86 100644 --- a/src/modes/world_status.hpp +++ b/src/modes/world_status.hpp @@ -103,6 +103,14 @@ private: /** The third sound to be played in ready, set, go. */ SFXBase *m_start_sound; + /** In networked game the world clock might be adjusted (without the + * player noticing), e.g. if a client causes rewinds in the server, + * that client needs to speed up to be further ahead of the server + * and so reduce the number of rollbacks. This is the amount of time + * by which the client's clock needs to be adjusted (positive or + * negative). */ + float m_adjust_time_by; + /** The clock mode: normal counting forwards, or countdown */ ClockType m_clock_mode; protected: @@ -126,13 +134,15 @@ private: float m_count_up_timer; bool m_engines_started; - void startEngines(); /** In networked game a client must wait for the server to start 'ready - * set go' to make sure all client are actually ready to start the game. - * A server on the other hand will run behind all clients, so it will - * wait for all clients to indicate that they have started the race. */ + * set go' to make sure all client are actually ready to start the game. + * A server on the other hand will run behind all clients, so it will + * wait for all clients to indicate that they have started the race. */ bool m_server_is_ready; + float adjustDT(float dt); + void startEngines(); + public: WorldStatus(); virtual ~WorldStatus(); @@ -196,7 +206,10 @@ public: float getTimeSinceStart() const { return m_count_up_timer; } // ------------------------------------------------------------------------ void setReadyToRace() { m_server_is_ready = true; } - + // ------------------------------------------------------------------------ + /** Sets a time by which the clock should be adjusted. Used by networking + * if too many rewinds are detected. */ + void setAdjustTime(float t) { m_adjust_time_by = t; } }; // WorldStatus diff --git a/src/network/protocols/game_protocol.cpp b/src/network/protocols/game_protocol.cpp index 554602242..3b3373fb1 100644 --- a/src/network/protocols/game_protocol.cpp +++ b/src/network/protocols/game_protocol.cpp @@ -69,7 +69,7 @@ void GameProtocol::update(float dt) .addUInt32(a.m_value_l).addUInt32(a.m_value_r); } // for a in m_all_actions - // FIXME: for now send reliable + // FIXME: for now send reliable sendToServer(m_data_to_send, /*reliable*/ true); m_all_actions.clear(); } // update @@ -87,6 +87,7 @@ bool GameProtocol::notifyEventAsynchronous(Event* event) { case GP_CONTROLLER_ACTION: handleControllerAction(event); break; case GP_STATE: handleState(event); break; + case GP_ADJUST_TIME: handleAdjustTime(event); break; default: Log::error("GameProtocol", "Received unknown message type %d - ignored.", message_type); break; @@ -130,6 +131,9 @@ void GameProtocol::controllerAction(int kart_id, PlayerAction action, World::getWorld()->getTime(), action, value); } // controllerAction + +#include "utils/time.hpp" + // ---------------------------------------------------------------------------- /** Called when a controller event is receiver - either on the server from * a client, or on a client from the server. It sorts the event into the @@ -140,9 +144,16 @@ void GameProtocol::handleControllerAction(Event *event) { NetworkString &data = event->data(); uint8_t count = data.getUInt8(); + bool will_trigger_rewind = false; + float rewind_delta = 0.0f; for (unsigned int i = 0; i < count; i++) { float time = data.getFloat(); + if (time < World::getWorld()->getTime()) + { + will_trigger_rewind = true; + rewind_delta = time - World::getWorld()->getTime(); + } uint8_t kart_id = data.getUInt8(); assert(kart_id < World::getWorld()->getNumKarts()); @@ -168,10 +179,46 @@ void GameProtocol::handleControllerAction(Event *event) // Send update to all clients except the original sender. STKHost::get()->sendPacketExcept(event->getPeer(), &data, false); + if (will_trigger_rewind) + { + Log::info("GameProtocol", "At %f %f requesting time adjust of %f for host %d", + World::getWorld()->getTime(), StkTime::getRealTime(), + rewind_delta, event->getPeer()->getHostId()); + // This message from a client triggered a rewind in the server. + // To avoid this, signal to the client that it should slow down. + adjustTimeForClient(event->getPeer(), rewind_delta); + } } // if server } // handleControllerAction +// ---------------------------------------------------------------------------- +/** The server might request that a client adjusts its world clock (in order to + * reduce rewinds). This function sends a a (unreliable) message to the + * client. + * \param peer The peer that triggered the rewind. + * \param t Time that the peer needs to slowdown (<0) or sped up(>0). + */ +void GameProtocol::adjustTimeForClient(STKPeer *peer, float t) +{ + assert(NetworkConfig::get()->isServer()); + NetworkString *ns = getNetworkString(5); + ns->addUInt8(GP_ADJUST_TIME).addFloat(t); + // This message can be send unreliable, it's not critical if it doesn't + // get delivered, the server will request again later anyway. + peer->sendPacket(ns, /*reliable*/false); + delete ns; +} // adjustTimeForClient + +// ---------------------------------------------------------------------------- +/** Called on a client when the server requests an adjustment of this client's + * world clock time (in order to reduce rewinds). + */ +void GameProtocol::handleAdjustTime(Event *event) +{ + float t = event->data().getFloat(); + World::getWorld()->setAdjustTime(t); +} // handleAdjustTime // ---------------------------------------------------------------------------- /** Called by the server before assembling a new message containing the full * state of the race to be sent to a client. diff --git a/src/network/protocols/game_protocol.hpp b/src/network/protocols/game_protocol.hpp index 3f6d63e87..5f211764f 100644 --- a/src/network/protocols/game_protocol.hpp +++ b/src/network/protocols/game_protocol.hpp @@ -30,6 +30,7 @@ class BareNetworkString; class NetworkString; +class STKPeer; class GameProtocol : public Protocol , public EventRewinder @@ -39,12 +40,18 @@ private: /** The type of game events to be forwarded to the server. */ enum { GP_CONTROLLER_ACTION, - GP_STATE}; + GP_STATE, + GP_ADJUST_TIME + }; /** A network string that collects all information from the server to be sent * next. */ NetworkString *m_data_to_send; + /** The server might request that the world clock of a client is adjusted + * to reduce number of rollbacks. */ + std::vector m_adjust_time; + // Dummy data structure to save all kart actions. struct Action { @@ -61,6 +68,7 @@ private: void handleControllerAction(Event *event); void handleState(Event *event); + void handleAdjustTime(Event *event); public: GameProtocol(); virtual ~GameProtocol(); @@ -73,6 +81,7 @@ public: void startNewState(); void addState(BareNetworkString *buffer); void sendState(); + void adjustTimeForClient(STKPeer *peer, float t); virtual void undo(BareNetworkString *buffer) OVERRIDE; virtual void rewind(BareNetworkString *buffer) OVERRIDE; @@ -80,7 +89,7 @@ public: virtual void setup() OVERRIDE {}; // ------------------------------------------------------------------------ virtual void asynchronousUpdate() OVERRIDE {} - + // ------------------------------------------------------------------------ }; // class GameProtocol #endif // GAME_PROTOCOL_HPP From b8bf4a3c2ebfc67e5a355fa830aa9765549e1272 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 24 Jan 2017 08:35:15 +1100 Subject: [PATCH 025/413] Fixed documentation. --- src/network/protocols/server_lobby.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 01fa90b32..ea347d88c 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -245,7 +245,7 @@ void ServerLobby::update(float dt) // they have started the race/ break; case WAIT_FOR_RACE_STARTED: - // The function finishedLoadingWorldClient() will trigger the + // The function startedRaceOnClient() will trigger the // next state. break; case DELAY_SERVER: From 426cbaaff5ff47f39391be73e516da40d40b4a05 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 25 Jan 2017 14:29:48 +1100 Subject: [PATCH 026/413] Added '--auto-connect' command line option to automatically connect a client to the first LAN server it finds and start a race - strictly for debuggint ;) --- src/main.cpp | 6 ++++- src/network/network_config.cpp | 1 + src/network/network_config.hpp | 11 ++++++++ src/network/protocols/client_lobby.cpp | 12 +++++++-- .../dialogs/server_info_dialog.hpp | 2 +- src/states_screens/kart_selection.cpp | 2 +- src/states_screens/network_kart_selection.cpp | 27 ++++++++++++++----- src/states_screens/server_selection.cpp | 12 +++++++++ src/states_screens/tracks_screen.cpp | 8 ++++++ 9 files changed, 70 insertions(+), 11 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index f91ef9b1e..aebbb8042 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -583,6 +583,7 @@ void cmdLineHelp() " --port=n Port number to use.\n" " --my-address=1.1.1.1:1 Own IP address (can replace stun protocol)\n" " --disable-lan Disable LAN detection (connect using WAN).\n" + " --auto-connect Automatically connect to fist server and start race\n" " --max-players=n Maximum number of clients (server only).\n" " --no-console Does not write messages in the console but to\n" " stdout.log.\n" @@ -1030,7 +1031,10 @@ int handleCmdLine() { NetworkConfig::get()->setPassword(s); } - + if (CommandLine::has("--auto-connect")) + { + NetworkConfig::get()->setAutoConnect(true); + } if(CommandLine::has("--max-players", &n)) UserConfigParams::m_server_max_players=n; diff --git a/src/network/network_config.cpp b/src/network/network_config.cpp index efa329b48..fb2b2f8e1 100644 --- a/src/network/network_config.cpp +++ b/src/network/network_config.cpp @@ -36,6 +36,7 @@ bool NetworkConfig::m_disable_lan = false; NetworkConfig::NetworkConfig() { m_network_type = NETWORK_NONE; + m_auto_connect = false; m_is_server = false; m_is_public_server = false; m_max_players = 4; diff --git a/src/network/network_config.hpp b/src/network/network_config.hpp index b7ce8d726..44b406a1e 100644 --- a/src/network/network_config.hpp +++ b/src/network/network_config.hpp @@ -73,6 +73,10 @@ private: * with the stk server. */ bool m_is_registered; + /** True if a client should connect to the first server it finds and + * immediately start a race. */ + bool m_auto_connect; + /** If this is a server, the server name. */ irr::core::stringw m_server_name; @@ -207,6 +211,13 @@ public: m_my_address.unlock(); return a; } // getMyAddress + // -------------------------------------------------------------------- + /** Sets if a client should immediately connect to the first server. */ + void setAutoConnect(bool b) { m_auto_connect = b; } + // -------------------------------------------------------------------- + /** Returns if an immediate connection to the first server was + * requested. */ + bool isAutoConnect() const { return m_auto_connect; } }; // class NetworkConfig diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index 16ffc9ab5..4c56e022c 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -507,6 +507,15 @@ void ClientLobby::connectionAccepted(Event* event) NetworkingLobby::getInstance()->addPlayer(profile); m_server = event->getPeer(); m_state = CONNECTED; + if (NetworkConfig::get()->isAutoConnect()) + { + // Send a message to the server to start + NetworkString start(PROTOCOL_LOBBY_ROOM); + start.setSynchronous(true); + start.addUInt8(LobbyProtocol::LE_REQUEST_BEGIN); + STKHost::get()->sendToServer(&start, true); + } + } // connectionAccepted //----------------------------------------------------------------------------- @@ -609,8 +618,7 @@ void ClientLobby::kartSelectionUpdate(Event* event) //----------------------------------------------------------------------------- -/*! \brief Called when the server broadcasts to start the race. -race needs to be started. +/*! \brief Called when the server broadcasts to start the race to all clients. * \param event : Event providing the information (no additional information * in this case). */ diff --git a/src/states_screens/dialogs/server_info_dialog.hpp b/src/states_screens/dialogs/server_info_dialog.hpp index fb55896ab..1740a4664 100644 --- a/src/states_screens/dialogs/server_info_dialog.hpp +++ b/src/states_screens/dialogs/server_info_dialog.hpp @@ -54,7 +54,6 @@ private: /** The cancel button. */ GUIEngine::IconButtonWidget *m_cancel_widget; - void requestJoin(); public: ServerInfoDialog(uint32_t server_id, uint32_t host_id, bool just_created = false); @@ -65,6 +64,7 @@ public: virtual bool onEscapePressed(); virtual void onUpdate(float dt); + void requestJoin(); }; // class ServerInfoDialog #endif diff --git a/src/states_screens/kart_selection.cpp b/src/states_screens/kart_selection.cpp index 510022e23..06758229e 100644 --- a/src/states_screens/kart_selection.cpp +++ b/src/states_screens/kart_selection.cpp @@ -425,7 +425,7 @@ void KartSelectionScreen::tearDown() void KartSelectionScreen::unloaded() { - // these pointer is no more valid (have been deleted along other widgets) + // This pointer is no longer valid (has been deleted along other widgets) m_dispatcher = NULL; } diff --git a/src/states_screens/network_kart_selection.cpp b/src/states_screens/network_kart_selection.cpp index 36735d127..22b32d797 100644 --- a/src/states_screens/network_kart_selection.cpp +++ b/src/states_screens/network_kart_selection.cpp @@ -30,6 +30,7 @@ #include "network/protocol_manager.hpp" #include "network/protocols/client_lobby.hpp" #include "network/stk_host.hpp" +#include "states_screens/race_setup_screen.hpp" #include "states_screens/server_selection.hpp" #include "states_screens/state_manager.hpp" #include "states_screens/tracks_screen.hpp" @@ -106,6 +107,15 @@ void NetworkKartSelectionScreen::init() m_kart_widgets[n].move( fullarea->m_x + splitWidth*n, fullarea->m_y, splitWidth, fullarea->m_h); } + // In case of auto-connect, select default kart and go to track selection. + if (NetworkConfig::get()->isAutoConnect()) + { + DynamicRibbonWidget* w = getWidget("karts"); + assert(w != NULL); + w->setSelection(UserConfigParams::m_default_kart, /*player id*/0, /*focus*/true); + playerConfirm(0); + RaceSetupScreen::getInstance()->push(); + } } // init @@ -145,7 +155,7 @@ void NetworkKartSelectionScreen::playerConfirm(const int playerID) for(unsigned int i=0; irequestKartSelection(players[i]->getGlobalPlayerId(), - selection); + selection ); } } } // playerConfirm @@ -168,11 +178,16 @@ void NetworkKartSelectionScreen::playerSelected(uint8_t player_id, if(widget_id==-1) return; - KartSelectionScreen::updateKartWidgetModel(widget_id, kart_name, - irr::core::stringw(kart_name.c_str())); - KartSelectionScreen::updateKartStats(widget_id, kart_name); - m_kart_widgets[widget_id].setKartInternalName(kart_name); - m_kart_widgets[widget_id].markAsReady(); // mark player ready + // In case of auto-connect the screen is already replaced, so + // m_kart_widget is empty. + if (! STKHost::get()->isAuthorisedToControl()) + { + KartSelectionScreen::updateKartWidgetModel(widget_id, kart_name, + irr::core::stringw(kart_name.c_str())); + KartSelectionScreen::updateKartStats(widget_id, kart_name); + m_kart_widgets[widget_id].setKartInternalName(kart_name); + m_kart_widgets[widget_id].markAsReady(); // mark player ready + } // If this is the authorised client, send the currently set race config // to the server. diff --git a/src/states_screens/server_selection.cpp b/src/states_screens/server_selection.cpp index 04101bf8e..ce2be0fbe 100644 --- a/src/states_screens/server_selection.cpp +++ b/src/states_screens/server_selection.cpp @@ -19,6 +19,7 @@ #include "audio/sfx_manager.hpp" #include "guiengine/modaldialog.hpp" +#include "network/network_config.hpp" #include "network/servers_manager.hpp" #include "online/xml_request.hpp" #include "states_screens/dialogs/message_dialog.hpp" @@ -208,6 +209,7 @@ void ServerSelection::eventCallback( GUIEngine::Widget* widget, * if so, update the list of servers. */ void ServerSelection::onUpdate(float dt) + { if (!m_refresh_request) return; @@ -250,4 +252,14 @@ void ServerSelection::onUpdate(float dt) if (selection != -1 && selection_str != "spacer" && selection_str != "loading") m_server_list_widget->setSelectionID(selection); } + + // In case of auto-connect command line parameter, select the first server asap + if (NetworkConfig::get()->isAutoConnect() && + m_refresh_request == NULL && + m_server_list_widget->getItemCount() > 0) + { + ServerInfoDialog *sid = new ServerInfoDialog(/*server*/0, + /*host id*/0, false); + sid->requestJoin(); + } } // onUpdate diff --git a/src/states_screens/tracks_screen.cpp b/src/states_screens/tracks_screen.cpp index 00d84e528..f80cd8ac4 100644 --- a/src/states_screens/tracks_screen.cpp +++ b/src/states_screens/tracks_screen.cpp @@ -171,6 +171,14 @@ void TracksScreen::init() tracks_widget->setSelection(0, PLAYER_ID_GAME_MASTER, true); } irr_driver->unsetTextureErrorMessage(); + if (NetworkConfig::get()->isAutoConnect()) + { + DynamicRibbonWidget* tw = getWidget("tracks"); + tw->setSelection(UserConfigParams::m_last_track, 0, + /*focus*/true); + eventCallback(tw, "tracks", + /*player id*/0); + } } // init // ----------------------------------------------------------------------------- From 0e6daf9319f74c5ee476037cf3d1a4a961cef1a2 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 25 Jan 2017 18:19:37 +1100 Subject: [PATCH 027/413] Fixed compiler warning. --- src/karts/kart_model.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index 92906ddef..33f53cbf1 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -530,7 +530,7 @@ scene::ISceneNode* KartModel::attachModel(bool animated_models, bool always_anim } } - for (int i = 0; i < m_headlight_objects.size(); i++) + for (unsigned int i = 0; i < m_headlight_objects.size(); i++) { HeadlightObject& obj = m_headlight_objects[i]; @@ -642,7 +642,7 @@ bool KartModel::loadModels(const KartProperties &kart_properties) kart_max.max(obj_max); } - for (int i = 0; i < m_headlight_objects.size(); i++) + for (unsigned int i = 0; i < m_headlight_objects.size(); i++) { HeadlightObject& obj = m_headlight_objects[i]; std::string full_name = kart_properties.getKartDir() + obj.getFilename(); From ecc4e30cbe969ee5260ece2c1d5cc998f90ef010 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 25 Jan 2017 21:51:26 +1100 Subject: [PATCH 028/413] Fixed compiler warning. --- src/states_screens/race_gui_overworld.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/states_screens/race_gui_overworld.cpp b/src/states_screens/race_gui_overworld.cpp index 5ffb1f81f..73b3e1261 100644 --- a/src/states_screens/race_gui_overworld.cpp +++ b/src/states_screens/race_gui_overworld.cpp @@ -119,7 +119,7 @@ RaceGUIOverworld::RaceGUIOverworld() { m_map_left = (int)((irr_driver->getActualScreenSize().Width - m_map_width) * 0.9f); - m_map_bottom = m_map_height + 10 * scaling; + m_map_bottom = m_map_height + int(10 * scaling); } m_speed_meter_icon = material_manager->getMaterial("speedback.png"); From 7ad0a0cf6930bba09cabd83d7bfaa3d402ac16fd Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 25 Jan 2017 21:55:16 +1100 Subject: [PATCH 029/413] Fixed coding style issues (same should be done with speedweight objects). --- src/karts/kart_model.cpp | 46 +++++++++++++++++-------------- src/karts/kart_model.hpp | 59 ++++++++++++++++++++++++++++++---------- 2 files changed, 70 insertions(+), 35 deletions(-) diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index 33f53cbf1..ebd1fb17c 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -273,17 +273,17 @@ KartModel::~KartModel() for (size_t i = 0; i < m_headlight_objects.size(); i++) { HeadlightObject& obj = m_headlight_objects[i]; - obj.m_node = NULL; - if (obj.m_node) + obj.setNode(NULL); + if (obj.getNode()) { - // Master KartModels should never have a speed weighted object attached. + // Master KartModels should never have a headlight attached. assert(!m_is_master); - obj.m_node->drop(); + obj.getNode()->drop(); } - if (m_is_master && obj.m_model) + if (m_is_master && obj.getModel()) { - irr_driver->dropAllTextures(obj.m_model); - irr_driver->removeMeshFromCache(obj.m_model); + irr_driver->dropAllTextures(obj.getModel()); + irr_driver->removeMeshFromCache(obj.getModel()); } } @@ -368,8 +368,8 @@ KartModel* KartModel::makeCopy(KartRenderType krt) km->m_headlight_objects.resize(m_headlight_objects.size()); for (size_t i = 0; im_headlight_objects[i] = m_headlight_objects[i]; } @@ -448,8 +448,8 @@ scene::ISceneNode* KartModel::attachModel(bool animated_models, bool always_anim for (size_t i = 0; isetParent(lod_node); + if (!m_headlight_objects[i].getNode()) continue; + m_headlight_objects[i].getNode()->setParent(lod_node); } #ifndef SERVER_ONLY @@ -534,16 +534,19 @@ scene::ISceneNode* KartModel::attachModel(bool animated_models, bool always_anim { HeadlightObject& obj = m_headlight_objects[i]; - obj.m_node = NULL; - if (obj.m_model) + obj.setNode(NULL); + if (obj.getModel()) { - obj.m_node = irr_driver->addMesh(obj.m_model, "kart_headlight", node, getRenderInfo()); - obj.m_node->grab(); - obj.m_node->setPosition(obj.getPosition()); + scene::ISceneNode *new_node = + irr_driver->addMesh(obj.getModel(), "kart_headlight", + node, getRenderInfo() ); + new_node->grab(); + obj.setNode(new_node); + Track* track = Track::getCurrentTrack(); if (track == NULL || track->getIsDuringDay()) - obj.m_node->setVisible(false); + obj.getNode()->setVisible(false); } } @@ -646,8 +649,8 @@ bool KartModel::loadModels(const KartProperties &kart_properties) { HeadlightObject& obj = m_headlight_objects[i]; std::string full_name = kart_properties.getKartDir() + obj.getFilename(); - obj.m_model = irr_driver->getMesh(full_name); - irr_driver->grabAllTextures(obj.m_model); + obj.setModel(irr_driver->getMesh(full_name)); + irr_driver->grabAllTextures(obj.getModel()); } Vec3 size = kart_max-kart_min; @@ -722,7 +725,8 @@ void KartModel::loadNitroEmitterInfo(const XMLNode &node, // ---------------------------------------------------------------------------- /** Loads a single speed weighted node. */ -void KartModel::loadSpeedWeightedInfo(const XMLNode* speed_weighted_node, const SpeedWeightedObject::Properties& fallback_properties) +void KartModel::loadSpeedWeightedInfo(const XMLNode* speed_weighted_node, + const SpeedWeightedObject::Properties& fallback_properties) { SpeedWeightedObject obj; obj.m_properties = fallback_properties; @@ -778,7 +782,7 @@ void KartModel::loadHeadlights(const XMLNode &node) Log::warn("KartModel", "Unknown XML node in the headlights section"); } } -} +} // loadHeadlights // ---------------------------------------------------------------------------- /** Resets the kart model. It stops animation from being played and resets diff --git a/src/karts/kart_model.hpp b/src/karts/kart_model.hpp index c748eb001..99c180279 100644 --- a/src/karts/kart_model.hpp +++ b/src/karts/kart_model.hpp @@ -84,33 +84,64 @@ struct SpeedWeightedObject }; typedef std::vector SpeedWeightedObjectList; +// ============================================================================ +/** A class to store the headlights of a kart. + */ class HeadlightObject { +private: + /** The filename of the headlight model. */ std::string m_filename; + /** The position relative to the parent kart scene node where the + * headlight mesh is attached to. */ core::vector3df m_position; + /** The mesh for the headlight. */ + scene::IMesh* m_model; + /** The scene node of the headlight. */ + scene::ISceneNode* m_node; public: - scene::IMesh* m_model; - scene::ISceneNode* m_node; - HeadlightObject() { - m_model = NULL; - m_node = NULL; - } - - HeadlightObject(const std::string& filename, core::vector3df pos) + m_model = NULL; + m_node = NULL; + m_filename = ""; + m_position.set(0, 0, 0); + } // HeadlightObject + // ------------------------------------------------------------------------ + HeadlightObject(const std::string& filename, core::vector3df &pos) { m_filename = filename; m_position = pos; - m_model = NULL; - m_node = NULL; - } - + m_model = NULL; + m_node = NULL; + } // HeadlightObjects + // ------------------------------------------------------------------------ const std::string& getFilename() const { return m_filename; } - const core::vector3df getPosition() const { return m_position; } -}; + // ------------------------------------------------------------------------ + /** Sets the mesh for this headlight object. */ + void setModel(scene::IMesh *mesh) { m_model = mesh; } + // ------------------------------------------------------------------------ + /** Sets the node of the headlight, and (if not NULL) also sets the + * position of this scene node to be the position of the headlight. */ + void setNode(scene::ISceneNode *node) + { + m_node = node; + if (m_node) m_node->setPosition(m_position); + } // setNode + // ------------------------------------------------------------------------ + const scene::ISceneNode *getNode() const { return m_node; } + // ------------------------------------------------------------------------ + scene::ISceneNode *getNode() { return m_node; } + // ------------------------------------------------------------------------ + const scene::IMesh *getModel() const { return m_model; } + // ------------------------------------------------------------------------ + scene::IMesh *getModel() { return m_model; } + // ------------------------------------------------------------------------ +}; // class HeadlightObject + +// ============================================================================ /** * \brief This class stores a 3D kart model. From cebf24f3af06a097aa2d11b328c456113a50e91b Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 27 Jan 2017 23:45:25 +1100 Subject: [PATCH 030/413] Don't use the time during a rewind to determine if a client needs to slow down. --- src/modes/world.cpp | 2 +- src/network/protocols/game_protocol.cpp | 16 ++++++++++------ src/network/rewind_manager.cpp | 7 +++++-- src/network/rewind_manager.hpp | 8 +++++++- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 3de7a9223..04a7cd750 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -964,7 +964,7 @@ void World::update(float dt) PROFILER_PUSH_CPU_MARKER("World::update (sub-updates)", 0x20, 0x7F, 0x00); WorldStatus::update(dt); - RewindManager::get()->saveStates(); + RewindManager::get()->update(); PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("World::update (Kart::upate)", 0x40, 0x7F, 0x00); diff --git a/src/network/protocols/game_protocol.cpp b/src/network/protocols/game_protocol.cpp index 3b3373fb1..686df27bd 100644 --- a/src/network/protocols/game_protocol.cpp +++ b/src/network/protocols/game_protocol.cpp @@ -131,9 +131,6 @@ void GameProtocol::controllerAction(int kart_id, PlayerAction action, World::getWorld()->getTime(), action, value); } // controllerAction - -#include "utils/time.hpp" - // ---------------------------------------------------------------------------- /** Called when a controller event is receiver - either on the server from * a client, or on a client from the server. It sorts the event into the @@ -149,10 +146,15 @@ void GameProtocol::handleControllerAction(Event *event) for (unsigned int i = 0; i < count; i++) { float time = data.getFloat(); - if (time < World::getWorld()->getTime()) + // Since this is running in a thread, it might be called during + // a rewind, i.e. with an incorrect world time. So the event + // time needs to be compared with the World time independent + // of any rewinding. + if (time < RewindManager::get()->getNotRewoundWorldTime() && + !will_trigger_rewind ) { will_trigger_rewind = true; - rewind_delta = time - World::getWorld()->getTime(); + rewind_delta = time - RewindManager::get()->getNotRewoundWorldTime(); } uint8_t kart_id = data.getUInt8(); assert(kart_id < World::getWorld()->getNumKarts()); @@ -181,8 +183,10 @@ void GameProtocol::handleControllerAction(Event *event) &data, false); if (will_trigger_rewind) { - Log::info("GameProtocol", "At %f %f requesting time adjust of %f for host %d", + Log::info("GameProtocol", + "At %f %f %f requesting time adjust of %f for host %d", World::getWorld()->getTime(), StkTime::getRealTime(), + RewindManager::get()->getNotRewoundWorldTime(), rewind_delta, event->getPeer()->getHostId()); // This message from a client triggered a rewind in the server. // To avoid this, signal to the client that it should slow down. diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 6ccdfae1c..154a40082 100755 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -73,6 +73,7 @@ RewindManager::~RewindManager() void RewindManager::reset() { m_is_rewinding = false; + m_not_rewound_time = 0; m_overall_state_size = 0; m_state_frequency = 0.1f; // save 10 states a second m_last_saved_state = -9999.9f; // forces initial state save @@ -157,13 +158,15 @@ void RewindManager::addNetworkState(int rewinder_index, * rewinder to do so. * \param dt Time step size. */ -void RewindManager::saveStates() +void RewindManager::update() { + if(!m_enable_rewind_manager || m_all_rewinder.size()==0 || m_is_rewinding ) return; float time = World::getWorld()->getTime(); + m_not_rewound_time = time; // Client doesn't save state (atm), so we need to store // time infos to get the correct dt when rewinding @@ -208,7 +211,7 @@ void RewindManager::saveStates() World::getWorld()->getTime(), m_overall_state_size); m_last_saved_state = time; -} // saveStates +} // update // ---------------------------------------------------------------------------- /** Replays all events from the last event played till the specified time. diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index c4439dafb..ab8e7360a 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -112,6 +112,10 @@ private: * events later. */ float m_current_time; + /** This stores the original World time during a rewind. It is used to + * detect if a client's local time need adjustment to reduce rewinds. */ + float m_not_rewound_time; + RewindManager(); ~RewindManager(); @@ -138,7 +142,7 @@ public: // Non-static functtion declarations: void reset(); - void saveStates(); + void update(); void rewindTo(float target_time); void playEventsTill(float time); void addEvent(EventRewinder *event_rewinder, BareNetworkString *buffer, @@ -176,6 +180,8 @@ public: // ------------------------------------------------------------------------ /** Returns true if currently a rewind is happening. */ bool isRewinding() const { return m_is_rewinding; } + // ------------------------------------------------------------------------ + float getNotRewoundWorldTime() const { return m_not_rewound_time; } }; // RewindManager From dfc5c1499ca6cbef5b929dc2e34ea06f9438686d Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 30 Jan 2017 08:52:41 +1100 Subject: [PATCH 031/413] Added separate log message for disconnecting clients. --- src/network/stk_host.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index eff17d7ec..e7ecd103a 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -559,6 +559,10 @@ void* STKHost::mainLoop(void* self) Log::debug("STKHost", "Addresses are : %lx, %lx", stk_event->getPeer(), peer); } // EVENT_TYPE_CONNECTED + else if (stk_event->getType() == EVENT_TYPE_DISCONNECTED) + { + Log::info("STKHost", "A client has just disconnected."); + } // EVENT_TYPE_CONNECTED else if (stk_event->getType() == EVENT_TYPE_MESSAGE) { Network::logPacket(stk_event->data(), true); From 6cc795fc60e631c304a1f3da02a249c862f6d89e Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 30 Jan 2017 08:54:23 +1100 Subject: [PATCH 032/413] Use a std::list instead of std::vector for the list of events. This allows not to lock the whole event queue while an event is being delivered, which allows more parallelism (especially the asynchronous eventss queue is not locked while e.g. the world is loaded, which can take several seconds). --- src/network/protocol_manager.cpp | 61 +++++++++++++++++++++----------- src/network/protocol_manager.hpp | 5 ++- 2 files changed, 44 insertions(+), 22 deletions(-) diff --git a/src/network/protocol_manager.cpp b/src/network/protocol_manager.cpp index e1b3a676c..8ca33d953 100644 --- a/src/network/protocol_manager.cpp +++ b/src/network/protocol_manager.cpp @@ -79,8 +79,10 @@ void ProtocolManager::abort() m_protocols.unlock(); m_events_to_process.lock(); - for (unsigned int i = 0; i < m_events_to_process.getData().size() ; i++) - delete m_events_to_process.getData()[i]; + EventList::iterator i; + for (EventList::iterator i =m_events_to_process.getData().begin(); + i!=m_events_to_process.getData().end(); ++i) + delete *i; m_events_to_process.getData().clear(); m_events_to_process.unlock(); @@ -325,22 +327,31 @@ void ProtocolManager::update(float dt) { // before updating, notify protocols that they have received events m_events_to_process.lock(); - int size = (int)m_events_to_process.getData().size(); - int offset = 0; - for (int i = 0; i < size; i++) + EventList::iterator i = m_events_to_process.getData().begin(); + + while (i != m_events_to_process.getData().end()) { - // Don't handle asynchronous events here. - if(!m_events_to_process.getData()[i+offset]->isSynchronous()) continue; - bool result = sendEvent(m_events_to_process.getData()[i+offset]); + // Don't handle asynchronous events here + if (!(*i)->isSynchronous()) + { + ++i; + continue; + } + m_events_to_process.unlock(); + bool result = sendEvent(*i); + m_events_to_process.lock(); if (result) { - m_events_to_process.getData() - .erase(m_events_to_process.getData().begin()+(i+offset), - m_events_to_process.getData().begin()+(i+offset+1)); - offset --; + i = m_events_to_process.getData().erase(i); + } + else + { + // This should only happen if the protocol has not been started + ++i; } } m_events_to_process.unlock(); + // now update all protocols m_protocols.lock(); for (unsigned int i = 0; i < m_protocols.getData().size(); i++) @@ -365,20 +376,28 @@ void ProtocolManager::asynchronousUpdate() // before updating, notice protocols that they have received information m_events_to_process.lock(); int size = (int)m_events_to_process.getData().size(); - int offset = 0; - for (int i = 0; i < size; i++) + EventList::iterator i = m_events_to_process.getData().begin(); + while (i != m_events_to_process.getData().end()) { // Don't handle synchronous events here. - if(m_events_to_process.getData()[i+offset]->isSynchronous()) continue; - bool result = sendEvent(m_events_to_process.getData()[i+offset]); + if ((*i)->isSynchronous()) + { + ++i; + continue; + } + m_events_to_process.unlock(); + bool result = sendEvent(*i); + m_events_to_process.lock(); if (result) { - m_events_to_process.getData() - .erase(m_events_to_process.getData().begin()+(i+offset), - m_events_to_process.getData().begin()+(i+offset+1)); - offset --; + i = m_events_to_process.getData().erase(i); } - } + else + { + // This should only happen if the protocol has not been started + ++i; + } + } // while i != m_events_to_process.end() m_events_to_process.unlock(); // now update all protocols that need to be updated in asynchronous mode diff --git a/src/network/protocol_manager.hpp b/src/network/protocol_manager.hpp index 82d722edf..d07c64a6a 100644 --- a/src/network/protocol_manager.hpp +++ b/src/network/protocol_manager.hpp @@ -30,6 +30,7 @@ #include "utils/synchronised.hpp" #include "utils/types.hpp" +#include #include class Event; @@ -101,9 +102,11 @@ private: * state and their unique id. */ Synchronised >m_protocols; + typedef std::list EventList; + /** Contains the network events to pass asynchronously to protocols * (i.e. from the separate ProtocolManager thread). */ - Synchronised > m_events_to_process; + Synchronised m_events_to_process; /** Contains the requests to start/pause etc... protocols. */ Synchronised< std::vector > m_requests; From a3b959cee942d0cda976ba0d9c3e6b4264975f6c Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 30 Jan 2017 08:58:00 +1100 Subject: [PATCH 033/413] Avoid that the server replays states saved in the previous frame. --- src/network/rewind_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 154a40082..1412601f0 100755 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -255,7 +255,7 @@ void RewindManager::playEventsTill(float time) if(ri->isEvent()) ri->rewind(); - else if (ri->isState()) + else if (ri->isState() && NetworkConfig::get()->isClient()) { Log::warn("RewindManager", "Client has received state in the future: at %f state %f", From d8706e2319a913b53e9896cbe132f314226d8782 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 30 Jan 2017 09:29:58 +1100 Subject: [PATCH 034/413] Added log message when server starts a race on clients. --- src/network/protocols/server_lobby.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index ea347d88c..270550388 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -349,6 +349,7 @@ void ServerLobby::registerServer() */ void ServerLobby::signalRaceStartToClients() { + Log::verbose("Server", "Signaling race start to clients"); const std::vector &peers = STKHost::get()->getPeers(); NetworkString *ns = getNetworkString(1); ns->addUInt8(LE_START_RACE); From 9df07ac0c22b30e2f29b6a310cefd9da63a0ac73 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 31 Jan 2017 18:25:30 +1100 Subject: [PATCH 035/413] Rearranged code somewhat to make it easier to read - only cosmetic changes. --- src/main_loop.cpp | 70 +++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 19809bf56..7b99180c4 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -85,6 +85,7 @@ float MainLoop::getLimitedDt() { m_curr_time = device->getTimer()->getRealTime(); dt = (float)(m_curr_time - m_prev_time); + if (dt <= 0) break; // should not really happen const World* const world = World::getWorld(); if (UserConfigParams::m_fps_debug && world) { @@ -110,29 +111,28 @@ float MainLoop::getLimitedDt() // client and server will not be in synch anymore if(!NetworkConfig::get()->isNetworking()) { - static const float max_elapsed_time = 3.0f*1.0f / 60.0f*1000.0f; /* time 3 internal substeps take */ - if (dt > max_elapsed_time) dt = max_elapsed_time; + /* time 3 internal substeps take */ + const float MAX_ELAPSED_TIME = 3.0f*1.0f / 60.0f*1000.0f; + if (dt > MAX_ELAPSED_TIME) dt = MAX_ELAPSED_TIME; } + if (!m_throttle_fps || ProfileWorld::isProfileMode()) break; - // Throttle fps if more than maximum, which can reduce - // the noise the fan on a graphics card makes. - // When in menus, reduce FPS much, it's not necessary to push to the maximum for plain menus - const int max_fps = (StateManager::get()->throttleFPS() ? 30 : UserConfigParams::m_max_fps); - if (dt > 0) - { - const int current_fps = (int)(1000.0f / dt); - if (m_throttle_fps && current_fps > max_fps && !ProfileWorld::isProfileMode()) - { - int wait_time = 1000 / max_fps - 1000 / current_fps; - if (wait_time < 1) wait_time = 1; + // Throttle fps if more than a certain maximum, which can reduce + // the noise the fan on a graphics card makes. When in menus, limit + // FPS even more + const int max_fps = StateManager::get()->throttleFPS() + ? 30 + : UserConfigParams::m_max_fps; - PROFILER_PUSH_CPU_MARKER("Throttle framerate", 0, 0, 0); - StkTime::sleep(wait_time); - PROFILER_POP_CPU_MARKER(); - } - else break; - } - else break; + const int current_fps = (int)(1000.0f / dt); + if (current_fps <= max_fps ) break; + + int wait_time = 1000 / max_fps - 1000 / current_fps; + if (wait_time < 1) wait_time = 1; + + PROFILER_PUSH_CPU_MARKER("Throttle framerate", 0, 0, 0); + StkTime::sleep(wait_time); + PROFILER_POP_CPU_MARKER(); } dt *= 0.001f; return dt; @@ -144,6 +144,8 @@ float MainLoop::getLimitedDt() */ void MainLoop::updateRace(float dt) { + if (!World::getWorld()) return; // No race on atm - i.e. we are in menu + // The race event manager will update world in case of an online race if ( RaceEventManager::getInstance() && RaceEventManager::getInstance()->isRunning() ) @@ -241,19 +243,18 @@ void MainLoop::run() PROFILER_POP_CPU_MARKER(); } - if (World::getWorld()) // race is active if world exists - { - PROFILER_PUSH_CPU_MARKER("Update race", 0, 255, 255); - updateRace(dt); - PROFILER_POP_CPU_MARKER(); - } // if race is active - + PROFILER_PUSH_CPU_MARKER("Update race", 0, 255, 255); + updateRace(dt); // Doesn't do anything if race is not active + PROFILER_POP_CPU_MARKER(); + // We need to check again because update_race may have requested // the main loop to abort; and it's not a good idea to continue // since the GUI engine is no more to be called then. - // Also only do music, input, and graphics update if graphics are + if (m_abort) break; + + // Only do music, input, and graphics update if graphics are // enabled. - if (!m_abort && !ProfileWorld::isNoGraphics()) + if (!ProfileWorld::isNoGraphics()) { PROFILER_PUSH_CPU_MARKER("Music/input/GUI", 0x7F, 0x00, 0x00); input_manager->update(dt); @@ -282,21 +283,18 @@ void MainLoop::run() } PROFILER_POP_CPU_MARKER(); - PROFILER_PUSH_CPU_MARKER("Database polling update", 0x00, 0x7F, 0x7F); - Online::RequestManager::get()->update(dt); - PROFILER_POP_CPU_MARKER(); } - else if (!m_abort && ProfileWorld::isNoGraphics()) + else { PROFILER_PUSH_CPU_MARKER("Protocol manager update", 0x7F, 0x00, 0x7F); if(NetworkConfig::get()->isNetworking()) ProtocolManager::getInstance()->update(dt); PROFILER_POP_CPU_MARKER(); - PROFILER_PUSH_CPU_MARKER("Database polling update", 0x00, 0x7F, 0x7F); - Online::RequestManager::get()->update(dt); - PROFILER_POP_CPU_MARKER(); } + PROFILER_PUSH_CPU_MARKER("Database polling update", 0x00, 0x7F, 0x7F); + Online::RequestManager::get()->update(dt); + PROFILER_POP_CPU_MARKER(); if (World::getWorld() ) { From ffff62ff52fdc87072caff993e71039b589dadae Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 1 Feb 2017 18:06:36 +1100 Subject: [PATCH 036/413] Removed debug code. --- src/karts/controller/player_controller.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/karts/controller/player_controller.cpp b/src/karts/controller/player_controller.cpp index 62297c5ef..75ce0babe 100644 --- a/src/karts/controller/player_controller.cpp +++ b/src/karts/controller/player_controller.cpp @@ -195,8 +195,6 @@ void PlayerController::action(PlayerAction action, int value) void PlayerController::actionFromNetwork(PlayerAction p_action, int value, int value_l, int value_r) { - if (m_steer_val_l != value_l || m_steer_val_r != value_r) - printf(""); m_steer_val_l = value_l; m_steer_val_r = value_r; action(p_action, value); From 69658c557b065d6fa53d631ace98d75f1eb8f8ac Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 9 Feb 2017 09:03:15 +1100 Subject: [PATCH 037/413] Refactored the event/state handling to make it somewhat faster by avoiding creating smaller time steps on the server when events from clients are received, and also made it easier to handle duplicated states (i.e. server rewinds and sends a second state for a certain time). Still work in progress though. --- sources.cmake | 2 +- src/modes/world.cpp | 3 +- src/network/protocol_manager.cpp | 14 +- src/network/race_event_manager.cpp | 9 +- src/network/rewind_info.cpp | 6 - src/network/rewind_info.hpp | 33 +--- src/network/rewind_manager.cpp | 180 +++++++++--------- src/network/rewind_manager.hpp | 26 +-- src/network/rewind_queue.cpp | 293 ++++++++++++++--------------- src/network/rewind_queue.hpp | 56 +++--- src/network/time_step_info.cpp | 96 ++++++++++ src/network/time_step_info.hpp | 92 +++++++++ 12 files changed, 465 insertions(+), 345 deletions(-) create mode 100644 src/network/time_step_info.cpp create mode 100644 src/network/time_step_info.hpp diff --git a/sources.cmake b/sources.cmake index 3b80aee29..45b0a1856 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,5 +1,5 @@ # Modify this file to change the last-modified date when you add/remove a file. -# This will then trigger a new cmake run automatically. +# This will then trigger a new cmake run automatically. file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp") file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp") file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 04a7cd750..9a5ec847d 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -964,7 +964,7 @@ void World::update(float dt) PROFILER_PUSH_CPU_MARKER("World::update (sub-updates)", 0x20, 0x7F, 0x00); WorldStatus::update(dt); - RewindManager::get()->update(); + RewindManager::get()->update(dt); PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("World::update (Kart::upate)", 0x40, 0x7F, 0x00); @@ -1025,7 +1025,6 @@ void World::update(float dt) void World::updateTime(const float dt) { WorldStatus::updateTime(dt); - RewindManager::get()->setCurrentTime(getTime()); } // updateTime // ---------------------------------------------------------------------------- diff --git a/src/network/protocol_manager.cpp b/src/network/protocol_manager.cpp index 8ca33d953..bafeb19e0 100644 --- a/src/network/protocol_manager.cpp +++ b/src/network/protocol_manager.cpp @@ -271,7 +271,8 @@ void ProtocolManager::terminateProtocol(Protocol *protocol) } // terminateProtocol // ---------------------------------------------------------------------------- -/** Sends the event to the corresponding protocol. +/** Sends the event to the corresponding protocol. Returns true if the event + * can be ignored, or false otherwise. */ bool ProtocolManager::sendEvent(Event* event) { @@ -304,13 +305,8 @@ bool ProtocolManager::sendEvent(Event* event) m_protocols.unlock(); - if (count>0 || StkTime::getTimeSinceEpoch()-event->getArrivalTime() - >= TIME_TO_KEEP_EVENTS ) - { - delete event; - return true; - } - return false; + return (count > 0 || StkTime::getTimeSinceEpoch() - event->getArrivalTime() + >= TIME_TO_KEEP_EVENTS ); } // sendEvent // ---------------------------------------------------------------------------- @@ -342,6 +338,7 @@ void ProtocolManager::update(float dt) m_events_to_process.lock(); if (result) { + delete *i; i = m_events_to_process.getData().erase(i); } else @@ -390,6 +387,7 @@ void ProtocolManager::asynchronousUpdate() m_events_to_process.lock(); if (result) { + delete *i; i = m_events_to_process.getData().erase(i); } else diff --git a/src/network/race_event_manager.cpp b/src/network/race_event_manager.cpp index 3d591cbae..fb0eea4e0 100644 --- a/src/network/race_event_manager.cpp +++ b/src/network/race_event_manager.cpp @@ -33,8 +33,13 @@ void RaceEventManager::update(float dt) // Replay all recorded events up to the current time (only if the // timer isn't stopped, otherwise a potential rewind will trigger // an infinite loop since world time does not increase) - if(World::getWorld()->getPhase()!=WorldStatus::IN_GAME_MENU_PHASE) - RewindManager::get()->playEventsTill(World::getWorld()->getTime()); + if (World::getWorld()->getPhase() != WorldStatus::IN_GAME_MENU_PHASE) + { + // This might adjust dt - if a new state is being played, the dt is + // determined from the last state till 'now' + RewindManager::get()->playEventsTill(World::getWorld()->getTime(), + &dt); + } World::getWorld()->updateWorld(dt); diff --git a/src/network/rewind_info.cpp b/src/network/rewind_info.cpp index e4da03ae9..34fcad498 100644 --- a/src/network/rewind_info.cpp +++ b/src/network/rewind_info.cpp @@ -30,12 +30,6 @@ RewindInfo::RewindInfo(float time, bool is_confirmed) m_is_confirmed = is_confirmed; } // RewindInfo -// ============================================================================ -RewindInfoTime::RewindInfoTime(float time) - : RewindInfo(time, /*is_confirmed*/true) -{ -} // RewindInfoTime - // ============================================================================ RewindInfoState::RewindInfoState(float time, Rewinder *rewinder, BareNetworkString *buffer, bool is_confirmed) diff --git a/src/network/rewind_info.hpp b/src/network/rewind_info.hpp index 0f2fbc565..408a02309 100644 --- a/src/network/rewind_info.hpp +++ b/src/network/rewind_info.hpp @@ -22,6 +22,7 @@ #include "network/event_rewinder.hpp" #include "network/network_string.hpp" #include "network/rewinder.hpp" +#include "utils/cpp2011.hpp" #include "utils/leak_check.hpp" #include "utils/ptr_vector.hpp" @@ -44,7 +45,7 @@ class RewindInfo private: LEAK_CHECK(); - /** Time when this state was taken. */ + /** Time when this RewindInfo was taken. */ float m_time; /** A confirmed event is one that was sent from the server. When @@ -65,22 +66,22 @@ public: // ------------------------------------------------------------------------ virtual ~RewindInfo() { } // ------------------------------------------------------------------------ - /** Returns the time at which this rewind state was saved. */ + /** Returns the time at which this RewindInfo was saved. */ float getTime() const { return m_time; } // ------------------------------------------------------------------------ /** Sets if this RewindInfo is confirmed or not. */ void setConfirmed(bool b) { m_is_confirmed = b; } // ------------------------------------------------------------------------ - /** Returns if this state is confirmed. */ + /** Returns if this RewindInfo is confirmed. */ bool isConfirmed() const { return m_is_confirmed; } // ------------------------------------------------------------------------ - /** If this rewind info is an event. Subclasses will overwrite this. */ + /** If this RewindInfo is an event. Subclasses will overwrite this. */ virtual bool isEvent() const { return false; } // ------------------------------------------------------------------------ - /** If this rewind info is time info. Subclasses will overwrite this. */ + /** If this RewindInfo is time info. Subclasses will overwrite this. */ virtual bool isTime() const { return false; } // ------------------------------------------------------------------------ - /** If this rewind info is an event. Subclasses will overwrite this. */ + /** If this RewindInfo is an event. Subclasses will overwrite this. */ virtual bool isState() const { return false; } // ------------------------------------------------------------------------ }; // RewindInfo @@ -117,26 +118,6 @@ public: BareNetworkString *getBuffer() const { return m_buffer; } }; // RewindInfoRewinder -// ============================================================================ -class RewindInfoTime : public RewindInfo -{ -private: - -public: - RewindInfoTime(float time); - virtual ~RewindInfoTime() {}; - - // ------------------------------------------------------------------------ - virtual bool isTime() const { return true; } - // ------------------------------------------------------------------------ - /** Called when going back in time to undo any rewind information. - * Does actually nothing. */ - virtual void undo() {} - // ------------------------------------------------------------------------ - /** Rewinds to this state. Nothing to be done for time info. */ - virtual void rewind() {} -}; // class RewindInfoTime - // ============================================================================ class RewindInfoState: public RewindInfoRewinder { diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 1412601f0..c7b14e3ec 100755 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -25,6 +25,7 @@ #include "network/protocols/game_protocol.hpp" #include "network/rewinder.hpp" #include "network/rewind_info.hpp" +#include "network/time_step_info.hpp" #include "physics/physics.hpp" #include "race/history.hpp" #include "utils/log.hpp" @@ -99,7 +100,8 @@ void RewindManager::reset() // ---------------------------------------------------------------------------- /** Adds an event to the rewind data. The data to be stored must be allocated * and not freed by the caller! - * \param time Time at which the event was recorded. + * \param time Time at which the event was recorded. If time is not specified + * (or set to -1), the current world time is used. * \param buffer Pointer to the event data. */ void RewindManager::addEvent(EventRewinder *event_rewinder, @@ -113,11 +115,11 @@ void RewindManager::addEvent(EventRewinder *event_rewinder, return; } - Log::verbose("RewindManager", "Time world %f self-event %f", - World::getWorld()->getTime(), getCurrentTime()); + Log::verbose("RewindManager", "Time world %f self-event", + World::getWorld()->getTime()); if (time < 0) - time = getCurrentTime(); - m_rewind_queue.addEvent(event_rewinder, buffer, confirmed, time); + time = World::getWorld()->getTime(); + m_rewind_queue.addLocalEvent(event_rewinder, buffer, confirmed, time); } // addEvent // ---------------------------------------------------------------------------- @@ -134,7 +136,7 @@ void RewindManager::addNetworkEvent(EventRewinder *event_rewinder, m_rewind_queue.addNetworkEvent(event_rewinder, buffer, time); Log::verbose("RewindManager", "Time world %f network-event %f", World::getWorld()->getTime(), time); -} // addEvent +} // addNetworkEvent // ---------------------------------------------------------------------------- /** Adds a state to the list of network rewind data. This function is @@ -144,23 +146,25 @@ void RewindManager::addNetworkEvent(EventRewinder *event_rewinder, * \param time Time at which the event was recorded. * \param buffer Pointer to the event data. */ -void RewindManager::addNetworkState(int rewinder_index, - BareNetworkString *buffer, float time) +void RewindManager::addNetworkState(int rewinder_index, BareNetworkString *buffer, + float time) { + assert(NetworkConfig::get()->isClient()); + // On a client dt from a state is never used, it maintains + // its own dt information (using TimeEvents). m_rewind_queue.addNetworkState(m_all_rewinder[rewinder_index], buffer, - time); + time, -99); Log::verbose("RewindManager", "Time world %f network-state %f", - World::getWorld()->getTime(), time); -} // addState + World::getWorld()->getTime(), time); +} // addNetworkState // ---------------------------------------------------------------------------- /** Determines if a new state snapshot should be taken, and if so calls all * rewinder to do so. * \param dt Time step size. */ -void RewindManager::update() +void RewindManager::update(float dt) { - if(!m_enable_rewind_manager || m_all_rewinder.size()==0 || m_is_rewinding ) return; @@ -168,44 +172,37 @@ void RewindManager::update() float time = World::getWorld()->getTime(); m_not_rewound_time = time; - // Client doesn't save state (atm), so we need to store - // time infos to get the correct dt when rewinding - if(time - m_last_saved_state < m_state_frequency || - NetworkConfig::get()->isClient()) + // Avoid duplicating time step during ready-set-go, all with time 0 + if (time > m_last_saved_state) + { + m_rewind_queue.addNewTimeStep(World::getWorld()->getTime(), dt); + } + + // Clients don't save state, so they just update m_last_saved_state + // (only for the above if test) and exit. + if ( NetworkConfig::get()->isClient() || + time - m_last_saved_state < m_state_frequency ) { - // Avoid saving a time event for the same time, which happens - // with t=0. - if (time > m_last_saved_state) - { - // No full state necessary, add a dummy entry for the time - // which increases replay precision (same time step size) - m_rewind_queue.addTimeEvent(getCurrentTime()); - } - if (NetworkConfig::get()->isClient()) - m_last_saved_state = time; return; } - // For now always create a snapshot. - if (NetworkConfig::get()->isServer()) - GameProtocol::getInstance()->startNewState(); - AllRewinder::const_iterator i; - for(i=m_all_rewinder.begin(); i!=m_all_rewinder.end(); ++i) + // Save state + GameProtocol::getInstance()->startNewState(); + AllRewinder::const_iterator rewinder; + for(rewinder=m_all_rewinder.begin(); rewinder!=m_all_rewinder.end(); ++rewinder) { - BareNetworkString *buffer = (*i)->saveState(); + BareNetworkString *buffer = (*rewinder)->saveState(); if(buffer && buffer->size()>=0) { m_overall_state_size += buffer->size(); - m_rewind_queue.addState(*i, buffer, /*confirmed*/true, - getCurrentTime()); - if(NetworkConfig::get()->isServer()) - GameProtocol::getInstance()->addState(buffer); + // Add to the previously created container + m_rewind_queue.addLocalState(*rewinder, buffer, /*confirmed*/true); + GameProtocol::getInstance()->addState(buffer); } // size >= 0 else delete buffer; // NULL or 0 byte buffer } - if (NetworkConfig::get()->isServer()) - GameProtocol::getInstance()->sendState(); + GameProtocol::getInstance()->sendState(); Log::verbose("RewindManager", "%f allocated %ld", World::getWorld()->getTime(), m_overall_state_size); @@ -216,8 +213,10 @@ void RewindManager::update() // ---------------------------------------------------------------------------- /** Replays all events from the last event played till the specified time. * \param time Up to (and inclusive) which time events will be replayed. + * \param dt Time step size. This might get adjusted if a new state has + * been received. */ -void RewindManager::playEventsTill(float time) +void RewindManager::playEventsTill(float time, float *dt) { // No events, nothing to do if (m_rewind_queue.isEmpty()) @@ -240,44 +239,46 @@ void RewindManager::playEventsTill(float time) rewindTo(rewind_time); } + // This is necessary to avoid that rewinding an event will store the + // event again as a seemingly new event. assert(!m_is_rewinding); m_is_rewinding = true; + // Now play all events between time and time + dt + // Note that we need to use time and not World::getTime(), since + // the world time was potentially set back during a rewind while(m_rewind_queue.hasMoreRewindInfo()) { - RewindInfo *ri = m_rewind_queue.getNext(); - if (ri->getTime() > time) - { - m_is_rewinding = false; - return; - } - ++m_rewind_queue; + TimeStepInfo *tsi = m_rewind_queue.getCurrent(); + // This timestep simulates from time to time+dt + if (tsi->getTime() > time + *dt) break; - if(ri->isEvent()) - ri->rewind(); - else if (ri->isState() && NetworkConfig::get()->isClient()) + ++m_rewind_queue; // Point to next rewind info + tsi->replayAllEvents(); + + if (tsi->hasConfirmedState() && NetworkConfig::get()->isClient()) { Log::warn("RewindManager", "Client has received state in the future: at %f state %f", - World::getWorld()->getTime(), ri->getTime()); - ri->rewind(); + World::getWorld()->getTime(), tsi->getTime()); } } m_is_rewinding = false; } // playEventsTill // ---------------------------------------------------------------------------- -/** Rewinds to the specified time. - * \param t Time to rewind to. +/** Rewinds to the specified time, then goes forward till the current + * World::getTime() is reached again: it will replay everything before + * World::getTime(), but not the events at World::getTime() (or later)/ + * \param rewind_time Time to rewind to. */ void RewindManager::rewindTo(float rewind_time) { assert(!m_is_rewinding); - Log::info("rewind", "Rewinding to %f at %f", rewind_time, - StkTime::getRealTime()); + Log::info("rewind", "Rewinding to %f at %f %f", rewind_time, + World::getWorld()->getTime(), StkTime::getRealTime()); history->doReplayHistory(History::HISTORY_NONE); - // Then undo the rewind infos going backwards in time // -------------------------------------------------- m_is_rewinding = true; @@ -289,57 +290,44 @@ void RewindManager::rewindTo(float rewind_time) float current_time = world->getTime(); // Get the (first) full state to which we have to rewind - RewindInfoState *state = - dynamic_cast(m_rewind_queue.getNext()); + TimeStepInfo *current = m_rewind_queue.getCurrent(); - // Store the time to which we have to replay to - float exact_rewind_time = state->getTime(); + // Store the time to which we have to replay to, + // which can be earlier than rewind_time + float exact_rewind_time = current->getTime(); // Now start the rewind with the full state: world->setTime(exact_rewind_time); - float local_physics_time = state->getLocalPhysicsTime(); + float local_physics_time = current->getLocalPhysicsTime(); Physics::getInstance()->getPhysicsWorld()->setLocalTime(local_physics_time); // Restore all states from the current time - the full state of a race // will be potentially stored in several state objects. State can be NULL // if the next event is not a state - while(state && state->getTime()==exact_rewind_time) + float dt = -1.0f; + + // Restore the state, then all events for the specified time + current->replayAllStates(); + + // Need to exit loop if in-game menu is open, since world clock + // will not be increased while the game is paused + if (World::getWorld()->getPhase() == WorldStatus::IN_GAME_MENU_PHASE) { - state->rewind(); - ++m_rewind_queue; - if(!m_rewind_queue.hasMoreRewindInfo()) break; - state = dynamic_cast(m_rewind_queue.getNext()); + m_is_rewinding = false; + return; } + current->replayAllStates(); + // Now go forward through the list of rewind infos: // ------------------------------------------------ - // Need to exit loop if in-game menu is open, since world clock - // will not be increased while the game is paused - while( world->getTime() < current_time && - World::getWorld()->getPhase()!=WorldStatus::IN_GAME_MENU_PHASE) + while (world->getTime() < current_time) { - // Now handle all states and events at the current time before - // updating the world: - while (m_rewind_queue.hasMoreRewindInfo()) - { - RewindInfo *ri = m_rewind_queue.getNext(); - if (ri->getTime() > world->getTime()) break; - - if (ri->isState()) - { - // TOOD: replace the old state with a new state. - // For now just set it to confirmed - ri->setConfirmed(true); - } - else if (ri->isEvent()) - { - ri->rewind(); - } - - ++m_rewind_queue; - } // while ri->getTime() <= world->getTime() - - float dt = m_rewind_queue.determineNextDT(current_time); + // Now handle all events(!) at the current time (i.e. between + // World::getTime() and World::getTime()+dt) before updating + // the world: + current->replayAllEvents(); + dt = current->getDT(); world->updateWorld(dt); #undef SHOW_ROLLBACK #ifdef SHOW_ROLLBACK @@ -347,7 +335,11 @@ void RewindManager::rewindTo(float rewind_time) #endif world->updateTime(dt); - } + ++m_rewind_queue; + if (!m_rewind_queue.hasMoreRewindInfo()) break; + current = m_rewind_queue.getCurrent(); + } // while (world->getTime() < current_time) + m_is_rewinding = false; Log::info("RewindManager", "Rewind from %f to %f finished at %f.", rewind_time, World::getWorld()->getTime(), diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index ab8e7360a..dabfdebfc 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -106,12 +106,6 @@ private: /** Time at which the last state was saved. */ float m_last_saved_state; - /** The current time to be used in all states/events. This is used to - * give all states and events during one frame the same time, even - * if e.g. states are saved before world time is increased, other - * events later. */ - float m_current_time; - /** This stores the original World time during a rewind. It is used to * detect if a client's local time need adjustment to reduce rewinds. */ float m_not_rewound_time; @@ -142,31 +136,15 @@ public: // Non-static functtion declarations: void reset(); - void update(); + void update(float dt); void rewindTo(float target_time); - void playEventsTill(float time); + void playEventsTill(float time, float *dt); void addEvent(EventRewinder *event_rewinder, BareNetworkString *buffer, bool confirmed, float time = -1.0f); void addNetworkEvent(EventRewinder *event_rewinder, BareNetworkString *buffer, float time); void addNetworkState(int rewinder_index, BareNetworkString *buffer, float time); - // ------------------------------------------------------------------------ - /** Sets the time that is to be used for all further states or events, - * and the time step size. This is necessary so that states/events before - * and after World::m_time is increased have the same time stamp. - * \param t Time. - * \param dt Time step size. - */ - void setCurrentTime(float t) - { - m_current_time = t; - } // setCurrentTime - - // ------------------------------------------------------------------------ - /** Returns the current time. */ - float getCurrentTime() const { return m_current_time; } - // ------------------------------------------------------------------------ /** Adds a Rewinder to the list of all rewinders. * \return true If rewinding is enabled, false otherwise. diff --git a/src/network/rewind_queue.cpp b/src/network/rewind_queue.cpp index 30f05c12c..0ad215f75 100755 --- a/src/network/rewind_queue.cpp +++ b/src/network/rewind_queue.cpp @@ -21,6 +21,7 @@ #include "modes/world.hpp" #include "network/rewind_info.hpp" #include "network/rewind_manager.hpp" +#include "network/time_step_info.hpp" #include @@ -38,13 +39,13 @@ RewindQueue::RewindQueue() RewindQueue::~RewindQueue() { // Destroying the - AllRewindInfo::const_iterator i; + AllTimeStepInfo::const_iterator i; - for(i=m_rewind_info.begin(); i!=m_rewind_info.end(); ++i) + for(i=m_time_step_info.begin(); i!=m_time_step_info.end(); ++i) { delete *i; } - m_rewind_info.clear(); + m_time_step_info.clear(); } // ~RewindQueue // ---------------------------------------------------------------------------- @@ -52,22 +53,21 @@ RewindQueue::~RewindQueue() */ void RewindQueue::reset() { -#ifdef REWIND_SEARCH_STATS - m_count_of_comparisons = 0; - m_count_of_searches = 0; -#endif - AllRewindInfo::const_iterator i; - for(i=m_rewind_info.begin(); i!=m_rewind_info.end(); i++) + for(AllTimeStepInfo::const_iterator i =m_time_step_info.begin(); + i!=m_time_step_info.end(); i++) { delete *i; } - m_rewind_info.clear(); - m_current = m_rewind_info.begin(); + + m_time_step_info.clear(); + m_current = m_time_step_info.begin(); m_network_events.lock(); - const AllRewindInfo &info = m_network_events.getData(); - for (i = info.begin(); i != info.end(); ++i) + + AllNetworkRewindInfo &info = m_network_events.getData(); + for (AllNetworkRewindInfo::const_iterator i = info.begin(); + i != info.end(); ++i) { delete *i; } @@ -75,38 +75,57 @@ void RewindQueue::reset() m_network_events.unlock(); } // reset +// ---------------------------------------------------------------------------- +/** Adds a new TimeStepInfo for the specified time. The TimeStepInfo acts + * as an container to store all states and events that happen at this time + * (or at least close to this time, since e.g. several events from clients + * happening at slightly different times will be all handled in the same + * timestep. + * \param time New time to add. + * \param dt Time step size that is going to be used for this time step. + */ +void RewindQueue::addNewTimeStep(float time, float dt) +{ + TimeStepInfo *tsi = new TimeStepInfo(time, dt); + + AllTimeStepInfo::iterator i = m_time_step_info.end(); + while (i != m_time_step_info.begin()) + { + --i; + if ((*i)->getTime() < time) + { + i++; + break; + } + }; + + m_time_step_info.insert(i, tsi); +} // addNewTimeStep + +// ---------------------------------------------------------------------------- +/** Finds the TimeStepInfo object to which an event at time t should be added. + * The TimeStepInfo object might not have the exacct same time, it can be + * the closest existing (or in future this function might even add a totally + * new TimeStepInfo object). + * \param Time at which the event that needs to be added hapened. + */ +TimeStepInfo *RewindQueue::findClosestTimeStepInfo(float t) +{ + AllTimeStepInfo::reverse_iterator i = m_time_step_info.rbegin(); + while (i != m_time_step_info.rend() && (*i)->getTime() > t) + i++; + return *i; +} // findClosestTimeStepInfo + // ---------------------------------------------------------------------------- /** A compare function used when sorting the event lists. It sorts events by * time. In case of equal times, it sorts states and events first (since the * state needs to be restored when replaying first before any other events). */ -bool RewindQueue::_RewindInfoCompare::operator()(const RewindInfo *ri1, - const RewindInfo *ri2) const +bool RewindQueue::_TimeStepInfoCompare::operator()(const TimeStepInfo * const ri1, + const TimeStepInfo * const ri2) const { - if (ri1->getTime() < ri2->getTime()) return true; - if (ri1->getTime() > ri2->getTime()) return false; - - // Now the times are equal. In this case make sure that states are sorted - // before times and they before events. If two identical types are found, - // sort them by pointer address (to guarantee that (a,b) and (b,a) give - // consistent results (otherwise e.g. VS in debug mode will detect - // inconsistencies and throw an exception). - if (ri1->isState()) - { - if (ri2->isState()) return ri1 < ri2; - return true; // state first, i.e smallest - } - if (ri1->isTime()) - { - if (ri2->isState()) return false; - if (ri2->isEvent()) return true; - return ri1 < ri2; - } - // Now ri1 must be event - if (ri2->isEvent()) return ri1 < ri2; - // ri2 is state or time which must come first - return false; - + return ri1->getTime() < ri2->getTime(); } // RewindQueue::operator() // ---------------------------------------------------------------------------- @@ -118,13 +137,8 @@ bool RewindQueue::_RewindInfoCompare::operator()(const RewindInfo *ri1, */ void RewindQueue::insertRewindInfo(RewindInfo *ri) { - AllRewindInfo::iterator i = - std::upper_bound(m_rewind_info.begin(), - m_rewind_info.end(), ri, - RewindQueue::m_rewind_info_compare); - AllRewindInfo::iterator new_pos = m_rewind_info.insert(i, ri); - if (m_current == m_rewind_info.end()) - m_current = new_pos; + TimeStepInfo *bucket = findClosestTimeStepInfo(ri->getTime()); + bucket->insert(ri); } // insertRewindInfo // ---------------------------------------------------------------------------- @@ -133,31 +147,38 @@ void RewindQueue::insertRewindInfo(RewindInfo *ri) * \param time Time at which the event was recorded. * \param buffer Pointer to the event data. */ -void RewindQueue::addEvent(EventRewinder *event_rewinder, - BareNetworkString *buffer, bool confirmed, - float time ) +void RewindQueue::addLocalEvent(EventRewinder *event_rewinder, + BareNetworkString *buffer, bool confirmed, + float time ) { RewindInfo *ri = new RewindInfoEvent(time, event_rewinder, buffer, confirmed); insertRewindInfo(ri); -} // addEvent +} // addLocalEvent // ---------------------------------------------------------------------------- -/** Adds a state from the local simulation. It is not thread-safe, so needs to - * be called from the main thread +/** Adds a state from the local simulation to the last created TimeStepInfo + * container with the current world time. It is not thread-safe, so needs + * to be called from the main thread. + * \param rewinder The rewinder object for this state. + * \param buffer The state information. + * \param confirmed If this state is confirmed to be correct (e.g. is + * being received from the servrer), or just a local state for + * faster rewinds. */ -void RewindQueue::addState(Rewinder *rewinder, BareNetworkString *buffer, - bool confirmed, float time) +void RewindQueue::addLocalState(Rewinder *rewinder, BareNetworkString *buffer, + bool confirmed) { - RewindInfo *ri = new RewindInfoState(time, rewinder, buffer, confirmed); + RewindInfo *ri = new RewindInfoState(World::getWorld()->getTime(), rewinder, + buffer, confirmed); assert(ri); insertRewindInfo(ri); -} // addState +} // addLocalState // ---------------------------------------------------------------------------- /** Adds an event to the list of network rewind data. This function is * threadsafe so can be called by the network thread. The data is synched - * to m_rewind_info by the main thread. The data to be stored must be + * to m_time_step_info by the main thread. The data to be stored must be * allocated and not freed by the caller! * \param time Time at which the event was recorded. * \param buffer Pointer to the event data. @@ -168,51 +189,29 @@ void RewindQueue::addNetworkEvent(EventRewinder *event_rewinder, RewindInfo *ri = new RewindInfoEvent(time, event_rewinder, buffer, /*confirmed*/true); - // Sort the incoming network events so that we can use list::merge - // to move the network events into the RewindInfo main list. m_network_events.lock(); - AllRewindInfo::iterator i = - std::upper_bound(m_network_events.getData().begin(), - m_network_events.getData().end() , ri, - RewindQueue::m_rewind_info_compare ); - m_network_events.getData().insert(i, ri); + m_network_events.getData().push_back(ri); m_network_events.unlock(); -} // addEvent +} // addNetworkEvent // ---------------------------------------------------------------------------- /** Adds a state to the list of network rewind data. This function is * threadsafe so can be called by the network thread. The data is synched - * to m_rewind_info by the main thread. The data to be stored must be + * to m_time_step_info by the main thread. The data to be stored must be * allocated and not freed by the caller! * \param time Time at which the event was recorded. * \param buffer Pointer to the event data. */ -void RewindQueue::addNetworkState(Rewinder *rewinder, - BareNetworkString *buffer, float time) +void RewindQueue::addNetworkState(Rewinder *rewinder, BareNetworkString *buffer, + float time, float dt) { RewindInfo *ri = new RewindInfoState(time, rewinder, buffer, /*confirmed*/true); - // Sort the incoming network events so that we can use list::merge - // to move the network events into the RewindInfo main list. m_network_events.lock(); - AllRewindInfo::iterator i = - std::upper_bound(m_network_events.getData().begin(), - m_network_events.getData().end(), ri, - RewindQueue::m_rewind_info_compare ); - m_network_events.getData().insert(i, ri); + m_network_events.getData().push_back(ri); m_network_events.unlock(); -} // addState - -// ---------------------------------------------------------------------------- -/** Adds a (dummy) time event to store the time. This enables rewinding to - * replay using the same time step size, minimising errors. - */ -void RewindQueue::addTimeEvent(float time) -{ - RewindInfo *ri = new RewindInfoTime(time); - insertRewindInfo(ri); -} // addTimeEvent +} // addNetworkState // ---------------------------------------------------------------------------- /** Merges thread-safe all data received from the network with the current @@ -227,55 +226,46 @@ void RewindQueue::mergeNetworkData(bool *needs_rewind, float *rewind_time) { *needs_rewind = false; m_network_events.lock(); - if (m_rewind_info.empty() && m_network_events.getData().empty()) + if(m_network_events.getData().empty()) { m_network_events.unlock(); return; } - /** First merge all newly received network events into the main + /** Merge all newly received network events into the main * event list */ - *rewind_time = -1.0f; + *rewind_time = 99999.9f; bool adjust_next = false; + float world_time = World::getWorld()->getTime(); - if (!m_network_events.getData().empty()) + AllNetworkRewindInfo::iterator i; + for (i = m_network_events.getData().begin(); + i != m_network_events.getData().end(); ++i) { // Check if a rewind is necessary - *rewind_time = m_network_events.getData().front()->getTime(); - if (*rewind_time < World::getWorld()->getTime()) + float t= (*i)->getTime(); + if (t < world_time) { *needs_rewind = true; + if (t < *rewind_time) *rewind_time = t; } - // It is possible that m_current points m_rewind_info.end() - // and that new elements are added to the end of the list. In this - // case m_rewind_info end would still point to end(), but should - // point to the newly added element. To achieve this, we first - // decrement m_current (note that it was tested previously that - // the list is not empty), merge in the new events, and then - // increment m_current again. If elements have been added to - // the end of the list, m_current will now corretly point to them. - - adjust_next = !m_rewind_info.empty(); - if (adjust_next) m_current--; - m_rewind_info.merge(m_network_events.getData(), m_rewind_info_compare); - - // RewindInfoCompare::RewindInfoCompare); - if (adjust_next) - m_current++; - else // rewind_info was empty, set pointer to first element - m_current = m_rewind_info.begin(); - } // if !m_network_events empty + TimeStepInfo *tsi = findClosestTimeStepInfo(t); + // FIXME We could try to find the closest TimeStepInfo, or even + // perhaps add a new TimeStepInfo if this new entry is + // 'close to the middle'. + tsi->insert(*i); + } // for i in m_network_events + m_network_events.getData().clear(); m_network_events.unlock(); - } // mergeNetworkData // ---------------------------------------------------------------------------- bool RewindQueue::isEmpty() const { - if (m_current != m_rewind_info.end()) + if (m_current != m_time_step_info.end()) return false; m_network_events.lock(); @@ -290,7 +280,7 @@ bool RewindQueue::isEmpty() const */ bool RewindQueue::hasMoreRewindInfo() const { - return m_current != m_rewind_info.end(); + return m_current != m_time_step_info.end(); } // hasMoreRewindInfo // ---------------------------------------------------------------------------- @@ -306,7 +296,7 @@ float RewindQueue::determineNextDT(float end_time) { // If there is a next state (which is known to have a different time) // use the time difference to determine the time step size. - if(m_current !=m_rewind_info.end()) + if(m_current !=m_time_step_info.end()) return (*m_current)->getTime() - World::getWorld()->getTime(); // Otherwise, i.e. we are rewinding the last state/event, take the @@ -318,44 +308,36 @@ float RewindQueue::determineNextDT(float end_time) // ---------------------------------------------------------------------------- /** Rewinds the rewind queue and undos all events/states stored. It stops - * when the first state is reached that was recorded before the undo_time. - * It sets the internal 'current' pointer to this state. + * when the first confirmed state is reached that was recorded before the + * undo_time and sets the internal 'current' pointer to this state. It is + * assumed that this function is called after a new TimeStepInfo instance + * was added (i.e. after RewindManager::update() was called), so the state + * m_current is pointing to is ignored. * \param undo_time To what at least events need to be undone. */ void RewindQueue::undoUntil(float undo_time) { - AllRewindInfo::iterator i = m_rewind_info.end(); - - while (i != m_rewind_info.begin()) + while (m_current != m_time_step_info.begin()) { - --i; - if ((*i)->isState() && - (*i)->getTime() <= undo_time) + --m_current; + // Undo all events and states from the current time + (*m_current)->undoAll(); + + if ((*m_current)->getTime() <= undo_time && + (*m_current)->hasConfirmedState()) { - // FIXME: we might have more than one state, so go back along - // states at the same time - m_current = i; return; } - // Undo the effect of the event. - (*i)->undo(); - - // Any states saved after the undo_time are invalid. For now mark - // them as being unconfirmed. - if ((*i)->isState() && - (*i)->getTime() > undo_time) - { - (*i)->setConfirmed(false); - } - } // while i!=m_rewind_info.begin() + } // while m_current!=m_time_step_info.begin() Log::error("RewindManager", "No state for rewind to %f", undo_time); - m_current = m_rewind_info.begin(); } // undoUntil + +#ifdef XXXXXXXXXXXXXXX // ---------------------------------------------------------------------------- /** Tests the sorting order of events with the same time stamp: states must * be first, then time info, then events. @@ -370,11 +352,11 @@ void RewindQueue::testingSortingOrderType(EventRewinder *rewinder, int types[3]) { BareNetworkString *s = new BareNetworkString(); s->addUInt8(1); - addState(NULL, s, true, 0.0f); + addState(NULL, s, true, 0.0f, 0.1f); break; } case 2: - addTimeEvent(0.0f); + addTimeEvent(0.0f, 0.1f); break; case 3: { @@ -394,7 +376,7 @@ void RewindQueue::testingSortingOrderType(EventRewinder *rewinder, int types[3]) // State, then time, then event assert(hasMoreRewindInfo()); assert(!isEmpty()); - RewindInfo *ri = getNext(); + RewindInfo *ri = getCurrent(); assert(ri->getTime() == 0.0f); assert(ri->isState()); RewindInfoState *ris = dynamic_cast(ri); @@ -402,14 +384,14 @@ void RewindQueue::testingSortingOrderType(EventRewinder *rewinder, int types[3]) assert(ris->getBuffer()->getUInt8() == 1); operator++(); - ri = getNext(); + ri = getCurrent(); assert(!isEmpty()); assert(hasMoreRewindInfo()); assert(ri->getTime() == 0.0f); assert(ri->isTime()); operator++(); - ri = getNext(); + ri = getCurrent(); assert(!isEmpty()); assert(hasMoreRewindInfo()); assert(ri->getTime() == 0.0f); @@ -439,7 +421,7 @@ void RewindQueue::testingSortingOrderTime(EventRewinder *rewinder, // Avoid warnings about 'no state found' when rewinding // and there is indeed no state at the given time. - addState(NULL, new BareNetworkString(), true, 0); + addState(NULL, new BareNetworkString(), true, 0, 0.1f); for (unsigned int i = 0; i<3; i++) { switch (types[i]) @@ -448,11 +430,11 @@ void RewindQueue::testingSortingOrderTime(EventRewinder *rewinder, { BareNetworkString *s = new BareNetworkString(); s->addUInt8(1); - addState(NULL, s, true, times[i]); + addState(NULL, s, true, times[i], 0.1f); break; } case 2: - addTimeEvent(times[i]); + addTimeEvent(times[i], 0.1f); break; case 3: { @@ -469,11 +451,11 @@ void RewindQueue::testingSortingOrderTime(EventRewinder *rewinder, // This should go back to the first state undoUntil(min_rewind_time); - RewindInfo *ri_prev = getNext(); + RewindInfo *ri_prev = getCurrent(); operator++(); while (hasMoreRewindInfo()) { - RewindInfo *ri = getNext(); + RewindInfo *ri = getCurrent(); assert(ri_prev->getTime() < ri->getTime()); ri_prev = ri; operator++(); @@ -544,12 +526,21 @@ void RewindQueue::unitTesting() // 1) Current pointer was not reset from end of list when an event // was added and the pointer was already at end of list RewindQueue b1; - b1.addState(NULL, new BareNetworkString(), true, 1.0f); + b1.addState(NULL, new BareNetworkString(), true, 1.0f, 0.1f); ++b1; // Should now point at end of list b1.hasMoreRewindInfo(); b1.addEvent(NULL, new BareNetworkString(), true, 2.0f); - RewindInfo *ri = b1.getNext(); + RewindInfo *ri = b1.getCurrent(); assert(ri->getTime() == 2.0f); assert(ri->isEvent()); -} // unitTesting \ No newline at end of file +} // unitTesting + + + +#endif + +void RewindQueue::unitTesting() +{ + +} \ No newline at end of file diff --git a/src/network/rewind_queue.hpp b/src/network/rewind_queue.hpp index a82b3b20a..645c527c1 100755 --- a/src/network/rewind_queue.hpp +++ b/src/network/rewind_queue.hpp @@ -27,8 +27,9 @@ #include #include -class RewindInfo; class EventRewinder; +class RewindInfo; +class TimeStepInfo; /** \ingroup network */ @@ -36,38 +37,31 @@ class EventRewinder; class RewindQueue { private: - - /** Pointer to all saved states. */ - typedef std::list AllRewindInfo; + /** Pointer to all saved */ + typedef std::list AllTimeStepInfo; /** The list of all events that are affected by a rewind. */ - AllRewindInfo m_rewind_info; + AllTimeStepInfo m_time_step_info; /** The list of all events received from the network. They are stored * in a separate thread (so this data structure is thread-save), and * merged into m_rewind_info from the main thread. This design (as * opposed to locking m_rewind_info) reduces the synchronisation * between main thread and network thread. */ - Synchronised m_network_events; + typedef std::vector AllNetworkRewindInfo; + Synchronised m_network_events; - /** Iterator to the next rewind info to be handled. */ - AllRewindInfo::iterator m_current; - -#define REWIND_SEARCH_STATS - -#ifdef REWIND_SEARCH_STATS - /** Gather some statistics about how many comparisons we do, - * to find out if it's worth doing a binary search.*/ - mutable int m_count_of_comparisons; - mutable int m_count_of_searches; -#endif + /** Iterator to the curren time step info to be handled. This should + * always be at the same time as World::getTime(). */ + AllTimeStepInfo::iterator m_current; + TimeStepInfo *findClosestTimeStepInfo(float t); void insertRewindInfo(RewindInfo *ri); - struct _RewindInfoCompare + struct _TimeStepInfoCompare { - bool operator()(const RewindInfo *ri1, const RewindInfo *ri2) const; - } m_rewind_info_compare; + bool operator()(const TimeStepInfo * const ri1, const TimeStepInfo * const ri2) const; + } m_time_step_info_compare; void testingSortingOrderType(EventRewinder *rewinder, int types[3]); void testingSortingOrderTime(EventRewinder *rewinder, int types[3], @@ -79,15 +73,15 @@ public: RewindQueue(); ~RewindQueue(); void reset(); - void addEvent(EventRewinder *event_rewinder, BareNetworkString *buffer, - bool confirmed, float time); - void addState(Rewinder *rewinder, BareNetworkString *buffer, - bool confirmed, float time); + void addNewTimeStep(float time, float dt); + void addLocalEvent(EventRewinder *event_rewinder, BareNetworkString *buffer, + bool confirmed, float time); + void addLocalState(Rewinder *rewinder, BareNetworkString *buffer, + bool confirmed); void addNetworkEvent(EventRewinder *event_rewinder, BareNetworkString *buffer, float time); void addNetworkState(Rewinder *rewinder, BareNetworkString *buffer, - float time); - void addTimeEvent(float time); + float time, float dt); void mergeNetworkData(bool *needs_rewind, float *rewind_time); bool isEmpty() const; bool hasMoreRewindInfo() const; @@ -95,19 +89,19 @@ public: float determineNextDT(float max_time); // ------------------------------------------------------------------------ - RewindQueue::AllRewindInfo::iterator& operator++() + RewindQueue::AllTimeStepInfo::iterator& operator++() { - assert(m_current != m_rewind_info.end()); + assert(m_current != m_time_step_info.end()); m_current++; return m_current; } // operator++ // ------------------------------------------------------------------------ - /** Returns the next event. Caller must make sure that there is at least + /** Returns the current RewindInfo. Caller must make sure that there is at least * one more RewindInfo (see hasMoreRewindInfo()). */ - RewindInfo *getNext() + TimeStepInfo *getCurrent() { - assert(m_current != m_rewind_info.end()); + assert(m_current != m_time_step_info.end()); return *m_current; } // getNext diff --git a/src/network/time_step_info.cpp b/src/network/time_step_info.cpp new file mode 100644 index 000000000..7a01a8b3d --- /dev/null +++ b/src/network/time_step_info.cpp @@ -0,0 +1,96 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/time_step_info.hpp" + +#include "network/rewind_info.hpp" +#include "physics/physics.hpp" + +/** Creates a new TimeStepInfo for a given time and given dt. + * \param time Time for this TimeStepInfo object. + * \param dt Time step size. + */ +TimeStepInfo::TimeStepInfo(float time, float dt) +{ + m_time = time; + m_dt = dt; + m_local_physics_time = Physics::getInstance()->getPhysicsWorld() + ->getLocalTime(); +} // StateEventList + +// -------------------------------------------------------------------- +/** Adds a state. State must be saved first, i.e. before events. The + * RewindManager guarantees this order + * \param ri The RewindInfo object for the state. + */ +void TimeStepInfo::insert(RewindInfo *ri) +{ + if (ri->isState()) + { + // States need to be inserted first. + // FIXME: handle duplicated states, e.g. server doing a rewind + // and sending another updated state + AllRewindInfo::iterator i = m_list_of_events.begin(); + while (i != m_list_of_events.end() && (*i)->isState()) + ++i; + m_list_of_events.insert(i, ri); + } + else + { + // Events at the same time are just added to the end + m_list_of_events.push_back(ri); + } +} // addStaet + +// -------------------------------------------------------------------- +/** Undos all events and states for this time step. + */ +void TimeStepInfo::undoAll() +{ + AllRewindInfo::reverse_iterator i; + for (i = m_list_of_events.rbegin(); i != m_list_of_events.rend(); i++) + { + (*i)->undo(); + } +} // undoAll +// -------------------------------------------------------------------- +/** Replays all events for this TimeStepInfo. + */ +void TimeStepInfo::replayAllEvents() +{ + AllRewindInfo::reverse_iterator i; + for (i = m_list_of_events.rbegin(); i != m_list_of_events.rend(); i++) + { + if ((*i)->isEvent()) + (*i)->rewind(); + } +} // replayAllEvents + +// -------------------------------------------------------------------- +/** Replays all state information for this TimeStepInfo. +*/ +void TimeStepInfo::replayAllStates() +{ + AllRewindInfo::reverse_iterator i; + for (i = m_list_of_events.rbegin(); i != m_list_of_events.rend(); i++) + { + if ((*i)->isState()) + (*i)->rewind(); + } +} // replayAllStates + diff --git a/src/network/time_step_info.hpp b/src/network/time_step_info.hpp new file mode 100644 index 000000000..9ab134da6 --- /dev/null +++ b/src/network/time_step_info.hpp @@ -0,0 +1,92 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2017 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_TIME_STEP_INFO_HPP +#define HEADER_TIME_STEP_INFO_HPP + +#include "network/rewind_info.hpp" +#include +#include + +class RewindInfo; +class EventRewinder; + +/** \ingroup network + */ + +class RewindInfo; +class RewindInfoEvent; +class RewindInfoState; + +/** This class stores information about each time step on a client or server. + * Firstly it stores the world time and time step size. In case of a rewind + * this allows the rewind to use the same time step size, which reduces + * jitter caused by different time step size. Secondly for each time it + * stores (if exist) all states, and all events. The TimeStepInfo acts as a + * container and will store all states and events that happened 'around' time, + * i.e. between time-X and time+Y (X and Y are implicitely defined in the + * RewindQueue). This avoids that messages from clients to the server create + * more and more TimeStepInfo, with smaller and smaller dt, which would make + * rewinds more expensive. + */ +class TimeStepInfo +{ +private: + typedef std::vector AllRewindInfo; + + /** The list of all states and events at a certain time. */ + AllRewindInfo m_list_of_events; + + /** Time at which those events should be executed here. */ + float m_time; + + /** Time step to be used. */ + float m_dt; + + /** Bullet maintains a 'left over' time since it is running with a fixed + * 60 fps. Restoring this value exactly improves accuracy of rewinds. */ + float m_local_physics_time; +public: + TimeStepInfo(float time, float dt); + void insert(RewindInfo *ri); + void undoAll(); + void replayAllEvents(); + void replayAllStates(); + // ------------------------------------------------------------------------ + /** Returns the time for this TimeStepInfo instance. */ + float getTime() const { return m_time; } + // ------------------------------------------------------------------------ + /** Returns the left-over physics time. */ + float getLocalPhysicsTime() const { return m_local_physics_time; } + // ------------------------------------------------------------------------ + /** Returns the (previous) time step size, so that rewindw can be done + * with same time step size. */ + float getDT() const { return m_dt; } + // ------------------------------------------------------------------------ + /** Returns if this TimeStepInfo instance has a confirmed state, i.e. if + * a rewind can start from this time. */ + bool hasConfirmedState() const + { + if (m_list_of_events.empty()) return false; + const RewindInfo *ri = m_list_of_events[0]; + return ri->isState() && ri->isConfirmed(); + } // hasConfirmedState +}; // TimeStepInfo + +#endif + From efeea8eeb2ef8b5ee0aa09bf30a393b28c1bde04 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 15 Feb 2017 09:50:00 +1100 Subject: [PATCH 038/413] More debug output. --- src/network/stk_host.cpp | 5 +++-- src/network/stk_peer.cpp | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index e7ecd103a..7ad78e943 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -569,8 +569,9 @@ void* STKHost::mainLoop(void* self) TransportAddress stk_addr(peer->getAddress()); #ifdef DEBUG_MESSAGE_CONTENT Log::verbose("NetworkManager", - "Message, Sender : %s, message:", - stk_addr.toString(/*show port*/false).c_str()); + "Message, Sender : %s time %f message:", + stk_addr.toString(/*show port*/false).c_str(), + StkTime::getRealTime()); Log::verbose("NetworkManager", "%s", stk_event->data().getLogMessage().c_str()); #endif diff --git a/src/network/stk_peer.cpp b/src/network/stk_peer.cpp index 61c0c860e..4405c5ab5 100644 --- a/src/network/stk_peer.cpp +++ b/src/network/stk_peer.cpp @@ -63,8 +63,8 @@ void STKPeer::sendPacket(NetworkString *data, bool reliable) { data->setToken(m_client_server_token); TransportAddress a(m_enet_peer->address); - Log::verbose("STKPeer", "sending packet of size %d to %s", - data->size(), a.toString().c_str()); + Log::verbose("STKPeer", "sending packet of size %d to %s at %f", + data->size(), a.toString().c_str(),StkTime::getRealTime()); ENetPacket* packet = enet_packet_create(data->getData(), data->getTotalSize(), From 17751631f1faa4a54fb69b2236507764ab849522 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 15 Feb 2017 09:57:38 +1100 Subject: [PATCH 039/413] Protocol data structure uses now finer grained locks, avoiding that asynchronous updates or event delivery can be delayed by synchronous updates/event deliveries to a different protocol. --- src/network/protocol.cpp | 15 - src/network/protocol.hpp | 15 +- src/network/protocol_manager.cpp | 412 +++++++++++++---------- src/network/protocol_manager.hpp | 113 +++++-- src/network/protocols/client_lobby.cpp | 27 +- src/network/protocols/lobby_protocol.cpp | 6 +- src/network/protocols/server_lobby.cpp | 7 +- 7 files changed, 330 insertions(+), 265 deletions(-) diff --git a/src/network/protocol.cpp b/src/network/protocol.cpp index 269633c4f..9e847d78f 100644 --- a/src/network/protocol.cpp +++ b/src/network/protocol.cpp @@ -37,7 +37,6 @@ Protocol::Protocol(ProtocolType type, CallbackObject* callback_object) m_callback_object = callback_object; m_type = type; m_state = PROTOCOL_STATE_INITIALISING; - m_id = 0; m_handle_connections = false; m_handle_disconnections = false; } // Protocol @@ -107,20 +106,6 @@ void Protocol::requestTerminate() ProtocolManager::getInstance()->requestTerminate(this); } // requestTerminate -// ---------------------------------------------------------------------------- -/** Finds a protocol with the given type and requests it to be terminated. - * If no such protocol exist, log an error message. - * \param type The protocol type to delete. - */ -void Protocol::findAndTerminateProtocol(ProtocolType type) -{ - Protocol* protocol = ProtocolManager::getInstance()->getProtocol(type); - if (protocol) - protocol->requestTerminate(); - else - Log::error("Protocol", "No protocol %d registered.", type); -} // findAndTerminateProtocol - // ---------------------------------------------------------------------------- /** Sends a message to all peers, inserting the peer's token into the message. * The message is composed of a 1-byte message (usually the message type) diff --git a/src/network/protocol.hpp b/src/network/protocol.hpp index 12d3412fa..d67135641 100644 --- a/src/network/protocol.hpp +++ b/src/network/protocol.hpp @@ -45,12 +45,13 @@ enum ProtocolType PROTOCOL_CONNECTION = 0x01, //!< Protocol that deals with client-server connection. PROTOCOL_LOBBY_ROOM = 0x02, //!< Protocol that is used during the lobby room phase. PROTOCOL_START_GAME = 0x03, //!< Protocol used when starting the game. - PROTOCOL_SYNCHRONIZATION = 0x04, //!isSynchronous()) + { + m_sync_events_to_process.lock(); + m_sync_events_to_process.getData().push_back(event); + m_sync_events_to_process.unlock(); + } + else + { + m_async_events_to_process.lock(); + m_async_events_to_process.getData().push_back(event); + m_async_events_to_process.unlock(); + } return; } // propagateEvent // ---------------------------------------------------------------------------- /** \brief Asks the manager to start a protocol. - * This function will store the request, and process it at a time it is + * This function will store the request, and process it at a time when it is * thread-safe. * \param protocol : A pointer to the protocol to start * \return The unique id of the protocol that is being started. */ -uint32_t ProtocolManager::requestStart(Protocol* protocol) +void ProtocolManager::requestStart(Protocol* protocol) { - // assign a unique id to the protocol. - protocol->setId(getNextProtocolId()); // create the request ProtocolRequest req(PROTOCOL_REQUEST_START, protocol); // add it to the request stack m_requests.lock(); m_requests.getData().push_back(req); m_requests.unlock(); - - return req.getProtocol()->getId(); } // requestStart // ---------------------------------------------------------------------------- @@ -195,25 +208,24 @@ void ProtocolManager::requestTerminate(Protocol* protocol) } // requestTerminate // ---------------------------------------------------------------------------- + /** \brief Starts a protocol. * Add the protocol info to the m_protocols vector. * \param protocol : ProtocolInfo to start. */ void ProtocolManager::startProtocol(Protocol *protocol) { - // add the protocol to the protocol vector so that it's updated - m_protocols.lock(); - pthread_mutex_lock(&m_asynchronous_protocols_mutex); - Log::info("ProtocolManager", - "A %s protocol with id=%u has been started. There are %ld protocols running.", - typeid(*protocol).name(), protocol->getId(), - m_protocols.getData().size()+1); - m_protocols.getData().push_back(protocol); - // setup the protocol and notify it that it's started + assert(pthread_equal(pthread_self(), m_asynchronous_update_thread)); + OneProtocolType &opt = m_all_protocols[protocol->getProtocolType()]; + opt.lock(); + opt.addProtocol(protocol); protocol->setup(); protocol->setState(PROTOCOL_STATE_RUNNING); - m_protocols.unlock(); - pthread_mutex_unlock(&m_asynchronous_protocols_mutex); + opt.unlock(); + Log::info("ProtocolManager", + "A %s protocol has been started.", typeid(*protocol).name()); + + // setup the protocol and notify it that it's started } // startProtocol // ---------------------------------------------------------------------------- @@ -223,9 +235,14 @@ void ProtocolManager::startProtocol(Protocol *protocol) */ void ProtocolManager::pauseProtocol(Protocol *protocol) { + assert(pthread_equal(pthread_self(), m_asynchronous_update_thread)); assert(protocol->getState() == PROTOCOL_STATE_RUNNING); + // We lock the protocol to avoid that paused() is called at the same + // time that the main thread delivers an event or calls update + m_all_protocols[protocol->getProtocolType()].lock(); protocol->setState(PROTOCOL_STATE_PAUSED); protocol->paused(); + m_all_protocols[protocol->getProtocolType()].unlock(); } // pauseProtocol // ---------------------------------------------------------------------------- @@ -235,11 +252,36 @@ void ProtocolManager::pauseProtocol(Protocol *protocol) */ void ProtocolManager::unpauseProtocol(Protocol *protocol) { + assert(pthread_equal(pthread_self(), m_asynchronous_update_thread)); assert(protocol->getState() == PROTOCOL_STATE_PAUSED); + // No lock necessary, since the protocol is paused, no other thread will + // be executing protocol->setState(PROTOCOL_STATE_RUNNING); protocol->unpaused(); } // unpauseProtocol +// ---------------------------------------------------------------------------- +/** Removes a protocol from the list of protocols of a certain type. + * Note that the protocol is not deleted. + * \param p The protocol to be removed. +*/ +void ProtocolManager::OneProtocolType::removeProtocol(Protocol *p) +{ + std::vector::iterator i = + std::find(m_protocols.getData().begin(), + m_protocols.getData().end(), p); + if (i == m_protocols.getData().end()) + { + Log::error("ProtocolManager", + "Trying to delete protocol '%s', which was not found", + typeid(*p).name()); + } + else + { + m_protocols.getData().erase(i); + } +} // deleteProtocol + // ---------------------------------------------------------------------------- /** \brief Notes that a protocol is terminated. * Remove a protocol from the protocols vector. @@ -247,68 +289,116 @@ void ProtocolManager::unpauseProtocol(Protocol *protocol) */ void ProtocolManager::terminateProtocol(Protocol *protocol) { - // Be sure that noone accesses the protocols vector while we erase a protocol - m_protocols.lock(); - pthread_mutex_lock(&m_asynchronous_protocols_mutex); - int offset = 0; - std::string protocol_type = typeid(*protocol).name(); - for (unsigned int i = 0; i < m_protocols.getData().size(); i++) - { - if (m_protocols.getData()[i-offset] == protocol) - { - protocol->setState(PROTOCOL_STATE_TERMINATED); - m_protocols.getData().erase(m_protocols.getData().begin()+(i-offset), - m_protocols.getData().begin()+(i-offset)+1); - offset++; - } - } - Log::info("ProtocolManager", - "A %s protocol has been terminated. There are %ld protocols running.", - protocol_type.c_str(), m_protocols.getData().size()); - pthread_mutex_unlock(&m_asynchronous_protocols_mutex); - m_protocols.unlock(); + assert(pthread_equal(pthread_self(), m_asynchronous_update_thread)); + + OneProtocolType &opt = m_all_protocols[protocol->getProtocolType()]; + // Be sure that noone accesses the protocols vector + // while the protocol is being removed. + opt.lock(); + opt.removeProtocol(protocol); + opt.unlock(); + protocol->setState(PROTOCOL_STATE_TERMINATED); protocol->terminated(); } // terminateProtocol +// ---------------------------------------------------------------------------- +/** Requests to terminate all protocols of the given protocol type. + * This function must be called from the ProtocolManager thread in order + * to avoid a race condition (only the ProtocolManager thread can change the + * number of elements in that list). + */ +void ProtocolManager::OneProtocolType::requestTerminateAll() +{ + for (unsigned int i = 0; i < m_protocols.getData().size(); i++) + { + m_protocols.getData()[i]->requestTerminate(); + } +} // requestTerminateAll + +// ---------------------------------------------------------------------------- +/** Finds a protocol with the given type and requests it to be terminated. + * If no such protocol exist, log an error message. + * \param type The protocol type to delete. + */ +void ProtocolManager::findAndTerminate(ProtocolType type) +{ + OneProtocolType &opt = m_all_protocols[type]; + if (opt.isEmpty()) + Log::error("ProtocolManager", + "findAndTerminate: No protocol %d registered.", type); + + opt.requestTerminateAll(); +} // findAndTerminate + +// ---------------------------------------------------------------------------- +/** Calls either notifyEvent(event) or notifyEventAsynchronous(evet) on all + * protocols. Note that no locking is done, it is the responsibility of the + * caller to avoid race conditions. + * \param event The event to deliver to the protocols. + */ +bool ProtocolManager::OneProtocolType::notifyEvent(Event *event) +{ + if (m_protocols.getData().empty()) return false; + + // Either all protocols of a certain type handle connects, or none. + // So we tet only one of them + if (event->getType() == EVENT_TYPE_CONNECTED && + !m_protocols.getData()[0]->handleConnects()) return false; + if (event->getType() == EVENT_TYPE_DISCONNECTED && + !m_protocols.getData()[0]->handleDisconnects()) return false; + + bool can_be_deleted = false; + for (unsigned int i = 0; i < m_protocols.getData().size(); i++) + { + bool done = event->isSynchronous() + ? m_protocols.getData()[i]->notifyEvent(event) + : m_protocols.getData()[i]->notifyEventAsynchronous(event); + can_be_deleted |= done; + } + return can_be_deleted; +} // notifyEvent + // ---------------------------------------------------------------------------- /** Sends the event to the corresponding protocol. Returns true if the event * can be ignored, or false otherwise. */ bool ProtocolManager::sendEvent(Event* event) { - m_protocols.lock(); - int count=0; - for(unsigned int i=0; igetType() == EVENT_TYPE_MESSAGE) { - Protocol *p = m_protocols.getData()[i]; - bool is_right_protocol = false; - switch(event->getType()) + OneProtocolType &opt = m_all_protocols[event->data().getProtocolType()]; + can_be_deleted = opt.notifyEvent(event); + } + else // connect or disconnect event --> test all protocols + { + for (unsigned int i = 0; i < m_all_protocols.size(); i++) { - case EVENT_TYPE_MESSAGE: - is_right_protocol = event->data().getProtocolType()==p->getProtocolType(); - break; - case EVENT_TYPE_DISCONNECTED: - is_right_protocol = p->handleDisconnects(); - break; - case EVENT_TYPE_CONNECTED: - is_right_protocol = p->handleConnects(); - break; - } // switch event->getType() - - if( is_right_protocol) - { - count ++; - event->isSynchronous() ? p->notifyEvent(event) - : p->notifyEventAsynchronous(event); + can_be_deleted |= m_all_protocols[i].notifyEvent(event); } - } // for i in protocols - - m_protocols.unlock(); - - return (count > 0 || StkTime::getTimeSinceEpoch() - event->getArrivalTime() - >= TIME_TO_KEEP_EVENTS ); + } + return can_be_deleted || StkTime::getTimeSinceEpoch() - event->getArrivalTime() + >= TIME_TO_KEEP_EVENTS; } // sendEvent +// ---------------------------------------------------------------------------- +/** Calls either the synchronous update or asynchronous update function in all + * protocols of this type. + * \param dt Time step size. + * \param async True if asynchronousUpdate() should be called. + */ +void ProtocolManager::OneProtocolType::update(float dt, bool async) +{ + for (unsigned int i = 0; i < m_protocols.getData().size(); i++) + { + if (m_protocols.getData()[i]->getState() == PROTOCOL_STATE_RUNNING) + { + async ? m_protocols.getData()[i]->asynchronousUpdate() + : m_protocols.getData()[i]->update(dt); + } + } +} // update + // ---------------------------------------------------------------------------- /** \brief Updates the manager. * @@ -321,25 +411,22 @@ bool ProtocolManager::sendEvent(Event* event) */ void ProtocolManager::update(float dt) { - // before updating, notify protocols that they have received events - m_events_to_process.lock(); - EventList::iterator i = m_events_to_process.getData().begin(); + // Update from main thread only: + assert(!pthread_equal(pthread_self(), m_asynchronous_update_thread)); - while (i != m_events_to_process.getData().end()) + // before updating, notify protocols that they have received events + m_sync_events_to_process.lock(); + EventList::iterator i = m_sync_events_to_process.getData().begin(); + + while (i != m_sync_events_to_process.getData().end()) { - // Don't handle asynchronous events here - if (!(*i)->isSynchronous()) - { - ++i; - continue; - } - m_events_to_process.unlock(); - bool result = sendEvent(*i); - m_events_to_process.lock(); - if (result) + m_sync_events_to_process.unlock(); + bool can_be_deleted = sendEvent(*i); + m_sync_events_to_process.lock(); + if (can_be_deleted) { delete *i; - i = m_events_to_process.getData().erase(i); + i = m_sync_events_to_process.getData().erase(i); } else { @@ -347,16 +434,16 @@ void ProtocolManager::update(float dt) ++i; } } - m_events_to_process.unlock(); + m_sync_events_to_process.unlock(); - // now update all protocols - m_protocols.lock(); - for (unsigned int i = 0; i < m_protocols.getData().size(); i++) + // Now update all protocols. + for (unsigned int i = 0; i < m_all_protocols.size(); i++) { - if (m_protocols.getData()[i]->getState() == PROTOCOL_STATE_RUNNING) - m_protocols.getData()[i]->update(dt); + OneProtocolType &opt = m_all_protocols[i]; + opt.lock(); + opt.update(dt, /*async*/false); + opt.unlock(); } - m_protocols.unlock(); } // update // ---------------------------------------------------------------------------- @@ -370,53 +457,59 @@ void ProtocolManager::update(float dt) */ void ProtocolManager::asynchronousUpdate() { - // before updating, notice protocols that they have received information - m_events_to_process.lock(); - int size = (int)m_events_to_process.getData().size(); - EventList::iterator i = m_events_to_process.getData().begin(); - while (i != m_events_to_process.getData().end()) + // Update from ProtocolManager thread only: + assert(pthread_equal(pthread_self(), m_asynchronous_update_thread)); + + // First deliver asynchronous messages for all protocols + // ===================================================== + m_async_events_to_process.lock(); + EventList::iterator i = m_async_events_to_process.getData().begin(); + while (i != m_async_events_to_process.getData().end()) { - // Don't handle synchronous events here. - if ((*i)->isSynchronous()) - { - ++i; - continue; - } - m_events_to_process.unlock(); + m_async_events_to_process.unlock(); + + m_all_protocols[(*i)->getType()].lock(); bool result = sendEvent(*i); - m_events_to_process.lock(); + m_all_protocols[(*i)->getType()].unlock(); + + m_async_events_to_process.lock(); if (result) { delete *i; - i = m_events_to_process.getData().erase(i); + i = m_async_events_to_process.getData().erase(i); } else { // This should only happen if the protocol has not been started + // or already terminated (e.g. late ping answer) ++i; } } // while i != m_events_to_process.end() - m_events_to_process.unlock(); + m_async_events_to_process.unlock(); - // now update all protocols that need to be updated in asynchronous mode - pthread_mutex_lock(&m_asynchronous_protocols_mutex); - // FIXME: does m_protocols need to be locked??? - for (unsigned int i = 0; i < m_protocols.getData().size(); i++) + // Second: update all running protocols + // ==================================== + // Now update all protocols. + for (unsigned int i = 0; i < m_all_protocols.size(); i++) { - if (m_protocols.getData()[i]->getState() == PROTOCOL_STATE_RUNNING) - m_protocols.getData()[i]->asynchronousUpdate(); + OneProtocolType &opt = m_all_protocols[i]; + // The lock is likely not necessary, since this function is only + // called from the ProtocolManager thread, and this thread is also + // the only one who changes the number of protocols. + opt.lock(); + opt.update(0, /*async*/true); // dt does not matter, so set it to 0 + opt.unlock(); } - pthread_mutex_unlock(&m_asynchronous_protocols_mutex); - // Process queued events for protocols - // these requests are asynchronous + // Process queued events (start, pause, ...) for protocols asynchronously + // ====================================================================== m_requests.lock(); while(m_requests.getData().size()>0) { ProtocolRequest request = m_requests.getData()[0]; m_requests.getData().erase(m_requests.getData().begin()); - m_requests.unlock(); // Make sure new requests can be queued up while handling requests. + m_requests.unlock(); // This is often used that terminating a protocol unpauses another, // so the m_requests queue must not be locked while executing requests. switch (request.getType()) @@ -439,22 +532,6 @@ void ProtocolManager::asynchronousUpdate() m_requests.unlock(); } // asynchronousUpdate -// ---------------------------------------------------------------------------- -/** \brief Get a protocol using its id. - * \param id : Unique ID of the seek protocol. - * \return The protocol that has the ID id. - */ -Protocol* ProtocolManager::getProtocol(uint32_t id) -{ - // FIXME: does m_protocols need to be locked?? - for (unsigned int i = 0; i < m_protocols.getData().size(); i++) - { - if (m_protocols.getData()[i]->getId() == id) - return m_protocols.getData()[i]; - } - return NULL; -} // getProtocol - // ---------------------------------------------------------------------------- /** \brief Get a protocol using its type. * \param type : The type of the protocol. @@ -462,29 +539,8 @@ Protocol* ProtocolManager::getProtocol(uint32_t id) */ Protocol* ProtocolManager::getProtocol(ProtocolType type) { - // FIXME: Does m_protocols need to be locked? - for (unsigned int i = 0; i < m_protocols.getData().size(); i++) - { - if (m_protocols.getData()[i]->getProtocolType() == type) - return m_protocols.getData()[i]; - } - return NULL; -} // getProtocol - -// ---------------------------------------------------------------------------- -/** \brief Assign an id to a protocol. - * This function will assign m_next_protocol_id as the protocol id. - * This id starts at 0 at the beginning and is increased by 1 each time - * a protocol starts. - * \param protocol_info : The protocol info that needs an id. - */ -uint32_t ProtocolManager::getNextProtocolId() -{ - m_next_protocol_id.lock(); - uint32_t id = m_next_protocol_id.getData(); - m_next_protocol_id.getData()++; - m_next_protocol_id.unlock(); - return id; -} // getNextProtocolId - + OneProtocolType &opt = m_all_protocols[type]; + if (opt.isEmpty()) return NULL; + return opt.getFirstProtocol(); +} // getProtocol \ No newline at end of file diff --git a/src/network/protocol_manager.hpp b/src/network/protocol_manager.hpp index d07c64a6a..9f346b37a 100644 --- a/src/network/protocol_manager.hpp +++ b/src/network/protocol_manager.hpp @@ -54,7 +54,8 @@ enum ProtocolRequestType // ---------------------------------------------------------------------------- /** \struct ProtocolRequest - * \brief Represents a request to do an action about a protocol. + * \brief Represents a request to do an action about a protocol, e.g. to + * start, pause, unpause or terminate a protocol. */ class ProtocolRequest { @@ -97,41 +98,89 @@ class ProtocolManager : public AbstractSingleton, friend class AbstractSingleton; private: - /** Contains the running protocols. - * This stores the protocols that are either running or paused, their - * state and their unique id. */ - Synchronised >m_protocols; + /** A simple class that stores all protocols of a certain type. While + * many protocols have at most one instance running, some (e.g. + * GetPublicAddress, ConntectToPeer, ...) can have several instances + * active at the same time. */ + class OneProtocolType + { + private: + Synchronised< std::vector > m_protocols; + public: + void removeProtocol(Protocol *p); + void requestTerminateAll(); + bool notifyEvent(Event *event); + void update(float dt, bool async); + void abort(); + // -------------------------------------------------------------------- + /** Returns the first protocol of a given type. It is assumed that + * there is a protocol of that type. */ + Protocol *getFirstProtocol() { return m_protocols.getData()[0]; } + // -------------------------------------------------------------------- + /** Returns if this protocol class handles connect events. Protocols + * of the same class either all handle a connect event, or none, so + * only the first protocol is actually tested. */ + bool handleConnects() const + { + return !m_protocols.getData().empty() && + m_protocols.getData()[0]->handleConnects(); + } // handleConnects + // -------------------------------------------------------------------- + /** Returns if this protocol class handles disconnect events. Protocols + * of the same class either all handle a disconnect event, or none, so + * only the first protocol is actually tested. */ + bool handleDisconnects() const + { + return !m_protocols.getData().empty() && + m_protocols.getData()[0]->handleDisconnects(); + } // handleDisconnects + // -------------------------------------------------------------------- + /** Locks access to this list of all protocols of a certain type. */ + void lock() { m_protocols.lock(); } + // -------------------------------------------------------------------- + /** Locks access to this list of all protocols of a certain type. */ + void unlock() { m_protocols.unlock(); } + // -------------------------------------------------------------------- + void addProtocol(Protocol *p) + { + m_protocols.getData().push_back(p); + } // addProtocol + // -------------------------------------------------------------------- + /** Returns if there are no protocols of this type registered. */ + bool isEmpty() const { return m_protocols.getData().empty(); } + // -------------------------------------------------------------------- + }; // class OneProtocolType + + // ------------------------------------------------------------------------ + + /** The list of all protocol types, each one containing a (potentially + * empty) list of protocols. */ + std::vector m_all_protocols; + + /** A list of network events - messages, disconnect and disconnects. */ typedef std::list EventList; + /** Contains the network events to pass synchronously to protocols + * (i.e. from the main thread). */ + Synchronised m_sync_events_to_process; + /** Contains the network events to pass asynchronously to protocols - * (i.e. from the separate ProtocolManager thread). */ - Synchronised m_events_to_process; + * (i.e. from the separate ProtocolManager thread). */ + Synchronised m_async_events_to_process; /** Contains the requests to start/pause etc... protocols. */ Synchronised< std::vector > m_requests; - /*! \brief The next id to assign to a protocol. - * This value is incremented by 1 each time a protocol is started. - * If a protocol has an id lower than this value, it means that it has - * been formerly started. - */ - Synchronised m_next_protocol_id; - /** When set to true, the main thread will exit. */ Synchronised m_exit; - // mutexes: - /*! Used to ensure that the protocol vector is used thread-safely. */ - pthread_mutex_t m_asynchronous_protocols_mutex; - /*! Asynchronous update thread.*/ - pthread_t* m_asynchronous_update_thread; + pthread_t m_asynchronous_update_thread; ProtocolManager(); virtual ~ProtocolManager(); static void* mainLoop(void *data); - uint32_t getNextProtocolId(); bool sendEvent(Event* event); virtual void startProtocol(Protocol *protocol); @@ -141,15 +190,21 @@ private: virtual void unpauseProtocol(Protocol *protocol); public: - virtual void abort(); - virtual void propagateEvent(Event* event); - virtual uint32_t requestStart(Protocol* protocol); - virtual void requestPause(Protocol* protocol); - virtual void requestUnpause(Protocol* protocol); - virtual void requestTerminate(Protocol* protocol); - virtual void update(float dt); - virtual Protocol* getProtocol(uint32_t id); - virtual Protocol* getProtocol(ProtocolType type); + void abort(); + void propagateEvent(Event* event); + Protocol* getProtocol(ProtocolType type); + void requestStart(Protocol* protocol); + void requestPause(Protocol* protocol); + void requestUnpause(Protocol* protocol); + void requestTerminate(Protocol* protocol); + void findAndTerminate(ProtocolType type); + void update(float dt); + // ------------------------------------------------------------------------ + const pthread_t & getThreadID() const + { + return m_asynchronous_update_thread; + } // getThreadID + }; // class ProtocolManager #endif // PROTOCOL_MANAGER_HPP diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index 4c56e022c..e7e595e31 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -679,29 +679,10 @@ void ClientLobby::raceFinished(Event* event) "Server notified that the race is finished."); // stop race protocols - Protocol* protocol = ProtocolManager::getInstance() - ->getProtocol(PROTOCOL_CONTROLLER_EVENTS); - if (protocol) - ProtocolManager::getInstance()->requestTerminate(protocol); - else - Log::error("ClientLobby", - "No controller events protocol registered."); - - protocol = ProtocolManager::getInstance() - ->getProtocol(PROTOCOL_KART_UPDATE); - if (protocol) - ProtocolManager::getInstance()->requestTerminate(protocol); - else - Log::error("ClientLobby", - "No kart update protocol registered."); - - protocol = ProtocolManager::getInstance() - ->getProtocol(PROTOCOL_GAME_EVENTS); - if (protocol) - ProtocolManager::getInstance()->requestTerminate(protocol); - else - Log::error("ClientLobby", - "No game events protocol registered."); + ProtocolManager *pm = ProtocolManager::getInstance(); + pm->findAndTerminate(PROTOCOL_CONTROLLER_EVENTS); + pm->findAndTerminate(PROTOCOL_KART_UPDATE); + pm->findAndTerminate(PROTOCOL_GAME_EVENTS); // finish the race WorldWithRank* ranked_world = (WorldWithRank*)(World::getWorld()); diff --git a/src/network/protocols/lobby_protocol.cpp b/src/network/protocols/lobby_protocol.cpp index b084be5d9..0c0985392 100644 --- a/src/network/protocols/lobby_protocol.cpp +++ b/src/network/protocols/lobby_protocol.cpp @@ -135,9 +135,5 @@ void LobbyProtocol::loadWorld() */ void LobbyProtocol::terminateLatencyProtocol() { - Protocol *p = ProtocolManager::getInstance() - ->getProtocol(PROTOCOL_SYNCHRONIZATION); - LatencyProtocol *sp = dynamic_cast(p); - if (sp) - sp->requestTerminate(); + ProtocolManager::getInstance()->findAndTerminate(PROTOCOL_SYNCHRONIZATION); } // stopLatencyProtocol diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 270550388..de363695c 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -279,9 +279,10 @@ void ServerLobby::update(float dt) // notify the network world that it is stopped RaceEventManager::getInstance()->stop(); // stop race protocols - findAndTerminateProtocol(PROTOCOL_CONTROLLER_EVENTS); - findAndTerminateProtocol(PROTOCOL_KART_UPDATE); - findAndTerminateProtocol(PROTOCOL_GAME_EVENTS); + ProtocolManager *pm = ProtocolManager::getInstance(); + pm->findAndTerminate(PROTOCOL_CONTROLLER_EVENTS); + pm->findAndTerminate(PROTOCOL_KART_UPDATE); + pm->findAndTerminate(PROTOCOL_GAME_EVENTS); } break; case DONE: From 6162be94324afca55277f47c72fef8adaff9663e Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 16 May 2017 22:00:35 +1000 Subject: [PATCH 040/413] Add support for a 'prefix' to be printed for all log messages. --- src/utils/log.cpp | 10 ++++++++++ src/utils/log.hpp | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/src/utils/log.cpp b/src/utils/log.cpp index e5d39167e..4816d15d5 100644 --- a/src/utils/log.cpp +++ b/src/utils/log.cpp @@ -35,6 +35,7 @@ Log::LogLevel Log::m_min_log_level = Log::LL_VERBOSE; bool Log::m_no_colors = false; FILE* Log::m_file_stdout = NULL; +std::string Log::m_prefix = ""; // ---------------------------------------------------------------------------- /** Selects background/foreground colors for the message depending on @@ -181,6 +182,8 @@ void Log::printMessage(int level, const char *component, const char *format, #ifdef ANDROID __android_log_vprint(alp, "SuperTuxKart", format, out); #else + if(!m_prefix.empty()) + printf("%s ", m_prefix.c_str()); printf("[%s] %s: ", names[level], component); vprintf(format, out); #endif @@ -193,6 +196,11 @@ void Log::printMessage(int level, const char *component, const char *format, static char szBuff[2048]; vsnprintf(szBuff, sizeof(szBuff), format, copy2); + if (!m_prefix.empty()) + { + OutputDebugString(m_prefix.c_str()); + OutputDebugString(" "); + } OutputDebugString("["); OutputDebugString(names[level]); OutputDebugString("] "); @@ -205,6 +213,8 @@ void Log::printMessage(int level, const char *component, const char *format, if(m_file_stdout) { + if(!m_prefix.empty()) + fprintf(m_file_stdout, "%s ", m_prefix.c_str()); fprintf (m_file_stdout, "[%s] %s: ", names[level], component); vfprintf(m_file_stdout, format, copy); fprintf (m_file_stdout, "\n"); diff --git a/src/utils/log.hpp b/src/utils/log.hpp index 61726cb8b..2d4c69a65 100644 --- a/src/utils/log.hpp +++ b/src/utils/log.hpp @@ -59,6 +59,9 @@ private: /** The file where stdout output will be written */ static FILE* m_file_stdout; + /** An optional prefix to be printed. */ + static std::string m_prefix; + static void setTerminalColor(LogLevel level); static void resetTerminalColor(); @@ -120,5 +123,9 @@ public: { m_no_colors = true; } // disableColor + // ------------------------------------------------------------------------ + /** Sets a prefix to be printed before each line. To disable the prefix, + * set it to "". */ + static void setPrefix(const std::string &prefix) { m_prefix = prefix; } }; // Log #endif From 54fdd2d586ca5a33d08bf5ddb1a898b7faf4ede1 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 16 May 2017 22:01:32 +1000 Subject: [PATCH 041/413] Add prefix for log messages during a rewind. --- src/network/rewind_manager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index c7b14e3ec..b46013096 100755 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -236,7 +236,9 @@ void RewindManager::playEventsTill(float time, float *dt) if (needs_rewind) { + Log::setPrefix("Rewind"); rewindTo(rewind_time); + Log::setPrefix(""); } // This is necessary to avoid that rewinding an event will store the From fb5a6007874dc058d6c5d750eaed1d34a1afad50 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 17 May 2017 09:19:19 +1000 Subject: [PATCH 042/413] Remove 'duplicated' input events (e.g. auto-repeat might send 'accelerate' events over and over). This reduces traffic to the server and other clients (and fixed a bug caused by rewinding those events in the wrong order). --- .../controller/local_player_controller.cpp | 14 +- src/karts/controller/player_controller.cpp | 121 +++++++++++++++++- src/karts/controller/player_controller.hpp | 1 + 3 files changed, 127 insertions(+), 9 deletions(-) diff --git a/src/karts/controller/local_player_controller.cpp b/src/karts/controller/local_player_controller.cpp index 1cc3316b8..89463dd4f 100644 --- a/src/karts/controller/local_player_controller.cpp +++ b/src/karts/controller/local_player_controller.cpp @@ -142,15 +142,21 @@ void LocalPlayerController::resetInputState() */ void LocalPlayerController::action(PlayerAction action, int value) { + // If this event does not change the control state (e.g. + // it's a (auto) repeat event), do nothing. This especially + // optimises traffic to the server and other clients. + if (!actionChangesState(action, value)) + return; + // If this is a client, send the action to networking layer if (World::getWorld()->isNetworkWorld() && NetworkConfig::get()->isClient() && !RewindManager::get()->isRewinding() ) { - GameProtocol::getInstance()->controllerAction(m_kart->getWorldKartId(), - action, value, - m_steer_val_l, - m_steer_val_r); + GameProtocol::getInstance() + ->controllerAction(m_kart->getWorldKartId(), + action, value, + m_steer_val_l, m_steer_val_r); } PlayerController::action(action, value); } // action diff --git a/src/karts/controller/player_controller.cpp b/src/karts/controller/player_controller.cpp index 75ce0babe..f0c02defc 100644 --- a/src/karts/controller/player_controller.cpp +++ b/src/karts/controller/player_controller.cpp @@ -78,6 +78,115 @@ void PlayerController::resetInputState() m_controls->reset(); } // resetInputState +// ---------------------------------------------------------------------------- +/** Returns true if the specified action and value will trigger a state + * change. If a new action from the player does not cause a state change, + * there is no need to forward that action to the server (or other clients). + * NOTE: this function must close mirror action(), make sure both functions + * are changed in synch. + * \param action The action to be executed. + * \param value If 32768, it indicates a digital value of 'fully set' + * if between 1 and 32767, it indicates an analog value, + * and if it's 0 it indicates that the corresponding button + * was released. + * \return + */ + bool PlayerController::actionChangesState(PlayerAction action, int value) +{ + switch (action) + { + case PA_STEER_LEFT: + if (m_steer_val_l != value) return true; + if (value) + { + if (m_steer_val != value) return true; + return (m_controls->getSkidControl() == KartControl::SC_NO_DIRECTION); + } + else + return m_steer_val != m_steer_val_r; + + break; + case PA_STEER_RIGHT: + if (m_steer_val_r != -value) return true; + if (value) + { + if(m_steer_val != -value) return true; + return m_controls->getSkidControl() == KartControl::SC_NO_DIRECTION; + } + else + return m_steer_val != m_steer_val_l; + + break; + case PA_ACCEL: + if (m_prev_accel != value) return true; + if (value && !(m_penalty_time > 0.0f)) + { + if (m_controls->getAccel() != value / 32768.f) return true; + if (m_controls->getBrake()) return true; + return m_controls->getNitro() != m_prev_nitro; + } + else + { + if (m_controls->getAccel() != 0 ) return true; + if (m_controls->getBrake() != m_prev_brake) return true; + return m_controls->getNitro(); + } + break; + case PA_BRAKE: + if (m_prev_brake != (value != 0) ) return true; + // let's consider below that to be a deadzone + if (value > 32768 / 2) + { + if (!m_controls->getBrake() ) return true; + if (m_controls->getAccel() != 0) return true; + return m_controls->getNitro(); + } + else + { + if (m_controls->getBrake() ) return true; + if (m_controls->getAccel() != m_prev_accel/32768.0f) return true; + // Nitro still depends on whether we're accelerating + return m_controls->getNitro() != m_prev_nitro && m_prev_accel; + } + break; + case PA_NITRO: + // This basically keeps track whether the button still is being pressed + if (m_prev_nitro != (value != 0)) return true; + // Enable nitro only when also accelerating + return m_controls->getNitro() != ( (value != 0) && + m_controls->getAccel() ); + break; + case PA_RESCUE: + return m_controls->getRescue() != (value != 0); + break; + case PA_FIRE: + return m_controls->getFire() != (value != 0); + break; + case PA_LOOK_BACK: + return m_controls->getLookBack() != (value != 0); + break; + case PA_DRIFT: + if (value == 0) + return m_controls->getSkidControl() != KartControl::SC_NONE; + else + { + if (m_steer_val == 0) + return m_controls->getSkidControl() != KartControl::SC_NO_DIRECTION; + else + return m_controls->getSkidControl() != (m_steer_val<0 + ? KartControl::SC_RIGHT + : KartControl::SC_LEFT ); + } + break; + case PA_PAUSE_RACE: + if (value != 0) StateManager::get()->escapePressed(); + break; + default: + break; + } + return true; +} // actionChangesState + // ---------------------------------------------------------------------------- /** This function interprets a kart action and value, and set the corresponding * entries in the kart control data structure. This function handles esp. @@ -85,11 +194,13 @@ void PlayerController::resetInputState() * releasing right, the steering must switch to left again. Similarly it * handles 'press left, press right, release left' (in which case still * right must be selected). Similarly for braking and acceleration. - * \param action The action to be executed. - * \param value If 32768, it indicates a digital value of 'fully set' - * if between 1 and 32767, it indicates an analog value, - * and if it's 0 it indicates that the corresponding button - * was released. + * NOTE: this function must close mirror action(), make sure both functions + * are changed in synch. + * \param action The action to be executed. + * \param value If 32768, it indicates a digital value of 'fully set' + * if between 1 and 32767, it indicates an analog value, + * and if it's 0 it indicates that the corresponding button + * was released. */ void PlayerController::action(PlayerAction action, int value) { diff --git a/src/karts/controller/player_controller.hpp b/src/karts/controller/player_controller.hpp index 024738854..7bc6950f0 100644 --- a/src/karts/controller/player_controller.hpp +++ b/src/karts/controller/player_controller.hpp @@ -45,6 +45,7 @@ public: virtual ~PlayerController (); virtual void update (float) OVERRIDE; virtual void action (PlayerAction action, int value) OVERRIDE; + virtual bool actionChangesState(PlayerAction action, int value); virtual void actionFromNetwork(PlayerAction action, int value, int value_l, int value_r); virtual void skidBonusTriggered() OVERRIDE; From 742be06c21f64e1c0a88be5831532e5ffbd920f9 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 22 May 2017 12:38:21 +1000 Subject: [PATCH 043/413] Removed code duplication by using macros. --- src/karts/controller/ai_base_controller.hpp | 23 +- src/karts/controller/controller.hpp | 2 +- src/karts/controller/ghost_controller.cpp | 3 +- src/karts/controller/ghost_controller.hpp | 3 +- .../controller/local_player_controller.cpp | 12 +- .../controller/local_player_controller.hpp | 3 +- src/karts/controller/player_controller.cpp | 238 +++++++----------- src/karts/controller/player_controller.hpp | 4 +- 8 files changed, 118 insertions(+), 170 deletions(-) diff --git a/src/karts/controller/ai_base_controller.hpp b/src/karts/controller/ai_base_controller.hpp index 033cbf369..8e7009245 100644 --- a/src/karts/controller/ai_base_controller.hpp +++ b/src/karts/controller/ai_base_controller.hpp @@ -85,20 +85,23 @@ public: AIBaseController(AbstractKart *kart); virtual ~AIBaseController() {}; virtual void reset(); - virtual bool disableSlipstreamBonus() const; - virtual void crashed(const Material *m); + virtual bool disableSlipstreamBonus() const OVERRIDE; + virtual void crashed(const Material *m) OVERRIDE; static void enableDebug() {m_ai_debug = true; } static void setTestAI(int n) {m_test_ai = n; } static int getTestAI() { return m_test_ai; } - virtual void crashed(const AbstractKart *k) {}; - virtual void handleZipper(bool play_sound) {}; - virtual void finishedRace(float time) {}; + virtual void crashed(const AbstractKart *k) OVERRIDE {}; + virtual void handleZipper(bool play_sound) OVERRIDE {}; + virtual void finishedRace(float time) OVERRIDE {}; virtual void collectedItem(const Item &item, int add_info=-1, - float previous_energy=0) {}; - virtual void setPosition(int p) {}; - virtual bool isPlayerController() const { return false; } - virtual bool isLocalPlayerController() const { return false; } - virtual void action(PlayerAction action, int value) {}; + float previous_energy=0) OVERRIDE {}; + virtual void setPosition(int p) OVERRIDE {}; + virtual bool isPlayerController() const OVERRIDE { return false; } + virtual bool isLocalPlayerController() const OVERRIDE { return false; } + virtual bool action(PlayerAction action, int value, bool dry_run=false) OVERRIDE + { + return true; + }; virtual void skidBonusTriggered() {}; // ------------------------------------------------------------------------ /** Not used for AIs. */ diff --git a/src/karts/controller/controller.hpp b/src/karts/controller/controller.hpp index da397dfc4..5b77efd45 100644 --- a/src/karts/controller/controller.hpp +++ b/src/karts/controller/controller.hpp @@ -89,7 +89,7 @@ public: const std::string &getControllerName() const { return m_controller_name; } // ------------------------------------------------------------------------ /** Default: ignore actions. Only PlayerController get them. */ - virtual void action(PlayerAction action, int value) = 0; + virtual bool action(PlayerAction action, int value, bool dry_run=false) = 0; // ------------------------------------------------------------------------ /** Callback whenever a new lap is triggered. Used by the AI * to trigger a recomputation of the way to use. */ diff --git a/src/karts/controller/ghost_controller.cpp b/src/karts/controller/ghost_controller.cpp index ce9fa8d96..6bd456c6f 100644 --- a/src/karts/controller/ghost_controller.cpp +++ b/src/karts/controller/ghost_controller.cpp @@ -81,9 +81,10 @@ void GhostController::addReplayTime(float time) } // addReplayTime //----------------------------------------------------------------------------- -void GhostController::action(PlayerAction action, int value) +bool GhostController::action(PlayerAction action, int value, bool dry_run) { // Watching replay use only if (action == PA_LOOK_BACK) m_controls->setLookBack(value!=0); + return true; } // action diff --git a/src/karts/controller/ghost_controller.hpp b/src/karts/controller/ghost_controller.hpp index 0d9a76e91..fafc742f3 100644 --- a/src/karts/controller/ghost_controller.hpp +++ b/src/karts/controller/ghost_controller.hpp @@ -55,7 +55,8 @@ public: virtual void setPosition(int p) OVERRIDE {} virtual bool isPlayerController() const OVERRIDE { return false; } virtual bool isLocalPlayerController() const OVERRIDE { return false; } - virtual void action(PlayerAction action, int value) OVERRIDE; + virtual bool action(PlayerAction action, int value, + bool dry_run=false) OVERRIDE; virtual void skidBonusTriggered() OVERRIDE {} virtual void newLap(int lap) OVERRIDE {} virtual void saveState(BareNetworkString *buffer) const {}; diff --git a/src/karts/controller/local_player_controller.cpp b/src/karts/controller/local_player_controller.cpp index 89463dd4f..8d3b970a4 100644 --- a/src/karts/controller/local_player_controller.cpp +++ b/src/karts/controller/local_player_controller.cpp @@ -139,14 +139,18 @@ void LocalPlayerController::resetInputState() * if between 1 and 32767, it indicates an analog value, * and if it's 0 it indicates that the corresponding button * was released. + * \param dry_run If set it will return if this action will trigger a + * state change or not. + * \return True if dry_run==true and a state change would be triggered. + * If dry_run==false, it returns true. */ -void LocalPlayerController::action(PlayerAction action, int value) +bool LocalPlayerController::action(PlayerAction action, int value, + bool dry_run) { // If this event does not change the control state (e.g. // it's a (auto) repeat event), do nothing. This especially // optimises traffic to the server and other clients. - if (!actionChangesState(action, value)) - return; + if (!PlayerController::action(action, value, /*dry_run*/true)) return false; // If this is a client, send the action to networking layer if (World::getWorld()->isNetworkWorld() && @@ -158,7 +162,7 @@ void LocalPlayerController::action(PlayerAction action, int value) action, value, m_steer_val_l, m_steer_val_r); } - PlayerController::action(action, value); + return PlayerController::action(action, value, /*dry_run*/false); } // action //----------------------------------------------------------------------------- diff --git a/src/karts/controller/local_player_controller.hpp b/src/karts/controller/local_player_controller.hpp index 5493e1b2d..cebdcf245 100644 --- a/src/karts/controller/local_player_controller.hpp +++ b/src/karts/controller/local_player_controller.hpp @@ -60,7 +60,8 @@ public: StateManager::ActivePlayer *player); ~LocalPlayerController(); void update (float) OVERRIDE; - void action (PlayerAction action, int value) OVERRIDE; + bool action (PlayerAction action, int value, + bool dry_run=false) OVERRIDE; virtual void handleZipper (bool play_sound) OVERRIDE; void collectedItem (const Item &item, int add_info=-1, float previous_energy=0) OVERRIDE; diff --git a/src/karts/controller/player_controller.cpp b/src/karts/controller/player_controller.cpp index f0c02defc..32dbb679d 100644 --- a/src/karts/controller/player_controller.cpp +++ b/src/karts/controller/player_controller.cpp @@ -78,115 +78,6 @@ void PlayerController::resetInputState() m_controls->reset(); } // resetInputState -// ---------------------------------------------------------------------------- -/** Returns true if the specified action and value will trigger a state - * change. If a new action from the player does not cause a state change, - * there is no need to forward that action to the server (or other clients). - * NOTE: this function must close mirror action(), make sure both functions - * are changed in synch. - * \param action The action to be executed. - * \param value If 32768, it indicates a digital value of 'fully set' - * if between 1 and 32767, it indicates an analog value, - * and if it's 0 it indicates that the corresponding button - * was released. - * \return - */ - bool PlayerController::actionChangesState(PlayerAction action, int value) -{ - switch (action) - { - case PA_STEER_LEFT: - if (m_steer_val_l != value) return true; - if (value) - { - if (m_steer_val != value) return true; - return (m_controls->getSkidControl() == KartControl::SC_NO_DIRECTION); - } - else - return m_steer_val != m_steer_val_r; - - break; - case PA_STEER_RIGHT: - if (m_steer_val_r != -value) return true; - if (value) - { - if(m_steer_val != -value) return true; - return m_controls->getSkidControl() == KartControl::SC_NO_DIRECTION; - } - else - return m_steer_val != m_steer_val_l; - - break; - case PA_ACCEL: - if (m_prev_accel != value) return true; - if (value && !(m_penalty_time > 0.0f)) - { - if (m_controls->getAccel() != value / 32768.f) return true; - if (m_controls->getBrake()) return true; - return m_controls->getNitro() != m_prev_nitro; - } - else - { - if (m_controls->getAccel() != 0 ) return true; - if (m_controls->getBrake() != m_prev_brake) return true; - return m_controls->getNitro(); - } - break; - case PA_BRAKE: - if (m_prev_brake != (value != 0) ) return true; - // let's consider below that to be a deadzone - if (value > 32768 / 2) - { - if (!m_controls->getBrake() ) return true; - if (m_controls->getAccel() != 0) return true; - return m_controls->getNitro(); - } - else - { - if (m_controls->getBrake() ) return true; - if (m_controls->getAccel() != m_prev_accel/32768.0f) return true; - // Nitro still depends on whether we're accelerating - return m_controls->getNitro() != m_prev_nitro && m_prev_accel; - } - break; - case PA_NITRO: - // This basically keeps track whether the button still is being pressed - if (m_prev_nitro != (value != 0)) return true; - // Enable nitro only when also accelerating - return m_controls->getNitro() != ( (value != 0) && - m_controls->getAccel() ); - break; - case PA_RESCUE: - return m_controls->getRescue() != (value != 0); - break; - case PA_FIRE: - return m_controls->getFire() != (value != 0); - break; - case PA_LOOK_BACK: - return m_controls->getLookBack() != (value != 0); - break; - case PA_DRIFT: - if (value == 0) - return m_controls->getSkidControl() != KartControl::SC_NONE; - else - { - if (m_steer_val == 0) - return m_controls->getSkidControl() != KartControl::SC_NO_DIRECTION; - else - return m_controls->getSkidControl() != (m_steer_val<0 - ? KartControl::SC_RIGHT - : KartControl::SC_LEFT ); - } - break; - case PA_PAUSE_RACE: - if (value != 0) StateManager::get()->escapePressed(); - break; - default: - break; - } - return true; -} // actionChangesState - // ---------------------------------------------------------------------------- /** This function interprets a kart action and value, and set the corresponding * entries in the kart control data structure. This function handles esp. @@ -194,103 +85,147 @@ void PlayerController::resetInputState() * releasing right, the steering must switch to left again. Similarly it * handles 'press left, press right, release left' (in which case still * right must be selected). Similarly for braking and acceleration. - * NOTE: this function must close mirror action(), make sure both functions - * are changed in synch. + * This function can be run in two modes: first, if 'dry_run' is set, + * it will return true if this action will cause a state change. This + * is sued in networking to avoid sending events to the server (and then + * to other clients) if they are just (e.g. auto) repeated events/ * \param action The action to be executed. * \param value If 32768, it indicates a digital value of 'fully set' * if between 1 and 32767, it indicates an analog value, * and if it's 0 it indicates that the corresponding button * was released. + * \param dry_run If set, it will only test if the parameter will trigger + * a state change. If not set, the appropriate actions + * (i.e. input state change) will be done. + * \return If dry_run is set, will return true if this action will + * cause a state change. If dry_run is not set, will return + * false. */ -void PlayerController::action(PlayerAction action, int value) +bool PlayerController::action(PlayerAction action, int value, bool dry_run) { Log::info("action", "t %f action %d value %d val_l %d val_r %d val %d", World::getWorld()->getTime(), action, value, m_steer_val_l, m_steer_val_r, m_steer_val); + + /** If dry_run (parameter) is true, this macro tests if this action would + * trigger a state change in the specified variable (without actually + * doing it). If it will trigger a state change, the marco will trigger + * immediatley a return to the caller. If dry_run is false, it will only + * assign the new value to the variable (and not return to the user + * early). The do-while(0) helps using this macro e.g. in the 'then' + * clause of an if statement. */ +#define SET_OR_TEST(var, value) \ + do \ + { \ + if(dry_run) \ + { \ + if (var != (value) ) return true; \ + } \ + else \ + { \ + var = value; \ + } \ + } while(0) + + /** Basically the same as the above macro, but is uses getter/setter + * funcitons. The name of the setter/getter is set'name'(value) and + * get'name'(). */ +#define SET_OR_TEST_GETTER(name, value) \ + do \ + { \ + if(dry_run) \ + { \ + if (m_controls->get##name() != (value) ) return true; \ + } \ + else \ + { \ + m_controls->set##name(value); \ + } \ + } while(0) + switch (action) { case PA_STEER_LEFT: - m_steer_val_l = value; + SET_OR_TEST(m_steer_val_l, value); if (value) { - m_steer_val = value; - if(m_controls->getSkidControl()==KartControl::SC_NO_DIRECTION) - m_controls->setSkidControl(KartControl::SC_LEFT); + SET_OR_TEST(m_steer_val, value); + if (m_controls->getSkidControl() == KartControl::SC_NO_DIRECTION) + SET_OR_TEST_GETTER(SkidControl, KartControl::SC_LEFT); } else - m_steer_val = m_steer_val_r; - + SET_OR_TEST(m_steer_val, m_steer_val_r); break; case PA_STEER_RIGHT: - m_steer_val_r = -value; + SET_OR_TEST(m_steer_val_r, -value); if (value) { - m_steer_val = -value; - if(m_controls->getSkidControl()==KartControl::SC_NO_DIRECTION) - m_controls->setSkidControl(KartControl::SC_RIGHT); + SET_OR_TEST(m_steer_val, -value); + if (m_controls->getSkidControl() == KartControl::SC_NO_DIRECTION) + SET_OR_TEST_GETTER(SkidControl, KartControl::SC_RIGHT); } else - m_steer_val = m_steer_val_l; + SET_OR_TEST(m_steer_val, m_steer_val_l); break; case PA_ACCEL: - m_prev_accel = value; + SET_OR_TEST(m_prev_accel, value); if (value && !(m_penalty_time > 0.0f)) { - m_controls->setAccel(value/32768.0f); - m_controls->setBrake(false); - m_controls->setNitro(m_prev_nitro); + SET_OR_TEST_GETTER(Accel, value/32768.0f); + SET_OR_TEST_GETTER(Brake, false); + SET_OR_TEST_GETTER(Nitro, m_prev_nitro); } else { - m_controls->setAccel(0.0f); - m_controls->setBrake(m_prev_brake); - m_controls->setNitro(false); + SET_OR_TEST_GETTER(Accel, 0.0f); + SET_OR_TEST_GETTER(Brake, m_prev_brake); + SET_OR_TEST_GETTER(Nitro, false); } break; case PA_BRAKE: - m_prev_brake = value!=0; + SET_OR_TEST(m_prev_brake, value!=0); // let's consider below that to be a deadzone if(value > 32768/2) { - m_controls->setBrake(true); - m_controls->setAccel(0.0f); - m_controls->setNitro(false); + SET_OR_TEST_GETTER(Brake, true); + SET_OR_TEST_GETTER(Accel, 0.0f); + SET_OR_TEST_GETTER(Nitro, false); } else { - m_controls->setBrake(false); - m_controls->setAccel(m_prev_accel/32768.0f); + SET_OR_TEST_GETTER(Brake, false); + SET_OR_TEST_GETTER(Accel, m_prev_accel/32768.0f); // Nitro still depends on whether we're accelerating - m_controls->setNitro(m_prev_nitro && m_prev_accel); + SET_OR_TEST_GETTER(Nitro, m_prev_nitro && m_prev_accel); } break; case PA_NITRO: // This basically keeps track whether the button still is being pressed - m_prev_nitro = (value != 0); + SET_OR_TEST(m_prev_nitro, value != 0 ); // Enable nitro only when also accelerating - m_controls->setNitro( ((value!=0) && m_controls->getAccel()) ); + SET_OR_TEST_GETTER(Nitro, ((value!=0) && m_controls->getAccel()) ); break; case PA_RESCUE: - m_controls->setRescue(value!=0); + SET_OR_TEST_GETTER(Rescue, value!=0); break; case PA_FIRE: - m_controls->setFire(value!=0); + SET_OR_TEST_GETTER(Fire, value!=0); break; case PA_LOOK_BACK: - m_controls->setLookBack(value!=0); + SET_OR_TEST_GETTER(LookBack, value!=0); break; case PA_DRIFT: - if(value==0) - m_controls->setSkidControl(KartControl::SC_NONE); + if (value == 0) + SET_OR_TEST_GETTER(SkidControl, KartControl::SC_NONE); else { - if(m_steer_val==0) - m_controls->setSkidControl(KartControl::SC_NO_DIRECTION); + if (m_steer_val == 0) + SET_OR_TEST_GETTER(SkidControl, KartControl::SC_NO_DIRECTION); else - m_controls->setSkidControl(m_steer_val<0 - ? KartControl::SC_RIGHT - : KartControl::SC_LEFT ); + SET_OR_TEST_GETTER(SkidControl, m_steer_val<0 + ? KartControl::SC_RIGHT + : KartControl::SC_LEFT ); } break; case PA_PAUSE_RACE: @@ -299,7 +234,10 @@ void PlayerController::action(PlayerAction action, int value) default: break; } - + if (dry_run) return false; + return true; +#undef SET_OR_TEST +#undef SET_OR_TEST_GETTER } // action //----------------------------------------------------------------------------- diff --git a/src/karts/controller/player_controller.hpp b/src/karts/controller/player_controller.hpp index 7bc6950f0..2d52e44d6 100644 --- a/src/karts/controller/player_controller.hpp +++ b/src/karts/controller/player_controller.hpp @@ -44,8 +44,8 @@ public: PlayerController(AbstractKart *kart); virtual ~PlayerController (); virtual void update (float) OVERRIDE; - virtual void action (PlayerAction action, int value) OVERRIDE; - virtual bool actionChangesState(PlayerAction action, int value); + virtual bool action (PlayerAction action, int value, + bool dry_run = false ) OVERRIDE; virtual void actionFromNetwork(PlayerAction action, int value, int value_l, int value_r); virtual void skidBonusTriggered() OVERRIDE; From 34af963cc16d13f5fe533a7d5c4de94e9d086390 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 29 May 2017 15:08:32 +1000 Subject: [PATCH 044/413] Fixed incorrect server-start-delay (caused by the start delay being delivered in a different thread, so adding DT in the main thread was not the right time step). --- src/network/protocols/server_lobby.cpp | 17 +++++++++++------ src/network/protocols/server_lobby.hpp | 5 +++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index de363695c..5f1e743b8 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -237,7 +237,6 @@ void ServerLobby::update(float dt) m_client_ready_count.getData() == m_game_setup->getPlayerCount()) { signalRaceStartToClients(); - m_server_delay = 0.1f; m_client_ready_count.getData() = 0; } m_client_ready_count.unlock(); @@ -249,9 +248,10 @@ void ServerLobby::update(float dt) // next state. break; case DELAY_SERVER: - m_server_delay -= dt; - if (m_server_delay < 0) + if (m_server_delay < StkTime::getRealTime()) { + Log::verbose("ServerLobby", "End delay at %lf", + StkTime::getRealTime()); m_state = RACING; World::getWorld()->setReadyToRace(); } @@ -350,7 +350,8 @@ void ServerLobby::registerServer() */ void ServerLobby::signalRaceStartToClients() { - Log::verbose("Server", "Signaling race start to clients"); + Log::verbose("Server", "Signaling race start to clients at %lf", + StkTime::getRealTime()); const std::vector &peers = STKHost::get()->getPeers(); NetworkString *ns = getNetworkString(1); ns->addUInt8(LE_START_RACE); @@ -935,12 +936,16 @@ void ServerLobby::finishedLoadingWorldClient(Event *event) void ServerLobby::startedRaceOnClient(Event *event) { m_client_ready_count.lock(); - Log::verbose("ServerLobby", "Host %d has started race.", - event->getPeer()->getHostId()); + Log::verbose("ServerLobby", "Host %d has started race at %lf.", + event->getPeer()->getHostId(), StkTime::getRealTime()); m_client_ready_count.getData()++; if (m_client_ready_count.getData() == m_game_setup->getPlayerCount()) { m_state = DELAY_SERVER; + m_server_delay = StkTime::getRealTime() + 0.1f; + Log::verbose("ServerLobby", "Started delay at %lf set delay to %lf", + StkTime::getRealTime(), + m_server_delay); terminateLatencyProtocol(); } m_client_ready_count.unlock(); diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 0121cc1dd..68dfea20d 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -43,8 +43,9 @@ private: /** Keeps track of an artificial server delay (which makes sure that the * data from all clients has arrived when the server computes a certain - * timestep. */ - float m_server_delay; + * timestep.(. It stores the real time since epoch + delta (atm 0.1 + * seconds), which is the real time at which the server should start. */ + double m_server_delay; Protocol *m_current_protocol; bool m_selection_enabled; From 3747427c5f582d2259e8da3f54f4d771a9cc0200 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 5 Jun 2017 08:54:52 +1000 Subject: [PATCH 045/413] ixed handling of adding states to time step info to minimise rewindw by assigning new events to existing time step info events. --- data/stk_config.xml | 11 +- src/config/stk_config.cpp | 8 +- src/config/stk_config.hpp | 6 + src/main_loop.cpp | 9 + src/network/rewind_manager.cpp | 41 +++-- src/network/rewind_manager.hpp | 1 + src/network/rewind_queue.cpp | 292 +++++++++++++++------------------ src/network/rewind_queue.hpp | 7 +- src/network/time_step_info.cpp | 11 +- src/network/time_step_info.hpp | 12 +- 10 files changed, 214 insertions(+), 184 deletions(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index b1f30d3e6..886da74de 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -114,8 +114,15 @@ - + work anymore - so for now don't enable this. + For the new networking code: + combine-threshold: Maximum time a network event can be different + from an existing time step info to be combined into one. This + reduces the number of time steps necessary (for slightly larger + inaccuracies). + --> + Server and all clients have identical information about all votes * stored in RaceConfig of GameSetup. From 40bdb4d777870c5cf12c67cd61a521fb57f51486 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 21 Nov 2017 18:15:03 +1100 Subject: [PATCH 070/413] Added #define to disable the online login for now. Makes it easier to start and debug LAN play. --- src/states_screens/main_menu_screen.cpp | 8 +++++++- src/states_screens/online_profile_servers.cpp | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/states_screens/main_menu_screen.cpp b/src/states_screens/main_menu_screen.cpp index 35523b85d..42e518dc3 100644 --- a/src/states_screens/main_menu_screen.cpp +++ b/src/states_screens/main_menu_screen.cpp @@ -513,16 +513,22 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, "\"Connect to the Internet\".")); return; } - + // Define this to require a login to the stk server (default behaviour) + // Undefine for testing LAN only. +#undef REQUIRE_LOGIN +#ifdef REQUIRE_LOGIN if (PlayerManager::getCurrentOnlineId()) +#endif { ProfileManager::get()->setVisiting(PlayerManager::getCurrentOnlineId()); OnlineProfileServers::getInstance()->push(); } +#ifdef REQUIRE_LOGIN else { UserScreen::getInstance()->push(); } +#endif } else if (selection == "addons") { diff --git a/src/states_screens/online_profile_servers.cpp b/src/states_screens/online_profile_servers.cpp index 7d3400b0f..7759bb9e8 100644 --- a/src/states_screens/online_profile_servers.cpp +++ b/src/states_screens/online_profile_servers.cpp @@ -57,6 +57,12 @@ OnlineProfileServers::OnlineProfileServers() : OnlineProfileBase("online/profile void OnlineProfileServers::loadedFromFile() { OnlineProfileBase::loadedFromFile(); + if (!PlayerManager::getCurrentOnlineId()) + { + getWidget("find_wan_server")->setActive(false); + getWidget("create_wan_server")->setActive(false); + getWidget("quick_wan_play")->setActive(false); + } } // loadedFromFile // ----------------------------------------------------------------------------- From 9b4f7737035868505f9a8f0fbec04945f5c9458d Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 22 Nov 2017 10:28:52 +1100 Subject: [PATCH 071/413] Avoid rewinds on the server (which would create a big chaos since state updated would be duplicated etc). Instead it will move 'past' events to the current time, causing only a 'jump' in the one client causing the event (instead of all). --- src/network/rewind_info.cpp | 13 +++++++++++++ src/network/rewind_info.hpp | 2 +- src/network/rewind_queue.cpp | 31 ++++++++++++++++++++++++------- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/network/rewind_info.cpp b/src/network/rewind_info.cpp index 34fcad498..49fe7a263 100644 --- a/src/network/rewind_info.cpp +++ b/src/network/rewind_info.cpp @@ -18,6 +18,7 @@ #include "network/rewind_info.hpp" +#include "network/network_config.hpp" #include "physics/physics.hpp" /** Constructor for a state: it only takes the size, and allocates a buffer @@ -30,6 +31,18 @@ RewindInfo::RewindInfo(float time, bool is_confirmed) m_is_confirmed = is_confirmed; } // RewindInfo +// ---------------------------------------------------------------------------- +/** Adjusts the time of this RewindInfo. This is only called on the server + * in case that an event is received in the past - in this case the server + * needs to avoid a Rewind by moving this event forward to the current time. + */ +void RewindInfo::setTime(float time) +{ + assert(NetworkConfig::get()->isServer()); + assert(m_time < time); + m_time = time; +} // setTime + // ============================================================================ RewindInfoState::RewindInfoState(float time, Rewinder *rewinder, BareNetworkString *buffer, bool is_confirmed) diff --git a/src/network/rewind_info.hpp b/src/network/rewind_info.hpp index 408a02309..0a43a951f 100644 --- a/src/network/rewind_info.hpp +++ b/src/network/rewind_info.hpp @@ -62,7 +62,7 @@ public: /** This is called while going forwards in time again to reach current * time. */ virtual void rewind() = 0; - + void setTime(float time); // ------------------------------------------------------------------------ virtual ~RewindInfo() { } // ------------------------------------------------------------------------ diff --git a/src/network/rewind_queue.cpp b/src/network/rewind_queue.cpp index 72b4673b5..a1e4855af 100755 --- a/src/network/rewind_queue.cpp +++ b/src/network/rewind_queue.cpp @@ -20,6 +20,7 @@ #include "config/stk_config.hpp" #include "modes/world.hpp" +#include "network/network_config.hpp" #include "network/rewind_info.hpp" #include "network/rewind_manager.hpp" #include "network/time_step_info.hpp" @@ -231,13 +232,16 @@ void RewindQueue::addNetworkState(Rewinder *rewinder, BareNetworkString *buffer, // ---------------------------------------------------------------------------- /** Merges thread-safe all data received from the network with the current * local rewind information. - * \param world_time Current world time up to which network events will be + * \param world_time[in] Current world time up to which network events will be * merged in. - * \param needs_rewind True if network rewind information was received which - * was in the past (of this simulation), so a rewind must be performed. - * \param rewind_time If needs_rewind is true, the time to which a rewind must - * be performed (at least). Otherwise undefined, but the value might - * be modified in this function. + * \param dt[in] Time step size. The current frame will cover events between + * world_time and world_time+dt. + * \param needs_rewind[out] True if network rewind information was received + * which was in the past (of this simulation), so a rewind must be + * performed. + * \param rewind_time[out] If needs_rewind is true, the time to which a rewind + * must be performed (at least). Otherwise undefined, but the value + * might be modified in this function. */ void RewindQueue::mergeNetworkData(float world_time, float dt, bool *needs_rewind, float *rewind_time) @@ -266,6 +270,19 @@ void RewindQueue::mergeNetworkData(float world_time, float dt, i++; continue; } + // A server never rewinds (otherwise we would have to handle + // duplicated states, which in the best case would then have + // a negative effect for every player, when in fact only one + // player might have a network hickup). + if (NetworkConfig::get()->isServer() && (*i)->getTime() < world_time) + { + Log::warn("RewindQueue", "At %f received message from %f", + world_time, (*i)->getTime()); + // Server received an event in the past. Adjust this event + // to be done now - at least we get a bit closer to the + // client state. + (*i)->setTime(world_time); + } // Find closest previous time step. AllTimeStepInfo::iterator prev = @@ -279,7 +296,7 @@ void RewindQueue::mergeNetworkData(float world_time, float dt, // Assign this event to the closest of the two existing timesteps // prev and next (inserting an additional event in the past would - // mean more CPU work in the rewind this will very likely trigger. + // mean more CPU work in the rewind this will very likely trigger). if (next == m_time_step_info.end()) tsi = *prev; else if ( (*next)->getTime()-event_time < event_time-(*prev)->getTime() ) From 7508b5db8bbbbf9a76ec717f5b15b119ac01559c Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 24 Nov 2017 18:10:55 +1100 Subject: [PATCH 072/413] Fixed index in debug output and compiler warning. --- src/karts/kart.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 1e33f9131..7790b2d9a 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1413,7 +1413,9 @@ void Kart::update(float dt) m_body->getBroadphaseHandle()->m_collisionFilterGroup = 0; } #ifdef XX - Log::verbose("physicsafter", "%s t %f %f xyz(9-11) %f %f %f %f %f %f v(13-15) %f %f %f steerf(17) %f maxangle(19) %f speed(21) %f steering(23-24) %f %f clock %lf", + Log::verbose("physicsafter", "%s t %f %f xyz(9-11) %f %f %f %f %f %f " + "v(16-18) %f %f %f steerf(20) %f maxangle(22) %f speed(24) %f " + "steering(26-27) %f %f clock(29) %lf", getIdent().c_str(), World::getWorld()->getTime(), dt, getXYZ().getX(), getXYZ().getY(), getXYZ().getZ(), @@ -2195,7 +2197,7 @@ void Kart::playCrashSFX(const Material* m, AbstractKart *k) { const float speed_for_max_volume = 15; //The speed at which the sound plays at maximum volume const float max_volume = 1; //The maximum volume a sound is played at - const float min_volume = 0.2; //The minimum volume a sound is played at + const float min_volume = 0.2f; //The minimum volume a sound is played at float volume; //The volume the crash sound will be played at From 37ee602f283dadf76e0b961161b5c5cb85b67de6 Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 24 Nov 2017 18:15:39 +1100 Subject: [PATCH 073/413] Added state type to debug output. --- src/network/rewind_queue.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/network/rewind_queue.cpp b/src/network/rewind_queue.cpp index a1e4855af..bee9b2b8c 100755 --- a/src/network/rewind_queue.cpp +++ b/src/network/rewind_queue.cpp @@ -305,9 +305,10 @@ void RewindQueue::mergeNetworkData(float world_time, float dt, tsi = *prev; tsi->insert(*i); - - Log::info("Rewind", "Inserting event from time %f to timstepinfo %f prev %f next %f", - (*i)->getTime(), tsi->getTime(), + Log::info("Rewind", "Inserting event from time %f type %c to timstepinfo %f prev %f next %f", + (*i)->getTime(), + (*i)->isEvent() ? 'E' : ((*i)->isState() ? 'S' : 'T'), + tsi->getTime(), (*prev)->getTime(), next != m_time_step_info.end() ? (*next)->getTime() : 9999 ); From b08e2f56e18d2acea673d4566b0b1d65258799aa Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 24 Nov 2017 18:33:26 +1100 Subject: [PATCH 074/413] Make the number of state updated the server sends configurable. --- data/stk_config.xml | 12 +++--------- src/config/stk_config.cpp | 8 +++----- src/config/stk_config.hpp | 7 ++----- src/network/rewind_manager.cpp | 2 +- 4 files changed, 9 insertions(+), 20 deletions(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index a53cd2577..f5d0dc555 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -158,16 +158,10 @@ away if there is an explosion. --> - - + - + simulation + is [T, T+DT] with T=now. + RewindManager::addNextTimeStep() : Adds a default TimeStepInfo entry to the + RewindQueue which will store events for the + current time step (e.g. key presses, and + network data). + irr_driver::update() : Rendering and input handling. + Controller::action() : Store user action in m_controls of + kart. Clients send event to server. + RaceEventManager::update() : A thin wrapper around world used in networked + races. + RewindManager::playEventsTill() : Plays all events in [T, T+DT]: copies unhandled + network events that must be handled at the + current time to the current TimeStepInfo. Can do + complete rewind and replay till T is reached again! + World::updateWorld() + RewindManager::update() : Store current state on server if necessary and + broadcast it to clients. + Karts::update() + Moveable::update() : Copy physics data from bullet to STK. + updateSpeed() : Get physics speed and set it in kart. + Controller::update() : Set kart steering based on user/AI input. + Slipstream::update() : call Kart::handleZipper if required. + updatePhysics() + HandleStartBoost : Trigger boost if required. + updateEnginePower...() : Sets engine power/brakes for bullet vehicle. + Skidding::update() : Update skidding values (which will + affect steering). + setSteering : Sets the bullet steering based on + kart's current steering. + updateSliding() : Test for sliding which can reduce the wheels + friction/grip, causing the physics to slide. + MaxSpeed::update() : Cap speed of kart if kart is too fast. + !physicsafter : !Print debug values + Physics::update() : Time step bullet as often as necessary. This is + using the steering etc information set above. + ProtocolManager::update() : Synchronous protocol updates. + World::updateTime() : Increase time from T to T+DT. \ No newline at end of file From ecbb1db2c953f90c2a60206f75f85f46d3a485f8 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 24 Jan 2018 08:39:10 +1100 Subject: [PATCH 097/413] Flush buffer in case of a disconnect, which allows the server to use buffered logging, which will be written when the client disappears. --- src/network/stk_host.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index 6ac01f207..f7ddb7c65 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -562,6 +562,7 @@ void* STKHost::mainLoop(void* self) else if (stk_event->getType() == EVENT_TYPE_DISCONNECTED) { Log::info("STKHost", "A client has just disconnected."); + Log::flushBuffers(); } // EVENT_TYPE_CONNECTED else if (stk_event->getType() == EVENT_TYPE_MESSAGE) { From 226ff0cea21b49732910b6859f774b5804c4f242 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 24 Jan 2018 12:38:26 +1100 Subject: [PATCH 098/413] Added simple script to extract useful data from a client and server run. --- tools/network_race_statistics | 70 +++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100755 tools/network_race_statistics diff --git a/tools/network_race_statistics b/tools/network_race_statistics new file mode 100755 index 000000000..6cf71cc0f --- /dev/null +++ b/tools/network_race_statistics @@ -0,0 +1,70 @@ +#!/bin/bash + +# Start an STK client with: +# --log=0 --stdout=client --online --logbuffer=100000 +# Add "--history=2" if you want to use a replay. +# Start a server with: +# --log=0 --stdout=server --lan-server=lan-server --no-graphics --online --logbuffer=100000 +# Remove --no-graphics if you also want to see the graphics on the server + +# Then do a race, and stop/abort on the client. Wait a long time for +# the log file to be flushed!!! The server will trigger flushing its +# log buffer when the client disconnects as well. So wait for both +# 'server' and 'client' files to stay unchanged. + +# First extract the phsyicsafter lines, which contain physics +# information to evaluate client/server consistency. + +cat server | grep physicsafter > xx.s +cat client | grep physicsafter > xx.c + +# Each log line done during a rewind will start with 'Rewind '. The +# space at the beginning changes the column numbers used in gnuplot +# for each field (as a result rewind lines are not plotted by default) +# Removing the space means that rewind data will be plotted as well +cat xx.c | sed 's/Rewind /Rewind/' >xx.cc + +# Now compute the error per client timestep. The parametrs are: +# -f which fields to use: first field is the world time, then one +# or more fields. The script will compute for each client frame the +# earliest immediate previous and next data from the server at the +# client time. Based on those two data points it will interpolate +# the data of the server at the client time, and compute the +# distance between client and server. + +# For column numbers check the xx.s files: each name contains the +# column numbers in (), e.g.: +# xyz(9-11) 0.1 0.2 0.3 +# This indicates that the column 9-11 in the file are the xyz position +# It saves column counting if the heading is kept up to date + +# Comparison of (physical) position used in STK: +~/stk-code/tools/compute_client_error.py -f 6,9,10,11 xx.s xx.cc >pos + +# Comparison of physical position at the end of the last full +# bullet time step (i.e. multple of 1/120). +~/stk-code/tools/compute_client_error.py -f 6,12,13,14 xx.s xx.cc >phys-pos + +# Comparison of velocity +~/stk-code/tools/compute_client_error.py -f 6,16,17,18 xx.s xx.cc >v + +# Comparison of steering +~/stk-code/tools/compute_client_error.py -f 6,20 xx.s xx.cc >steering + +# Useful gnuplot commands: +# Plot the path taken for client and server (use xx.c instead of xx.cc not +# remove rewinds): +# plot "xx.cc" u 9:11 w lp lw 2, "xx.s" u 9:11 w lp, "recorded/xx.c" u 9:11 w lp +# Plot steering values used: +# a=20; plot "xx.cc" u 6:a w lp lw 2, "xx.s" u 6:a w lp, "recorded/xx.c" u 6:a w lp +# Change a=XX if you want to display a different value +# +# It can be useful to plot the time step size: +# plot "xx.s" u 6:7 w lp, "xx.cc" u 6:7 w lp +# and also to check that the game time is in sync between client and server: +# Field 29 is the real time clock, so they can be compared if you are running +# on the same machine. So this shows what the game clock is at a given real +# time. Note that the client must be somewhat ahead of the server! +# plot "xx.cc" u 29:6 w lp lw 2, "xx.s" u 29:6 w lp + + From e9f9302c2feb84ce862b7049142fd4ba6ef1dc3c Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 24 Jan 2018 12:38:54 +1100 Subject: [PATCH 099/413] Added simple script to extract only the data rendered on the client. --- tools/get_rendered_data | 3 +++ 1 file changed, 3 insertions(+) create mode 100755 tools/get_rendered_data diff --git a/tools/get_rendered_data b/tools/get_rendered_data new file mode 100755 index 000000000..9bd46a1f7 --- /dev/null +++ b/tools/get_rendered_data @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -a "^\[verbose \] physicsafter" $1 \ No newline at end of file From 9ebcdc5af3c80d818db4fdfff7023d4c37b85677 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 29 Jan 2018 14:50:29 +0800 Subject: [PATCH 100/413] Set DeviceManager assign more to ASSIGN in network kart selection Because input manager use this to determine backspace functionality (for rescue in game or leave a screen) This will be reset to NO_ASSIGN when you go back to main menu screen --- src/states_screens/network_kart_selection.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/states_screens/network_kart_selection.cpp b/src/states_screens/network_kart_selection.cpp index 1b71912f0..c160c4287 100644 --- a/src/states_screens/network_kart_selection.cpp +++ b/src/states_screens/network_kart_selection.cpp @@ -23,6 +23,7 @@ #include "config/user_config.hpp" #include "graphics/irr_driver.hpp" #include "guiengine/widgets/player_kart_widget.hpp" +#include "input/device_manager.hpp" #include "items/item_manager.hpp" #include "karts/kart_properties.hpp" #include "karts/kart_properties_manager.hpp" @@ -157,6 +158,7 @@ void NetworkKartSelectionScreen::playerConfirm(const int playerID) clrp->requestKartSelection(players[i]->getGlobalPlayerId(), selection ); } + input_manager->getDeviceManager()->setAssignMode(ASSIGN); } } // playerConfirm From 59f28d3746dd55957283427708690a31ce054e60 Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 2 Feb 2018 09:16:34 +1100 Subject: [PATCH 101/413] Fixed line ending style. --- src/race/history.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/race/history.cpp b/src/race/history.cpp index 7133818a6..357a2390b 100644 --- a/src/race/history.cpp +++ b/src/race/history.cpp @@ -147,8 +147,8 @@ float History::updateReplayAndGetDT(float world_time, float dt) { const InputEvent &ie = m_all_input_events[m_event_index]; AbstractKart *kart = world->getKart(ie.m_kart_index); - Log::verbose("history", "time %f event-time %f action %d %d", - world->getTime(), ie.m_time, ie.m_action, ie.m_value); + Log::verbose("history", "time %f event-time %f action %d %d", + world->getTime(), ie.m_time, ie.m_action, ie.m_value); kart->getController()->action(ie.m_action, ie.m_value); m_event_index++; } @@ -157,14 +157,14 @@ float History::updateReplayAndGetDT(float world_time, float dt) // Now handle the non-networking case // ---------------------------------- - Log::verbose("history", "Begin %f %f %f %f current %d event %d", - m_history_time, world_time, dt, world_time + dt, - m_current, m_event_index); + Log::verbose("history", "Begin %f %f %f %f current %d event %d", + m_history_time, world_time, dt, world_time + dt, + m_current, m_event_index); m_current++; - Log::verbose("history", "Inner %f %f %f %f current %d event %d", - m_history_time, world_time, dt, world_time + dt, - m_current, m_event_index); + Log::verbose("history", "Inner %f %f %f %f current %d event %d", + m_history_time, world_time, dt, world_time + dt, + m_current, m_event_index); // Check if we have reached the end of the buffer if (m_current >= (int)m_all_deltas.size()) @@ -202,9 +202,9 @@ float History::updateReplayAndGetDT(float world_time, float dt) { const InputEvent &ie = m_all_input_events[m_event_index]; AbstractKart *kart = world->getKart(ie.m_kart_index); - Log::verbose("history", "time %f event-time %f action %d %d", - world->getTime(), ie.m_time, ie.m_action, ie.m_value); - kart->getController()->action(ie.m_action, ie.m_value); + Log::verbose("history", "time %f event-time %f action %d %d", + world->getTime(), ie.m_time, ie.m_action, ie.m_value); + kart->getController()->action(ie.m_action, ie.m_value); m_event_index++; } From 4d75042f3b4b5cdcc74410ea695aafda890cebeb Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 9 Feb 2018 16:12:44 +1100 Subject: [PATCH 102/413] Fixed compiler warnings. --- src/states_screens/race_gui_overworld.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/states_screens/race_gui_overworld.cpp b/src/states_screens/race_gui_overworld.cpp index 7faa0e5c7..b45dfc909 100644 --- a/src/states_screens/race_gui_overworld.cpp +++ b/src/states_screens/race_gui_overworld.cpp @@ -182,9 +182,10 @@ void RaceGUIOverworld::renderGlobal(float dt) if (race_manager->getIfEmptyScreenSpaceExists() && !GUIEngine::ModalDialog::isADialogActive()) { - const float Sqrt = sqrt(race_manager->getNumLocalPlayers()); - const int rows = ceil(Sqrt); - const int cols = round(Sqrt); + const float sqrt_num_players = + sqrtf((float)race_manager->getNumLocalPlayers()); + const int rows = (int)ceil(sqrt_num_players); + const int cols = (int)round(sqrt_num_players); static video::SColor black = video::SColor(255,0,0,0); GL32_draw2DRectangle(black, irr_driver->getSplitscreenWindow( race_manager->getNumLocalPlayers())); From 366313d29d04a1901cca305d4e511068823402f5 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 9 Feb 2018 17:59:48 +0800 Subject: [PATCH 103/413] Avoid an unknown value from attachment rewind --- src/items/attachment.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/items/attachment.cpp b/src/items/attachment.cpp index 3629c6c7c..3d97cc604 100644 --- a/src/items/attachment.cpp +++ b/src/items/attachment.cpp @@ -269,7 +269,13 @@ void Attachment::saveState(BareNetworkString *buffer) const void Attachment::rewindTo(BareNetworkString *buffer) { uint8_t type = buffer->getUInt8(); + AttachmentType new_type = AttachmentType(type & 0x7f); // mask out bit 7 + // FIXME Sometimes type == 255 is returned, reason unknown + if (new_type > ATTACH_NOTHING) + { + return; + } // If there is no attachment, clear the attachment if necessary and exit if(new_type==ATTACH_NOTHING) From 3290321c5db2f45f52ebdf487e5dcccde4157184 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 9 Feb 2018 18:00:19 +0800 Subject: [PATCH 104/413] PlayerController can become EndController anytime --- src/network/protocols/game_protocol.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/network/protocols/game_protocol.cpp b/src/network/protocols/game_protocol.cpp index d02299307..a4aeaa028 100644 --- a/src/network/protocols/game_protocol.cpp +++ b/src/network/protocols/game_protocol.cpp @@ -305,7 +305,8 @@ void GameProtocol::rewind(BareNetworkString *buffer) int value_r = buffer->getUInt32(); Controller *c = World::getWorld()->getKart(kart_id)->getController(); PlayerController *pc = dynamic_cast(c); - assert(pc); + // FIXME this can be endcontroller when finishing the race + //assert(pc); if(pc) pc->actionFromNetwork(action, value, value_l, value_r); } // rewind From 52e933021275bd942c2a49e59cddae11ee650598 Mon Sep 17 00:00:00 2001 From: hiker Date: Sat, 10 Feb 2018 17:34:10 +1100 Subject: [PATCH 105/413] Removed debug output. --- src/modes/world_status.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index b36b96d55..8fcefd7e0 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -182,8 +182,6 @@ void WorldStatus::update(float dt) */ void WorldStatus::updateTime(const float dt) { - Log::verbose("updateTime", "phase %d time %f dt %f aux %f", - m_phase, m_time, dt, m_auxiliary_timer); switch (m_phase) { // Note: setup phase must be a separate phase, since the race_manager From 4f54fb7898b5c90d4b0d12a7e042f5de939d351c Mon Sep 17 00:00:00 2001 From: hiker Date: Sat, 10 Feb 2018 17:51:12 +1100 Subject: [PATCH 106/413] Avoid crash in history replay (dirty workaround), --- src/network/rewind_queue.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/network/rewind_queue.cpp b/src/network/rewind_queue.cpp index a9d80b340..f6a7ebb7c 100755 --- a/src/network/rewind_queue.cpp +++ b/src/network/rewind_queue.cpp @@ -154,7 +154,14 @@ void RewindQueue::insertRewindInfo(RewindInfo *ri) { // FIXME: this should always be the last element in the list(??) AllTimeStepInfo::iterator bucket = findPreviousTimeStepInfo(ri->getTime()); - (*bucket)->insert(ri); + + // FIXME: In case of a history replay an element could be inserted in the + // very first frame (on very quick recorded start, and if the first frame + // takes a long time - e.g. in networking startup), i.e. before a TimeStep + // info was added. Since this is mostly for debugging, just ignore this + // this for now. + if(bucket!=m_time_step_info.end())) + (*bucket)->insert(ri); } // insertRewindInfo // ---------------------------------------------------------------------------- From 37d81be0331e530e19398b3208d9fe7c3f73fb23 Mon Sep 17 00:00:00 2001 From: hiker Date: Sat, 10 Feb 2018 17:55:45 +1100 Subject: [PATCH 107/413] Made the physics time step size configurable in the config file. --- data/stk_config.xml | 2 ++ src/config/stk_config.cpp | 6 +++++- src/config/stk_config.hpp | 3 +++ src/physics/physics.cpp | 3 ++- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index c38fa8387..9d60c3835 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -92,6 +92,7 @@ case (all three normals discarded, the interpolation will just return the normal of the triangle (i.e. de facto no interpolation), but it helps making smoothing much more useful without fixing tracks. + fps: The physics timestep size default-track-friction: Default friction to be used for the track and any track/library pbject. default-moveable-friction: Default friction to be used for any moveable, @@ -99,6 +100,7 @@ --> diff --git a/src/config/stk_config.cpp b/src/config/stk_config.cpp index ba27297ef..736e32ab2 100644 --- a/src/config/stk_config.cpp +++ b/src/config/stk_config.cpp @@ -148,7 +148,9 @@ void STKConfig::load(const std::string &filename) CHECK_NEG(m_replay_dt, "replay delta-t" ); CHECK_NEG(m_smooth_angle_limit, "physics smooth-angle-limit" ); CHECK_NEG(m_default_track_friction, "physics default-track-friction"); - CHECK_NEG(m_network_state_frequeny, "network state-frequency"); + CHECK_NEG(m_physics_fps, "physics fps" ); + CHECK_NEG(m_network_state_frequeny, "network state-frequency" ); + CHECK_NEG(m_network_state_frequeny, "network state-frequency" ); CHECK_NEG(m_default_moveable_friction, "physics default-moveable-friction"); // Square distance to make distance checks cheaper (no sqrt) @@ -170,6 +172,7 @@ void STKConfig::init_defaults() m_smooth_angle_limit = m_penalty_time = m_default_track_friction = m_default_moveable_friction = UNDEFINED; + m_physics_fps = -100; m_bubblegum_counter = -100; m_shield_restrict_weapos = false; m_max_karts = -100; @@ -259,6 +262,7 @@ void STKConfig::getAllData(const XMLNode * root) physics_node->get("default-track-friction", &m_default_track_friction); physics_node->get("default-moveable-friction", &m_default_moveable_friction); + physics_node->get("fps", &m_physics_fps ); } if (const XMLNode *startup_node= root->getNode("startup")) diff --git a/src/config/stk_config.hpp b/src/config/stk_config.hpp index bffe4bef4..2c7cd1154 100644 --- a/src/config/stk_config.hpp +++ b/src/config/stk_config.hpp @@ -105,6 +105,9 @@ public: /** Default friction to be used for any moveable, e.g. karts, balls. */ float m_default_moveable_friction; + /** Default FPS rate for physics. */ + int m_physics_fps; + int m_max_skidmarks; /**stepSimulation(dt, max_num_steps, 1.0f/120.0f); + m_dynamics_world->stepSimulation(dt, max_num_steps, + 1.0f/stk_config->m_physics_fps); // Now handle the actual collision. Note: flyables can not be removed // inside of this loop, since the same flyables might hit more than one From 46416781faa7e12b5de81fa69888ebee4436c596 Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 13 Feb 2018 15:20:55 +0800 Subject: [PATCH 108/413] Make it possible to restart race after going back to lobby At the moment the token is reset by client after each restart --- src/network/protocols/client_lobby.cpp | 4 ++++ src/network/protocols/server_lobby.cpp | 1 + src/network/stk_host.cpp | 12 ++++++++++++ src/network/stk_host.hpp | 1 + 4 files changed, 18 insertions(+) diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index f13a29f70..cb691dfdc 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -712,6 +712,10 @@ void ClientLobby::raceFinished(Event* event) void ClientLobby::exitResultScreen(Event *event) { RaceResultGUI::getInstance()->backToLobby(); + // Will be reset to linked if connected to server, see update(float dt) + m_game_setup = STKHost::get()->setupNewGame(); + STKHost::get()->getServerPeerForClient()->unsetClientServerToken(); + m_state = NONE; } // exitResultScreen //----------------------------------------------------------------------------- diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 5f1e743b8..11b7eda64 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -283,6 +283,7 @@ void ServerLobby::update(float dt) pm->findAndTerminate(PROTOCOL_CONTROLLER_EVENTS); pm->findAndTerminate(PROTOCOL_KART_UPDATE); pm->findAndTerminate(PROTOCOL_GAME_EVENTS); + setup(); } break; case DONE: diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index f7ddb7c65..0b6f12d54 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -710,6 +710,18 @@ STKPeer* STKHost::getPeer(ENetPeer *enet_peer) m_next_unique_host_id ++; return peer; } // getPeer + +// ---------------------------------------------------------------------------- +/** \brief Return the only server peer for client. + * \return STKPeer the STKPeer of server. + */ +STKPeer* STKHost::getServerPeerForClient() const +{ + assert(m_peers.size() == 1); + assert(NetworkConfig::get()->isClient()); + return m_peers[0]; +} // getServerPeerForClient + // ---------------------------------------------------------------------------- /** \brief Tells if a peer is known and connected. * \return True if the peer is known and connected, false elseway. diff --git a/src/network/stk_host.hpp b/src/network/stk_host.hpp index ae1be86e0..92d109732 100644 --- a/src/network/stk_host.hpp +++ b/src/network/stk_host.hpp @@ -162,6 +162,7 @@ public: void removePeer(const STKPeer* peer); bool isConnectedTo(const TransportAddress& peer_address); STKPeer *getPeer(ENetPeer *enet_peer); + STKPeer *getServerPeerForClient() const; std::vector getMyPlayerProfiles(); int mustStopListening(); uint16_t getPort() const; From 8a1ef31f4ad0f4707240e8d1dd19566a037f06a6 Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 14 Feb 2018 01:57:05 +0800 Subject: [PATCH 109/413] Allow remove unusable karts or tracks in network game --- src/network/protocols/client_lobby.cpp | 35 +++++++ src/network/protocols/client_lobby.hpp | 8 ++ src/network/protocols/server_lobby.cpp | 97 ++++++++++++++++++- src/network/protocols/server_lobby.hpp | 7 ++ src/states_screens/ghost_replay_selection.cpp | 1 - src/states_screens/kart_selection.cpp | 4 +- src/states_screens/kart_selection.hpp | 2 + src/states_screens/network_kart_selection.cpp | 1 - src/states_screens/network_kart_selection.hpp | 14 +++ src/states_screens/tracks_screen.cpp | 29 +++--- src/states_screens/tracks_screen.hpp | 4 - 11 files changed, 177 insertions(+), 25 deletions(-) diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index cb691dfdc..6c2e1af68 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -19,6 +19,7 @@ #include "network/protocols/client_lobby.hpp" #include "config/player_manager.hpp" +#include "karts/kart_properties_manager.hpp" #include "modes/world_with_rank.hpp" #include "network/event.hpp" #include "network/network_config.hpp" @@ -33,6 +34,7 @@ #include "states_screens/network_kart_selection.hpp" #include "states_screens/race_result_gui.hpp" #include "states_screens/state_manager.hpp" +#include "tracks/track_manager.hpp" #include "utils/log.hpp" // ============================================================================ @@ -321,6 +323,27 @@ void ClientLobby::update(float dt) // 4 (size of id), global id ns->addUInt8(LE_CONNECTION_REQUESTED).encodeString(name) .encodeString(NetworkConfig::get()->getPassword()); + + auto all_k = kart_properties_manager->getAllAvailableKarts(); + auto all_t = track_manager->getAllTrackIdentifiers(); + if (all_k.size() > 65536) + { + all_k.resize(65535); + } + if (all_t.size() > 65536) + { + all_t.resize(65535); + } + ns->addUInt16((uint16_t)all_k.size()).addUInt16((uint16_t)all_t.size()); + for (const std::string& kart : all_k) + { + ns->encodeString(kart); + } + for (const std::string& track : all_t) + { + ns->encodeString(track); + } + sendToServer(ns); delete ns; m_state = REQUESTING_CONNECTION; @@ -334,6 +357,7 @@ void ClientLobby::update(float dt) { NetworkKartSelectionScreen* screen = NetworkKartSelectionScreen::getInstance(); + screen->setAvailableKartsFromServer(m_avaliable_karts); screen->push(); m_state = SELECTING_KARTS; @@ -658,6 +682,17 @@ void ClientLobby::startingRaceNow() void ClientLobby::startSelection(Event* event) { m_state = KART_SELECTION; + const NetworkString& data = event->data(); + m_avaliable_karts.resize(data.getUInt16()); + m_avaliable_tracks.resize(data.getUInt16()); + for (std::string& kart : m_avaliable_karts) + { + data.decodeString(&kart); + } + for (std::string& track : m_avaliable_tracks) + { + data.decodeString(&track); + } Log::info("ClientLobby", "Kart selection starts now"); } // startSelection diff --git a/src/network/protocols/client_lobby.hpp b/src/network/protocols/client_lobby.hpp index 39edbd131..da28d1b75 100644 --- a/src/network/protocols/client_lobby.hpp +++ b/src/network/protocols/client_lobby.hpp @@ -49,6 +49,9 @@ private: /** The state of the finite state machine. */ STATE m_state; + std::vector m_avaliable_karts; + std::vector m_avaliable_tracks; + public: ClientLobby(); virtual ~ClientLobby(); @@ -67,6 +70,11 @@ public: void startingRaceNow(); void leave(); + const std::vector& getAvaliableKarts() const + { return m_avaliable_karts; } + const std::vector& getAvaliableTracks() const + { return m_avaliable_tracks; } + virtual bool notifyEvent(Event* event) OVERRIDE; virtual bool notifyEventAsynchronous(Event* event) OVERRIDE; virtual void finishedLoadingWorld() OVERRIDE; diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 11b7eda64..2e5bd4edf 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -20,6 +20,7 @@ #include "config/player_manager.hpp" #include "config/user_config.hpp" +#include "karts/kart_properties_manager.hpp" #include "modes/world.hpp" #include "network/event.hpp" #include "network/network_config.hpp" @@ -36,11 +37,11 @@ #include "states_screens/networking_lobby.hpp" #include "states_screens/race_result_gui.hpp" #include "states_screens/waiting_for_others.hpp" +#include "tracks/track_manager.hpp" #include "utils/log.hpp" #include "utils/random_generator.hpp" #include "utils/time.hpp" - /** This is the central game setup protocol running in the server. It is * mostly a finite state machine. Note that all nodes in ellipses and light * grey background are actual states; nodes in boxes and white background @@ -48,9 +49,9 @@ * change. \dot digraph interaction { - node [shape=box]; "Server Constructor"; "playerTrackVote"; "connectionRequested"; - "signalRaceStartToClients"; "startedRaceOnClient"; "loadWorld"; - node [shape=ellipse,style=filled,color=lightgrey]; + node [shape=box]; "Server Constructor"; "playerTrackVote"; "connectionRequested"; + "signalRaceStartToClients"; "startedRaceOnClient"; "loadWorld"; + node [shape=ellipse,style=filled,color=lightgrey]; "Server Constructor" -> "INIT_WAN" [label="If WAN game"] "Server Constructor" -> "ACCEPTING_CLIENTS" [label="If LAN game"] @@ -83,6 +84,19 @@ ServerLobby::ServerLobby() : LobbyProtocol(NULL) { setHandleDisconnections(true); + // We use maximum 16bit unsigned limit + auto all_k = kart_properties_manager->getAllAvailableKarts(); + auto all_t = track_manager->getAllTrackIdentifiers(); + if (all_k.size() > 65536) + { + all_k.resize(65535); + } + if (all_t.size() > 65536) + { + all_t.resize(65535); + } + m_available_kts.getData().first = { all_k.begin(), all_k.end() }; + m_available_kts.getData().second = { all_t.begin(), all_t.end() }; } // ServerLobby //----------------------------------------------------------------------------- @@ -387,6 +401,20 @@ void ServerLobby::startSelection(const Event *event) // a new screen, which must be donefrom the main thread. ns->setSynchronous(true); ns->addUInt8(LE_START_SELECTION); + m_available_kts.lock(); + const auto& all_k = m_available_kts.getData().first; + const auto& all_t = m_available_kts.getData().second; + ns->addUInt16((uint16_t)all_k.size()).addUInt16((uint16_t)all_t.size()); + for (const std::string& kart : all_k) + { + ns->encodeString(kart); + } + for (const std::string& track : all_t) + { + ns->encodeString(track); + } + m_available_kts.unlock(); + sendMessageToPeersChangingToken(ns, /*reliable*/true); delete ns; @@ -564,12 +592,71 @@ void ServerLobby::connectionRequested(Event* event) // Connection accepted. // ==================== std::string name_u8; - int len = data.decodeString(&name_u8); + data.decodeString(&name_u8); core::stringw name = StringUtils::utf8ToWide(name_u8); std::string password; data.decodeString(&password); bool is_authorised = (password==NetworkConfig::get()->getPassword()); + std::vector client_karts_v, client_tracks_v; + client_karts_v.resize(data.getUInt16()); + client_tracks_v.resize(data.getUInt16()); + for (std::string& kart : client_karts_v) + { + data.decodeString(&kart); + } + for (std::string& track : client_tracks_v) + { + data.decodeString(&track); + } + std::set client_karts(client_karts_v.begin(), + client_karts_v.end()); + std::set client_tracks(client_tracks_v.begin(), + client_tracks_v.end()); + + // Remove karts or tracks that do not exist in either client or server + m_available_kts.lock(); + std::vector karts_erase, tracks_erase; + for (const std::string& client_kart : client_karts) + { + if (m_available_kts.getData().first.find(client_kart) == + m_available_kts.getData().first.end()) + { + karts_erase.push_back(client_kart); + } + } + for (const std::string& server_kart : m_available_kts.getData().first) + { + if (client_karts.find(server_kart) == client_karts.end()) + { + karts_erase.push_back(server_kart); + } + } + for (const std::string& client_track : client_tracks) + { + if (m_available_kts.getData().second.find(client_track) == + m_available_kts.getData().second.end()) + { + tracks_erase.push_back(client_track); + } + } + for (const std::string& server_track : m_available_kts.getData().second) + { + if (client_tracks.find(server_track) == client_tracks.end()) + { + tracks_erase.push_back(server_track); + } + } + for (const std::string& kart_erase : karts_erase) + { + m_available_kts.getData().first.erase(kart_erase); + } + for (const std::string& track_erase : tracks_erase) + { + m_available_kts.getData().second.erase(track_erase); + } + m_available_kts.unlock(); + // Get the unique global ID for this player. m_next_player_id.lock(); m_next_player_id.getData()++; diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 68dfea20d..744aefae3 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -5,6 +5,8 @@ #include "utils/cpp2011.hpp" #include "utils/synchronised.hpp" +#include + class ServerLobby : public LobbyProtocol , public CallbackObject { @@ -27,6 +29,11 @@ private: EXITING } m_state; + /** Available karts and tracks for all clients, this will be initialized + * with data in server first. */ + Synchronised, + std::set > > m_available_kts; + /** Next id to assign to a peer. */ Synchronised m_next_player_id; diff --git a/src/states_screens/ghost_replay_selection.cpp b/src/states_screens/ghost_replay_selection.cpp index 3205ce918..113612a73 100644 --- a/src/states_screens/ghost_replay_selection.cpp +++ b/src/states_screens/ghost_replay_selection.cpp @@ -163,7 +163,6 @@ void GhostReplaySelection::eventCallback(GUIEngine::Widget* widget, else if (name == "record-ghost") { race_manager->setRecordRace(true); - TracksScreen::getInstance()->setOfficalTrack(false); TracksScreen::getInstance()->push(); } else if (name == "replay_difficulty_toggle") diff --git a/src/states_screens/kart_selection.cpp b/src/states_screens/kart_selection.cpp index a6b9f9a61..22cfcc38e 100644 --- a/src/states_screens/kart_selection.cpp +++ b/src/states_screens/kart_selection.cpp @@ -1470,8 +1470,8 @@ void KartSelectionScreen::setKartsFromCurrentGroup() { const KartProperties* prop = kart_properties_manager->getKartById(i); // Ignore karts that are not in the selected group - if(selected_kart_group != ALL_KART_GROUPS_ID && - !prop->isInGroup(selected_kart_group)) + if((selected_kart_group != ALL_KART_GROUPS_ID && + !prop->isInGroup(selected_kart_group)) || isIgnored(prop->getIdent())) continue; karts.push_back(prop); } diff --git a/src/states_screens/kart_selection.hpp b/src/states_screens/kart_selection.hpp index c66483f67..7d21621e1 100644 --- a/src/states_screens/kart_selection.hpp +++ b/src/states_screens/kart_selection.hpp @@ -123,6 +123,8 @@ protected: /** Remove the multiplayer message. */ void removeMultiplayerMessage(); + virtual bool isIgnored(const std::string& ident) const { return false; } + /** Stores a pointer to the current selection screen */ static KartSelectionScreen* m_instance_ptr; public: diff --git a/src/states_screens/network_kart_selection.cpp b/src/states_screens/network_kart_selection.cpp index c160c4287..53f87aac2 100644 --- a/src/states_screens/network_kart_selection.cpp +++ b/src/states_screens/network_kart_selection.cpp @@ -219,7 +219,6 @@ void NetworkKartSelectionScreen::playerSelected(uint8_t player_id, //WaitingForOthersScreen::getInstance()->push(); //return; } - TracksScreen::getInstance()->setOfficalTrack(true); TracksScreen::getInstance()->push(); } // playerSelected diff --git a/src/states_screens/network_kart_selection.hpp b/src/states_screens/network_kart_selection.hpp index 8c7777e56..9c8021a7d 100644 --- a/src/states_screens/network_kart_selection.hpp +++ b/src/states_screens/network_kart_selection.hpp @@ -21,6 +21,8 @@ #include "states_screens/kart_selection.hpp" #include "guiengine/screen.hpp" +#include + class NetworkKartSelectionScreen : public KartSelectionScreen, public GUIEngine::ScreenSingleton { @@ -33,7 +35,19 @@ protected: virtual ~NetworkKartSelectionScreen(); virtual void playerConfirm(const int playerID) OVERRIDE; + +private: + std::set m_avaliable_karts; + virtual bool isIgnored(const std::string& ident) const OVERRIDE + { + return m_avaliable_karts.find(ident) == m_avaliable_karts.end(); + } + public: + void setAvailableKartsFromServer(const std::vector& k) + { + m_avaliable_karts = { k.begin(), k.end() }; + } virtual void init() OVERRIDE; virtual bool onEscapePressed() OVERRIDE; virtual void playerSelected(uint8_t player_id, diff --git a/src/states_screens/tracks_screen.cpp b/src/states_screens/tracks_screen.cpp index 50107729f..8a26fcc5e 100644 --- a/src/states_screens/tracks_screen.cpp +++ b/src/states_screens/tracks_screen.cpp @@ -195,19 +195,20 @@ void TracksScreen::buildTrackList() m_random_track_list.clear(); const std::string& curr_group_name = tabs->getSelectionIDString(0); - - if (!(curr_group_name == DEFAULT_GROUP_NAME || - curr_group_name == ALL_TRACK_GROUPS_ID) && m_offical_track) - { - tracks_widget->setText(_("Only official tracks are supported.")); - tracks_widget->updateItemDisplay(); - return; - } - const int track_amount = (int)track_manager->getNumberOfTracks(); // First build a list of all tracks to be displayed // (e.g. exclude arenas, ...) + bool is_network = (STKHost::existHost()); + std::set m_available_tracks_from_server; + if (is_network) + { + Protocol* protocol = LobbyProtocol::get(); + ClientLobby* clrp = dynamic_cast(protocol); + assert(clrp); + m_available_tracks_from_server = + { clrp->getAvaliableTracks().begin(), clrp->getAvaliableTracks().end() }; + } PtrVector tracks; for (int n = 0; n < track_amount; n++) { @@ -216,14 +217,18 @@ void TracksScreen::buildTrackList() && !curr->hasEasterEggs()) continue; if (curr->isArena() || curr->isSoccer()||curr->isInternal()) continue; - if (m_offical_track && !curr->isInGroup(DEFAULT_GROUP_NAME)) continue; + if (!curr->isInGroup(DEFAULT_GROUP_NAME)) continue; if (curr_group_name != ALL_TRACK_GROUPS_ID && !curr->isInGroup(curr_group_name)) continue; - + if (is_network && + m_available_tracks_from_server.find(curr->getIdent()) == + m_available_tracks_from_server.end()) + { + continue; + } tracks.push_back(curr); } // for n m_random_track_list; - bool m_offical_track; - public: /** \brief implement callback from parent class GUIEngine::Screen */ @@ -58,8 +56,6 @@ public: /** \brief implement callback from parent class GUIEngine::Screen */ virtual void beforeAddingWidget() OVERRIDE; - void setOfficalTrack(bool offical) { m_offical_track = offical; } - void setFocusOnTrack(const std::string& trackName); }; From df5adbc320f8aed77c7cc7595b4371215ac29f2a Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 14 Feb 2018 09:20:27 +1100 Subject: [PATCH 110/413] Fixed comment. --- src/network/protocol_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/protocol_manager.cpp b/src/network/protocol_manager.cpp index 152fddf43..689edb771 100644 --- a/src/network/protocol_manager.cpp +++ b/src/network/protocol_manager.cpp @@ -58,7 +58,7 @@ void* ProtocolManager::mainLoop(void* data) PROFILER_POP_CPU_MARKER(); } return NULL; -} // protocolManagerAsynchronousUpdate +} // mainLoop // ---------------------------------------------------------------------------- From 2a30f75b78fb37b7e782bae66ce00154af3b729a Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 14 Feb 2018 09:20:45 +1100 Subject: [PATCH 111/413] Avoid crash that can happen if the first DT is large and event happens during that time. --- src/network/rewind_manager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index b52df770b..4aa876812 100755 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -243,6 +243,8 @@ void RewindManager::playEventsTill(float time, float *dt) Physics::getInstance()->getPhysicsWorld()->resetLocalTime(); } + if (m_rewind_queue.isEmpty()) return; + // This is necessary to avoid that rewinding an event will store the // event again as a seemingly new event. assert(!m_is_rewinding); From 4bd784843107cad99335396c4af47912ed3ca414 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 14 Feb 2018 09:27:13 +1100 Subject: [PATCH 112/413] Removed duplicated update of the ProtocolManager, and only update it when STKHost exists (otherwise STK will crash since ProtocolManager can get called when it does not exist). --- src/main_loop.cpp | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/src/main_loop.cpp b/src/main_loop.cpp index f7518c879..1d5761e61 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -303,16 +303,6 @@ void MainLoop::run() SFXManager::get()->update(); PROFILER_POP_CPU_MARKER(); - PROFILER_PUSH_CPU_MARKER("Protocol manager update", 0x7F, 0x00, 0x7F); - if (STKHost::existHost()) - { - if (STKHost::get()->requestedShutdown()) - STKHost::get()->shutdown(); - else - ProtocolManager::getInstance()->update(dt); - } - PROFILER_POP_CPU_MARKER(); - PROFILER_PUSH_CPU_MARKER("Database polling update", 0x00, 0x7F, 0x7F); Online::RequestManager::get()->update(frame_duration); PROFILER_POP_CPU_MARKER(); @@ -333,20 +323,22 @@ void MainLoop::run() // since the GUI engine is no more to be called then. if (m_abort) break; - if (!ProfileWorld::isNoGraphics() && STKHost::existHost() && - STKHost::get()->requestedShutdown() ) + if (STKHost::existHost()) { - STKHost::get()->shutdown(); - } + if (!ProfileWorld::isNoGraphics() && + STKHost::get()->requestedShutdown() ) + { + STKHost::get()->shutdown(); + } - PROFILER_PUSH_CPU_MARKER("Protocol manager update", - 0x7F, 0x00, 0x7F); - if (NetworkConfig::get()->isNetworking()) + PROFILER_PUSH_CPU_MARKER("Protocol manager update", + 0x7F, 0x00, 0x7F); ProtocolManager::getInstance()->update(dt); - PROFILER_POP_CPU_MARKER(); - + PROFILER_POP_CPU_MARKER(); + } if (World::getWorld()) World::getWorld()->updateTime(dt); } // for i < num_steps + m_is_last_substep = false; PROFILER_POP_CPU_MARKER(); // MainLoop pop PROFILER_SYNC_FRAME(); From 469f7d2fc57b9fec01bba1f755d435c977d9ae5a Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 14 Feb 2018 12:23:05 +0800 Subject: [PATCH 113/413] Drop player if he has incompatible karts / tracks --- src/network/protocols/client_lobby.cpp | 25 ++++--- src/network/protocols/client_lobby.hpp | 13 ++-- src/network/protocols/server_lobby.cpp | 68 +++++++++---------- src/states_screens/network_kart_selection.hpp | 8 +-- src/states_screens/tracks_screen.cpp | 10 ++- 5 files changed, 61 insertions(+), 63 deletions(-) diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index 6c2e1af68..8e80504d7 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -326,14 +326,10 @@ void ClientLobby::update(float dt) auto all_k = kart_properties_manager->getAllAvailableKarts(); auto all_t = track_manager->getAllTrackIdentifiers(); - if (all_k.size() > 65536) - { + if (all_k.size() >= 65536) all_k.resize(65535); - } - if (all_t.size() > 65536) - { + if (all_t.size() >= 65536) all_t.resize(65535); - } ns->addUInt16((uint16_t)all_k.size()).addUInt16((uint16_t)all_t.size()); for (const std::string& kart : all_k) { @@ -357,7 +353,7 @@ void ClientLobby::update(float dt) { NetworkKartSelectionScreen* screen = NetworkKartSelectionScreen::getInstance(); - screen->setAvailableKartsFromServer(m_avaliable_karts); + screen->setAvailableKartsFromServer(m_available_karts); screen->push(); m_state = SELECTING_KARTS; @@ -571,6 +567,9 @@ void ClientLobby::connectionRefused(Event* event) case 2: Log::info("ClientLobby", "Client busy."); break; + case 3: + Log::info("ClientLobby", "Having incompatible karts / tracks."); + break; default: Log::info("ClientLobby", "Connection refused."); break; @@ -683,15 +682,19 @@ void ClientLobby::startSelection(Event* event) { m_state = KART_SELECTION; const NetworkString& data = event->data(); - m_avaliable_karts.resize(data.getUInt16()); - m_avaliable_tracks.resize(data.getUInt16()); - for (std::string& kart : m_avaliable_karts) + const unsigned kart_num = data.getUInt16(); + const unsigned track_num = data.getUInt16(); + for (unsigned i = 0; i < kart_num; i++) { + std::string kart; data.decodeString(&kart); + m_available_karts.insert(kart); } - for (std::string& track : m_avaliable_tracks) + for (unsigned i = 0; i < track_num; i++) { + std::string track; data.decodeString(&track); + m_available_tracks.insert(track); } Log::info("ClientLobby", "Kart selection starts now"); } // startSelection diff --git a/src/network/protocols/client_lobby.hpp b/src/network/protocols/client_lobby.hpp index da28d1b75..bf2ab7366 100644 --- a/src/network/protocols/client_lobby.hpp +++ b/src/network/protocols/client_lobby.hpp @@ -4,6 +4,7 @@ #include "network/protocols/lobby_protocol.hpp" #include "network/transport_address.hpp" #include "utils/cpp2011.hpp" +#include class STKPeer; @@ -49,8 +50,8 @@ private: /** The state of the finite state machine. */ STATE m_state; - std::vector m_avaliable_karts; - std::vector m_avaliable_tracks; + std::set m_available_karts; + std::set m_available_tracks; public: ClientLobby(); @@ -70,10 +71,10 @@ public: void startingRaceNow(); void leave(); - const std::vector& getAvaliableKarts() const - { return m_avaliable_karts; } - const std::vector& getAvaliableTracks() const - { return m_avaliable_tracks; } + const std::set& getAvailableKarts() const + { return m_available_karts; } + const std::set& getAvailableTracks() const + { return m_available_tracks; } virtual bool notifyEvent(Event* event) OVERRIDE; virtual bool notifyEventAsynchronous(Event* event) OVERRIDE; diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 2e5bd4edf..1bd7c956e 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -87,14 +87,10 @@ ServerLobby::ServerLobby() : LobbyProtocol(NULL) // We use maximum 16bit unsigned limit auto all_k = kart_properties_manager->getAllAvailableKarts(); auto all_t = track_manager->getAllTrackIdentifiers(); - if (all_k.size() > 65536) - { + if (all_k.size() >= 65536) all_k.resize(65535); - } - if (all_t.size() > 65536) - { + if (all_t.size() >= 65536) all_t.resize(65535); - } m_available_kts.getData().first = { all_k.begin(), all_k.end() }; m_available_kts.getData().second = { all_t.begin(), all_t.end() }; } // ServerLobby @@ -598,55 +594,55 @@ void ServerLobby::connectionRequested(Event* event) data.decodeString(&password); bool is_authorised = (password==NetworkConfig::get()->getPassword()); - std::vector client_karts_v, client_tracks_v; - client_karts_v.resize(data.getUInt16()); - client_tracks_v.resize(data.getUInt16()); - for (std::string& kart : client_karts_v) + std::set client_karts, client_tracks; + const unsigned kart_num = data.getUInt16(); + const unsigned track_num = data.getUInt16(); + for (unsigned i = 0; i < kart_num; i++) { + std::string kart; data.decodeString(&kart); + client_karts.insert(kart); } - for (std::string& track : client_tracks_v) + for (unsigned i = 0; i < track_num; i++) { + std::string track; data.decodeString(&track); + client_tracks.insert(track); } - std::set client_karts(client_karts_v.begin(), - client_karts_v.end()); - std::set client_tracks(client_tracks_v.begin(), - client_tracks_v.end()); - // Remove karts or tracks that do not exist in either client or server - m_available_kts.lock(); - std::vector karts_erase, tracks_erase; - for (const std::string& client_kart : client_karts) - { - if (m_available_kts.getData().first.find(client_kart) == - m_available_kts.getData().first.end()) - { - karts_erase.push_back(client_kart); - } - } + // Remove karts/tracks from server that are not supported on the new client + // so that in the end the server has a list of all karts/tracks available + // on all clients + std::set karts_erase, tracks_erase; for (const std::string& server_kart : m_available_kts.getData().first) { if (client_karts.find(server_kart) == client_karts.end()) { - karts_erase.push_back(server_kart); - } - } - for (const std::string& client_track : client_tracks) - { - if (m_available_kts.getData().second.find(client_track) == - m_available_kts.getData().second.end()) - { - tracks_erase.push_back(client_track); + karts_erase.insert(server_kart); } } for (const std::string& server_track : m_available_kts.getData().second) { if (client_tracks.find(server_track) == client_tracks.end()) { - tracks_erase.push_back(server_track); + tracks_erase.insert(server_track); } } + + // Drop this player if he doesn't have at least 1 kart / track the same + // from server + if (karts_erase.size() == m_available_kts.getData().first.size() || + tracks_erase.size() == m_available_kts.getData().second.size()) + { + NetworkString *message = getNetworkString(2); + message->addUInt8(LE_CONNECTION_REFUSED).addUInt8(3); + peer->sendPacket(message); + delete message; + Log::verbose("ServerLobby", "Player has incompatible karts / tracks"); + m_available_kts.unlock(); + return; + } + for (const std::string& kart_erase : karts_erase) { m_available_kts.getData().first.erase(kart_erase); diff --git a/src/states_screens/network_kart_selection.hpp b/src/states_screens/network_kart_selection.hpp index 9c8021a7d..0c714e2ca 100644 --- a/src/states_screens/network_kart_selection.hpp +++ b/src/states_screens/network_kart_selection.hpp @@ -37,16 +37,16 @@ protected: virtual void playerConfirm(const int playerID) OVERRIDE; private: - std::set m_avaliable_karts; + std::set m_available_karts; virtual bool isIgnored(const std::string& ident) const OVERRIDE { - return m_avaliable_karts.find(ident) == m_avaliable_karts.end(); + return m_available_karts.find(ident) == m_available_karts.end(); } public: - void setAvailableKartsFromServer(const std::vector& k) + void setAvailableKartsFromServer(const std::set& k) { - m_avaliable_karts = { k.begin(), k.end() }; + m_available_karts = k; } virtual void init() OVERRIDE; virtual bool onEscapePressed() OVERRIDE; diff --git a/src/states_screens/tracks_screen.cpp b/src/states_screens/tracks_screen.cpp index 8a26fcc5e..cb207ac37 100644 --- a/src/states_screens/tracks_screen.cpp +++ b/src/states_screens/tracks_screen.cpp @@ -200,14 +200,12 @@ void TracksScreen::buildTrackList() // First build a list of all tracks to be displayed // (e.g. exclude arenas, ...) bool is_network = (STKHost::existHost()); - std::set m_available_tracks_from_server; + ClientLobby* clrp = NULL; if (is_network) { Protocol* protocol = LobbyProtocol::get(); - ClientLobby* clrp = dynamic_cast(protocol); + clrp = dynamic_cast(protocol); assert(clrp); - m_available_tracks_from_server = - { clrp->getAvaliableTracks().begin(), clrp->getAvaliableTracks().end() }; } PtrVector tracks; for (int n = 0; n < track_amount; n++) @@ -221,8 +219,8 @@ void TracksScreen::buildTrackList() if (curr_group_name != ALL_TRACK_GROUPS_ID && !curr->isInGroup(curr_group_name)) continue; if (is_network && - m_available_tracks_from_server.find(curr->getIdent()) == - m_available_tracks_from_server.end()) + clrp->getAvailableTracks().find(curr->getIdent()) == + clrp->getAvailableTracks().end()) { continue; } From c0333fe0f7f5684fdb4ce1edf55091207b869edf Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 15 Feb 2018 10:06:13 +1100 Subject: [PATCH 114/413] Bugfix: TimeInfo objects were missing for substeps, resulting in very stuttering game play. --- src/main_loop.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 1d5761e61..b2c57b497 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -311,6 +311,16 @@ void MainLoop::run() for(int i=0; iisEnabled() && i>0) + { + RewindManager::get() + ->addNextTimeStep(World::getWorld()->getTime(), dt); + } + // Enable last substep in last iteration m_is_last_substep = (i == num_steps - 1); From 20a2bc3bbbd880b9931bd5976d5f6d44658f5c7c Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 15 Feb 2018 13:28:28 +0800 Subject: [PATCH 115/413] Terminate controller and game event protocols when exit result screen --- src/network/protocols/client_lobby.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index 8e80504d7..518898a39 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -753,6 +753,11 @@ void ClientLobby::exitResultScreen(Event *event) // Will be reset to linked if connected to server, see update(float dt) m_game_setup = STKHost::get()->setupNewGame(); STKHost::get()->getServerPeerForClient()->unsetClientServerToken(); + // stop race protocols + ProtocolManager *pm = ProtocolManager::getInstance(); + pm->findAndTerminate(PROTOCOL_CONTROLLER_EVENTS); + pm->findAndTerminate(PROTOCOL_KART_UPDATE); + pm->findAndTerminate(PROTOCOL_GAME_EVENTS); m_state = NONE; } // exitResultScreen From fa2a8bccd328889ac71178849470766644c633f3 Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 15 Feb 2018 14:07:58 +0800 Subject: [PATCH 116/413] Reset available karts and tracks each selection --- src/network/protocols/client_lobby.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index 518898a39..fa9ef0f6b 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -684,6 +684,8 @@ void ClientLobby::startSelection(Event* event) const NetworkString& data = event->data(); const unsigned kart_num = data.getUInt16(); const unsigned track_num = data.getUInt16(); + m_available_karts.clear(); + m_available_tracks.clear(); for (unsigned i = 0; i < kart_num; i++) { std::string kart; From 3d8efcdfa16addec34d6a454c7ddbc7752b2d6b2 Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 15 Feb 2018 15:33:18 +0800 Subject: [PATCH 117/413] Use >= for counter in case of some peer disconnect in between for completing race end --- src/network/protocols/server_lobby.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 1bd7c956e..1c1b75f20 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -1042,7 +1042,7 @@ void ServerLobby::startedRaceOnClient(Event *event) void ServerLobby::playerFinishedResult(Event *event) { m_player_ready_counter++; - if(m_player_ready_counter == STKHost::get()->getPeerCount()) + if(m_player_ready_counter >= STKHost::get()->getPeerCount()) { // We can't trigger the world/race exit here, since this is called // from the protocol manager thread. So instead we force the timeout From 05f7c014dd4a31812962ef56319356aea08bb730 Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 15 Feb 2018 16:47:04 +0800 Subject: [PATCH 118/413] Create a prototype for thread-safe protocol manager Using weak and shared_ptr, if !lock(), than it was atomtically destroyed --- src/main_loop.cpp | 5 +- src/network/protocol.cpp | 8 +- src/network/protocol_manager.cpp | 80 +++++++++++-------- src/network/protocol_manager.hpp | 28 ++++--- src/network/protocols/client_lobby.cpp | 8 +- src/network/protocols/connect_to_peer.cpp | 2 +- src/network/protocols/lobby_protocol.cpp | 2 +- src/network/protocols/server_lobby.cpp | 3 +- src/network/race_event_manager.cpp | 6 +- src/network/stk_host.cpp | 25 +++--- src/states_screens/network_kart_selection.cpp | 2 +- src/states_screens/online_profile_servers.cpp | 2 +- src/utils/singleton.hpp | 4 +- 13 files changed, 103 insertions(+), 72 deletions(-) diff --git a/src/main_loop.cpp b/src/main_loop.cpp index b2c57b497..a9d185049 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -343,7 +343,10 @@ void MainLoop::run() PROFILER_PUSH_CPU_MARKER("Protocol manager update", 0x7F, 0x00, 0x7F); - ProtocolManager::getInstance()->update(dt); + if (auto pm = ProtocolManager::lock()) + { + pm->update(dt); + } PROFILER_POP_CPU_MARKER(); } if (World::getWorld()) World::getWorld()->updateTime(dt); diff --git a/src/network/protocol.cpp b/src/network/protocol.cpp index 90f5ee610..29d54192e 100644 --- a/src/network/protocol.cpp +++ b/src/network/protocol.cpp @@ -79,7 +79,7 @@ bool Protocol::checkDataSize(Event* event, unsigned int minimum_size) */ void Protocol::requestStart() { - ProtocolManager::getInstance()->requestStart(this); + ProtocolManager::lock()->requestStart(this); } // requestStart // ---------------------------------------------------------------------------- @@ -87,7 +87,7 @@ void Protocol::requestStart() */ void Protocol::requestPause() { - ProtocolManager::getInstance()->requestPause(this); + ProtocolManager::lock()->requestPause(this); } // requestPause // ---------------------------------------------------------------------------- @@ -95,7 +95,7 @@ void Protocol::requestPause() */ void Protocol::requestUnpause() { - ProtocolManager::getInstance()->requestUnpause(this); + ProtocolManager::lock()->requestUnpause(this); } // requestUnpause // ---------------------------------------------------------------------------- @@ -103,7 +103,7 @@ void Protocol::requestUnpause() */ void Protocol::requestTerminate() { - ProtocolManager::getInstance()->requestTerminate(this); + ProtocolManager::lock()->requestTerminate(this); } // requestTerminate // ---------------------------------------------------------------------------- diff --git a/src/network/protocol_manager.cpp b/src/network/protocol_manager.cpp index 689edb771..6327732b9 100644 --- a/src/network/protocol_manager.cpp +++ b/src/network/protocol_manager.cpp @@ -30,42 +30,58 @@ #include #include #include +#include #include - -ProtocolManager::ProtocolManager() +// ============================================================================ +std::weak_ptr ProtocolManager::m_protocol_manager; +// ============================================================================ +std::shared_ptr ProtocolManager::createInstance() { - m_exit.setAtomic(false); - - m_all_protocols.resize(PROTOCOL_MAX); - - pthread_create(&m_asynchronous_update_thread, NULL, - ProtocolManager::mainLoop, this); -} // ProtocolManager + if (!m_protocol_manager.expired()) + { + Log::fatal("ProtocolManager", + "Create only 1 instance of ProtocolManager!"); + return NULL; + } + auto pm = std::make_shared(); + pm->m_asynchronous_update_thread = std::thread( + std::bind(&ProtocolManager::startAsynchronousUpdateThread, pm)); + m_protocol_manager = pm; + return pm; +} // createInstance // ---------------------------------------------------------------------------- - -void* ProtocolManager::mainLoop(void* data) +std::shared_ptr ProtocolManager::lock() { - VS::setThreadName("ProtocolManager"); - - ProtocolManager* manager = static_cast(data); - while(manager && !manager->m_exit.getAtomic()) - { - manager->asynchronousUpdate(); - PROFILER_PUSH_CPU_MARKER("sleep", 0, 255, 255); - StkTime::sleep(2); - PROFILER_POP_CPU_MARKER(); - } - return NULL; -} // mainLoop + return m_protocol_manager.lock(); +} // lock +// ---------------------------------------------------------------------------- +ProtocolManager::ProtocolManager() +{ + m_exit.store(false); + m_all_protocols.resize(PROTOCOL_MAX); +} // ProtocolManager // ---------------------------------------------------------------------------- ProtocolManager::~ProtocolManager() { } // ~ProtocolManager +// ---------------------------------------------------------------------------- +void ProtocolManager::startAsynchronousUpdateThread() +{ + VS::setThreadName("ProtocolManager"); + while(!m_exit.load()) + { + asynchronousUpdate(); + PROFILER_PUSH_CPU_MARKER("sleep", 0, 255, 255); + StkTime::sleep(2); + PROFILER_POP_CPU_MARKER(); + } +} // startAsynchronousUpdateThread + // ---------------------------------------------------------------------------- void ProtocolManager::OneProtocolType::abort() { @@ -79,8 +95,8 @@ void ProtocolManager::OneProtocolType::abort() */ void ProtocolManager::abort() { - m_exit.setAtomic(true); - pthread_join(m_asynchronous_update_thread, NULL); // wait the thread to finish + m_exit.store(true); + m_asynchronous_update_thread.join(); // wait the thread to finish // Now only this main thread is active, no more need for locks for (unsigned int i = 0; i < m_all_protocols.size(); i++) @@ -218,7 +234,7 @@ void ProtocolManager::requestTerminate(Protocol* protocol) */ void ProtocolManager::startProtocol(Protocol *protocol) { - assert(pthread_equal(pthread_self(), m_asynchronous_update_thread)); + assert(std::this_thread::get_id() == m_asynchronous_update_thread.get_id()); OneProtocolType &opt = m_all_protocols[protocol->getProtocolType()]; opt.lock(); opt.addProtocol(protocol); @@ -238,7 +254,7 @@ void ProtocolManager::startProtocol(Protocol *protocol) */ void ProtocolManager::pauseProtocol(Protocol *protocol) { - assert(pthread_equal(pthread_self(), m_asynchronous_update_thread)); + assert(std::this_thread::get_id() == m_asynchronous_update_thread.get_id()); assert(protocol->getState() == PROTOCOL_STATE_RUNNING); // We lock the protocol to avoid that paused() is called at the same // time that the main thread delivers an event or calls update @@ -255,7 +271,7 @@ void ProtocolManager::pauseProtocol(Protocol *protocol) */ void ProtocolManager::unpauseProtocol(Protocol *protocol) { - assert(pthread_equal(pthread_self(), m_asynchronous_update_thread)); + assert(std::this_thread::get_id() == m_asynchronous_update_thread.get_id()); assert(protocol->getState() == PROTOCOL_STATE_PAUSED); // No lock necessary, since the protocol is paused, no other thread will // be executing @@ -292,7 +308,7 @@ void ProtocolManager::OneProtocolType::removeProtocol(Protocol *p) */ void ProtocolManager::terminateProtocol(Protocol *protocol) { - assert(pthread_equal(pthread_self(), m_asynchronous_update_thread)); + assert(std::this_thread::get_id() == m_asynchronous_update_thread.get_id()); OneProtocolType &opt = m_all_protocols[protocol->getProtocolType()]; // Be sure that noone accesses the protocols vector @@ -415,7 +431,7 @@ void ProtocolManager::OneProtocolType::update(float dt, bool async) void ProtocolManager::update(float dt) { // Update from main thread only: - assert(!pthread_equal(pthread_self(), m_asynchronous_update_thread)); + assert(std::this_thread::get_id() != m_asynchronous_update_thread.get_id()); // before updating, notify protocols that they have received events m_sync_events_to_process.lock(); @@ -462,7 +478,7 @@ void ProtocolManager::asynchronousUpdate() { PROFILER_PUSH_CPU_MARKER("Message delivery", 255, 0, 0); // Update from ProtocolManager thread only: - assert(pthread_equal(pthread_self(), m_asynchronous_update_thread)); + assert(std::this_thread::get_id() == m_asynchronous_update_thread.get_id()); // First deliver asynchronous messages for all protocols // ===================================================== @@ -554,4 +570,4 @@ Protocol* ProtocolManager::getProtocol(ProtocolType type) if (opt.isEmpty()) return NULL; return opt.getFirstProtocol(); -} // getProtocol \ No newline at end of file +} // getProtocol diff --git a/src/network/protocol_manager.hpp b/src/network/protocol_manager.hpp index b0752ae71..e4424cc5d 100644 --- a/src/network/protocol_manager.hpp +++ b/src/network/protocol_manager.hpp @@ -30,8 +30,11 @@ #include "utils/synchronised.hpp" #include "utils/types.hpp" +#include #include +#include #include +#include class Event; class STKPeer; @@ -130,10 +133,8 @@ public: * responsible to forward events to all protocols with the same id. * */ -class ProtocolManager : public AbstractSingleton, - public NoCopy +class ProtocolManager : public NoCopy { - friend class AbstractSingleton; private: /** A simple class that stores all protocols of a certain type. While @@ -211,14 +212,15 @@ private: Synchronised< std::vector > m_requests; /** When set to true, the main thread will exit. */ - Synchronised m_exit; + std::atomic_bool m_exit; /*! Asynchronous update thread.*/ - pthread_t m_asynchronous_update_thread; + std::thread m_asynchronous_update_thread; - ProtocolManager(); - virtual ~ProtocolManager(); - static void* mainLoop(void *data); + /*! Single instance of protocol manager.*/ + static std::weak_ptr m_protocol_manager; + + void startAsynchronousUpdateThread(); bool sendEvent(Event* event); virtual void startProtocol(Protocol *protocol); @@ -228,6 +230,8 @@ private: virtual void unpauseProtocol(Protocol *protocol); public: + ProtocolManager(); + virtual ~ProtocolManager(); void abort(); void propagateEvent(Event* event); Protocol* getProtocol(ProtocolType type); @@ -238,10 +242,16 @@ public: void findAndTerminate(ProtocolType type); void update(float dt); // ------------------------------------------------------------------------ - const pthread_t & getThreadID() const + bool isExiting() const { return m_exit.load(); } + // ------------------------------------------------------------------------ + const std::thread& getThread() const { return m_asynchronous_update_thread; } // getThreadID + // ------------------------------------------------------------------------ + static std::shared_ptr createInstance(); + // ------------------------------------------------------------------------ + static std::shared_ptr lock(); }; // class ProtocolManager diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index fa9ef0f6b..d0ec62168 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -370,7 +370,7 @@ void ClientLobby::update(float dt) break; case DONE: m_state = EXITING; - ProtocolManager::getInstance()->requestTerminate(this); + ProtocolManager::lock()->requestTerminate(this); break; case EXITING: break; @@ -722,7 +722,8 @@ void ClientLobby::raceFinished(Event* event) "Server notified that the race is finished."); // stop race protocols - ProtocolManager *pm = ProtocolManager::getInstance(); + auto pm = ProtocolManager::lock(); + assert(pm); pm->findAndTerminate(PROTOCOL_CONTROLLER_EVENTS); pm->findAndTerminate(PROTOCOL_KART_UPDATE); pm->findAndTerminate(PROTOCOL_GAME_EVENTS); @@ -756,7 +757,8 @@ void ClientLobby::exitResultScreen(Event *event) m_game_setup = STKHost::get()->setupNewGame(); STKHost::get()->getServerPeerForClient()->unsetClientServerToken(); // stop race protocols - ProtocolManager *pm = ProtocolManager::getInstance(); + auto pm = ProtocolManager::lock(); + assert(pm); pm->findAndTerminate(PROTOCOL_CONTROLLER_EVENTS); pm->findAndTerminate(PROTOCOL_KART_UPDATE); pm->findAndTerminate(PROTOCOL_GAME_EVENTS); diff --git a/src/network/protocols/connect_to_peer.cpp b/src/network/protocols/connect_to_peer.cpp index 41e0bf6a9..70b505e16 100644 --- a/src/network/protocols/connect_to_peer.cpp +++ b/src/network/protocols/connect_to_peer.cpp @@ -132,7 +132,7 @@ void ConnectToPeer::asynchronousUpdate() { m_current_protocol = new PingProtocol(m_peer_address, /*time-between-ping*/2.0); - ProtocolManager::getInstance()->requestStart(m_current_protocol); + ProtocolManager::lock()->requestStart(m_current_protocol); m_state = CONNECTING; } else diff --git a/src/network/protocols/lobby_protocol.cpp b/src/network/protocols/lobby_protocol.cpp index 0c0985392..4b4f489b0 100644 --- a/src/network/protocols/lobby_protocol.cpp +++ b/src/network/protocols/lobby_protocol.cpp @@ -135,5 +135,5 @@ void LobbyProtocol::loadWorld() */ void LobbyProtocol::terminateLatencyProtocol() { - ProtocolManager::getInstance()->findAndTerminate(PROTOCOL_SYNCHRONIZATION); + ProtocolManager::lock()->findAndTerminate(PROTOCOL_SYNCHRONIZATION); } // stopLatencyProtocol diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 1c1b75f20..3ae85c425 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -289,7 +289,8 @@ void ServerLobby::update(float dt) // notify the network world that it is stopped RaceEventManager::getInstance()->stop(); // stop race protocols - ProtocolManager *pm = ProtocolManager::getInstance(); + auto pm = ProtocolManager::lock(); + assert(pm); pm->findAndTerminate(PROTOCOL_CONTROLLER_EVENTS); pm->findAndTerminate(PROTOCOL_KART_UPDATE); pm->findAndTerminate(PROTOCOL_GAME_EVENTS); diff --git a/src/network/race_event_manager.cpp b/src/network/race_event_manager.cpp index 8d2fb5d2f..02a699646 100644 --- a/src/network/race_event_manager.cpp +++ b/src/network/race_event_manager.cpp @@ -27,7 +27,7 @@ void RaceEventManager::update(float dt) { // This can happen in case of disconnects - protocol manager is // shut down, but still events to process. - if(!ProtocolManager::getInstance()) + if(!ProtocolManager::lock()) return; // Replay all recorded events up to the current time (only if the @@ -78,7 +78,7 @@ bool RaceEventManager::isRaceOver() void RaceEventManager::kartFinishedRace(AbstractKart *kart, float time) { GameEventsProtocol* protocol = static_cast( - ProtocolManager::getInstance()->getProtocol(PROTOCOL_GAME_EVENTS)); + ProtocolManager::lock()->getProtocol(PROTOCOL_GAME_EVENTS)); protocol->kartFinishedRace(kart, time); } // kartFinishedRace @@ -94,7 +94,7 @@ void RaceEventManager::collectedItem(Item *item, AbstractKart *kart) assert(NetworkConfig::get()->isServer()); GameEventsProtocol* protocol = static_cast( - ProtocolManager::getInstance()->getProtocol(PROTOCOL_GAME_EVENTS)); + ProtocolManager::lock()->getProtocol(PROTOCOL_GAME_EVENTS)); protocol->collectedItem(item,kart); } // collectedItem diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index 0b6f12d54..85c71a053 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -290,7 +290,7 @@ STKHost::STKHost(const irr::core::stringw &server_name) startListening(); Protocol *p = LobbyProtocol::create(); - ProtocolManager::getInstance()->requestStart(p); + ProtocolManager::lock()->requestStart(p); } // STKHost(server_name) @@ -320,7 +320,7 @@ void STKHost::init() Log::info("STKHost", "Host initialized."); Network::openLog(); // Open packet log file - ProtocolManager::getInstance(); + ProtocolManager::createInstance(); // Optional: start the network console m_network_console = NULL; @@ -337,7 +337,6 @@ void STKHost::init() */ STKHost::~STKHost() { - ProtocolManager::kill(); // delete the game setup if (m_game_setup) delete m_game_setup; @@ -375,7 +374,7 @@ void STKHost::requestShutdown() void STKHost::shutdown() { ServersManager::get()->unsetJoinedServer(); - ProtocolManager::getInstance()->abort(); + ProtocolManager::lock()->abort(); deleteAllPeers(); destroy(); } // shutdown @@ -414,7 +413,7 @@ void STKHost::abort() { // Finish protocol manager first, to avoid that it access data // in STKHost. - ProtocolManager::getInstance()->abort(); + ProtocolManager::lock()->abort(); stopListening(); } // abort @@ -548,6 +547,13 @@ void* STKHost::mainLoop(void* self) if (event.type == ENET_EVENT_TYPE_NONE) continue; + auto pm = ProtocolManager::lock(); + if (!pm || pm->isExiting()) + { + // Don't create more event if no protocol manager or it will + // be exiting + continue; + } // Create an STKEvent with the event data. This will also // create the peer if it doesn't exist already Event* stk_event = new Event(&event); @@ -579,7 +585,7 @@ void* STKHost::mainLoop(void* self) } // if message event // notify for the event now. - ProtocolManager::getInstance()->propagateEvent(stk_event); + pm->propagateEvent(stk_event); } // while enet_host_service } // while !mustStopListening @@ -638,13 +644,6 @@ void STKHost::handleDirectSocketRequest() { // In case of a LAN connection, we only allow connections from // a LAN address (192.168*, ..., and 127.*). - if (NetworkConfig::get()->isLAN() && !sender.isLAN()) - { - Log::error("STKHost", "Client trying to connect from '%s'", - sender.toString().c_str()); - Log::error("STKHost", "which is outside of LAN - rejected."); - return; - } Protocol *c = new ConnectToPeer(sender); c->requestStart(); } diff --git a/src/states_screens/network_kart_selection.cpp b/src/states_screens/network_kart_selection.cpp index 53f87aac2..559e165f2 100644 --- a/src/states_screens/network_kart_selection.cpp +++ b/src/states_screens/network_kart_selection.cpp @@ -196,7 +196,7 @@ void NetworkKartSelectionScreen::playerSelected(uint8_t player_id, // to the server. if(STKHost::get()->isAuthorisedToControl()) { - Protocol* protocol = ProtocolManager::getInstance() + Protocol* protocol = ProtocolManager::lock() ->getProtocol(PROTOCOL_LOBBY_ROOM); ClientLobby* clrp = dynamic_cast(protocol); diff --git a/src/states_screens/online_profile_servers.cpp b/src/states_screens/online_profile_servers.cpp index 3d070b154..d7ca56184 100644 --- a/src/states_screens/online_profile_servers.cpp +++ b/src/states_screens/online_profile_servers.cpp @@ -151,7 +151,7 @@ void OnlineProfileServers::doQuickPlay() NetworkingLobby::getInstance()->push(); ConnectToServer *cts = new ConnectToServer(server->getServerId(), server->getHostId()); - ProtocolManager::getInstance()->requestStart(cts); + ProtocolManager::lock()->requestStart(cts); } else { diff --git a/src/utils/singleton.hpp b/src/utils/singleton.hpp index 5c04d556c..074887a67 100644 --- a/src/utils/singleton.hpp +++ b/src/utils/singleton.hpp @@ -24,8 +24,8 @@ #include "utils/log.hpp" -/*! \class ProtocolManager - * \brief Manages the protocols at runtime. +/*! \class AbstractSingleton + * \brief Manages the abstract singleton at runtime. * This has been designed to allow multi-inheritance. This is advised to * re-declare getInstance, but whithout templates parameters in the inheriting * classes. From c5788a2c9026fd731047de4c5a845eeb8c12d663 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 16 Feb 2018 00:48:27 +0800 Subject: [PATCH 119/413] Move the cleaning of events to destructor of protocol manager So that the last one who deletes it can clear all the remaining data properly (ie if it's STKHost listening thread) Remove the assert in async update thread as it may not be true for the first thread creation --- src/network/protocol_manager.cpp | 73 ++++++++++++++++---------------- src/network/protocol_manager.hpp | 3 +- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/network/protocol_manager.cpp b/src/network/protocol_manager.cpp index 6327732b9..ef04e8f11 100644 --- a/src/network/protocol_manager.cpp +++ b/src/network/protocol_manager.cpp @@ -38,15 +38,24 @@ std::weak_ptr ProtocolManager::m_protocol_manager; // ============================================================================ std::shared_ptr ProtocolManager::createInstance() { - if (!m_protocol_manager.expired()) + if (!emptyInstance()) { Log::fatal("ProtocolManager", "Create only 1 instance of ProtocolManager!"); return NULL; } auto pm = std::make_shared(); - pm->m_asynchronous_update_thread = std::thread( - std::bind(&ProtocolManager::startAsynchronousUpdateThread, pm)); + pm->m_asynchronous_update_thread = std::thread([pm]() + { + VS::setThreadName("ProtocolManager"); + while(!pm->m_exit.load()) + { + pm->asynchronousUpdate(); + PROFILER_PUSH_CPU_MARKER("sleep", 0, 255, 255); + StkTime::sleep(2); + PROFILER_POP_CPU_MARKER(); + } + }); m_protocol_manager = pm; return pm; } // createInstance @@ -57,6 +66,12 @@ std::shared_ptr ProtocolManager::lock() return m_protocol_manager.lock(); } // lock +// ---------------------------------------------------------------------------- +bool ProtocolManager::emptyInstance() +{ + return m_protocol_manager.expired(); +} // emptyInstance + // ---------------------------------------------------------------------------- ProtocolManager::ProtocolManager() { @@ -67,37 +82,6 @@ ProtocolManager::ProtocolManager() // ---------------------------------------------------------------------------- ProtocolManager::~ProtocolManager() { -} // ~ProtocolManager - -// ---------------------------------------------------------------------------- -void ProtocolManager::startAsynchronousUpdateThread() -{ - VS::setThreadName("ProtocolManager"); - while(!m_exit.load()) - { - asynchronousUpdate(); - PROFILER_PUSH_CPU_MARKER("sleep", 0, 255, 255); - StkTime::sleep(2); - PROFILER_POP_CPU_MARKER(); - } -} // startAsynchronousUpdateThread - -// ---------------------------------------------------------------------------- -void ProtocolManager::OneProtocolType::abort() -{ - for (unsigned int i = 0; i < m_protocols.getData().size(); i++) - delete m_protocols.getData()[i]; - m_protocols.getData().clear(); -} // OneProtocolType::abort - -// ---------------------------------------------------------------------------- -/** \brief Stops the protocol manager. - */ -void ProtocolManager::abort() -{ - m_exit.store(true); - m_asynchronous_update_thread.join(); // wait the thread to finish - // Now only this main thread is active, no more need for locks for (unsigned int i = 0; i < m_all_protocols.size(); i++) { @@ -122,6 +106,24 @@ void ProtocolManager::abort() m_requests.getData().clear(); m_requests.unlock(); +} // ~ProtocolManager + +// ---------------------------------------------------------------------------- +void ProtocolManager::OneProtocolType::abort() +{ + for (unsigned int i = 0; i < m_protocols.getData().size(); i++) + delete m_protocols.getData()[i]; + m_protocols.getData().clear(); +} // OneProtocolType::abort + +// ---------------------------------------------------------------------------- +/** \brief Stops the protocol manager. + */ +void ProtocolManager::abort() +{ + m_exit.store(true); + // wait the thread to finish + m_asynchronous_update_thread.join(); } // abort // ---------------------------------------------------------------------------- @@ -477,9 +479,6 @@ void ProtocolManager::update(float dt) void ProtocolManager::asynchronousUpdate() { PROFILER_PUSH_CPU_MARKER("Message delivery", 255, 0, 0); - // Update from ProtocolManager thread only: - assert(std::this_thread::get_id() == m_asynchronous_update_thread.get_id()); - // First deliver asynchronous messages for all protocols // ===================================================== m_async_events_to_process.lock(); diff --git a/src/network/protocol_manager.hpp b/src/network/protocol_manager.hpp index e4424cc5d..5306ed3b3 100644 --- a/src/network/protocol_manager.hpp +++ b/src/network/protocol_manager.hpp @@ -220,7 +220,6 @@ private: /*! Single instance of protocol manager.*/ static std::weak_ptr m_protocol_manager; - void startAsynchronousUpdateThread(); bool sendEvent(Event* event); virtual void startProtocol(Protocol *protocol); @@ -251,6 +250,8 @@ public: // ------------------------------------------------------------------------ static std::shared_ptr createInstance(); // ------------------------------------------------------------------------ + static bool emptyInstance(); + // ------------------------------------------------------------------------ static std::shared_ptr lock(); }; // class ProtocolManager From cfeadf335cbc84c1625188de60024878dd6bfb10 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 17 Feb 2018 10:51:33 +0800 Subject: [PATCH 120/413] Move some code to header --- src/network/protocol_manager.cpp | 12 ------------ src/network/protocol_manager.hpp | 16 ++++++++++++---- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/network/protocol_manager.cpp b/src/network/protocol_manager.cpp index ef04e8f11..f1b2c025b 100644 --- a/src/network/protocol_manager.cpp +++ b/src/network/protocol_manager.cpp @@ -60,18 +60,6 @@ std::shared_ptr ProtocolManager::createInstance() return pm; } // createInstance -// ---------------------------------------------------------------------------- -std::shared_ptr ProtocolManager::lock() -{ - return m_protocol_manager.lock(); -} // lock - -// ---------------------------------------------------------------------------- -bool ProtocolManager::emptyInstance() -{ - return m_protocol_manager.expired(); -} // emptyInstance - // ---------------------------------------------------------------------------- ProtocolManager::ProtocolManager() { diff --git a/src/network/protocol_manager.hpp b/src/network/protocol_manager.hpp index 5306ed3b3..ed1834ea5 100644 --- a/src/network/protocol_manager.hpp +++ b/src/network/protocol_manager.hpp @@ -229,8 +229,10 @@ private: virtual void unpauseProtocol(Protocol *protocol); public: - ProtocolManager(); - virtual ~ProtocolManager(); + // =========================================== + // Public constructor is required for shared_ptr + ProtocolManager(); + virtual ~ProtocolManager(); void abort(); void propagateEvent(Event* event); Protocol* getProtocol(ProtocolType type); @@ -250,9 +252,15 @@ public: // ------------------------------------------------------------------------ static std::shared_ptr createInstance(); // ------------------------------------------------------------------------ - static bool emptyInstance(); + static bool emptyInstance() + { + return m_protocol_manager.expired(); + } // emptyInstance // ------------------------------------------------------------------------ - static std::shared_ptr lock(); + static std::shared_ptr lock() + { + return m_protocol_manager.lock(); + } // lock }; // class ProtocolManager From 372753f505d4cf8e5e2da5403889979f4b56a7ec Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 17 Feb 2018 11:40:48 +0800 Subject: [PATCH 121/413] Use atomic flag to start / stop listening thread --- src/network/stk_host.cpp | 70 ++++++++++------------------------------ src/network/stk_host.hpp | 35 +++++++++++++------- 2 files changed, 41 insertions(+), 64 deletions(-) diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index 85c71a053..3bf92ed4f 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -43,8 +43,7 @@ # include # include #endif -#include -#include +#include STKHost *STKHost::m_stk_host = NULL; bool STKHost::m_enable_console = false; @@ -303,12 +302,12 @@ void STKHost::init() m_shutdown = false; m_network = NULL; m_lan_network = NULL; - m_listening_thread = NULL; m_game_setup = NULL; m_is_registered = false; m_error_message = ""; - pthread_mutex_init(&m_exit_mutex, NULL); + m_exit_flag.clear(); + m_exit_flag.test_and_set(); // Start with initialising ENet // ============================ @@ -355,18 +354,6 @@ STKHost::~STKHost() delete m_network; } // ~STKHost -//----------------------------------------------------------------------------- -/** Requests that the network infrastructure is to be shut down. This function - * is called from a thread, but the actual shutdown needs to be done from - * the main thread to avoid race conditions (e.g. ProtocolManager might still - * access data structures when the main thread tests if STKHost exist (which - * it does, but ProtocolManager might be shut down already. - */ -void STKHost::requestShutdown() -{ - m_shutdown = true; -} // requestExit - //----------------------------------------------------------------------------- /** Called from the main thread when the network infrastructure is to be shut * down. @@ -462,9 +449,9 @@ bool STKHost::connect(const TransportAddress& address) */ void STKHost::startListening() { - pthread_mutex_lock(&m_exit_mutex); // will let the update function start - m_listening_thread = new pthread_t; - pthread_create(m_listening_thread, NULL, &STKHost::mainLoop, this); + m_exit_flag.clear(); + m_exit_flag.test_and_set(); + m_listening_thread = std::thread(std::bind(&STKHost::mainLoop, this)); } // startListening // ---------------------------------------------------------------------------- @@ -473,29 +460,11 @@ void STKHost::startListening() */ void STKHost::stopListening() { - if (m_listening_thread) - { - // This will stop the update function on its next update - pthread_mutex_unlock(&m_exit_mutex); - pthread_join(*m_listening_thread, NULL); // wait for the thread to end - } + m_exit_flag.clear(); + if (m_listening_thread.joinable()) + m_listening_thread.join(); } // stopListening -// --------------------------------------------------------------------------- -/** \brief Returns true when the thread should stop listening. - */ -int STKHost::mustStopListening() -{ - switch (pthread_mutex_trylock(&m_exit_mutex)) { - case 0: /* if we got the lock, unlock and return 1 (true) */ - pthread_mutex_unlock(&m_exit_mutex); - return 1; - case EBUSY: /* return 0 (false) if the mutex was locked */ - return 0; - } - return 1; -} // mustStopListening - // ---------------------------------------------------------------------------- /** Returns true if this client instance is allowed to control the server. * A client can authorise itself by providing the server's password. It is @@ -520,26 +489,25 @@ bool STKHost::isAuthorisedToControl() const * event and passes it to the Network Manager. * \param self : used to pass the ENet host to the function. */ -void* STKHost::mainLoop(void* self) +void STKHost::mainLoop() { VS::setThreadName("STKHost"); ENetEvent event; - STKHost* myself = (STKHost*)(self); - ENetHost* host = myself->m_network->getENetHost(); + ENetHost* host = m_network->getENetHost(); if(NetworkConfig::get()->isServer() && (NetworkConfig::get()->isLAN() || NetworkConfig::get()->isPublicServer()) ) { TransportAddress address(0, NetworkConfig::get()->getServerDiscoveryPort()); ENetAddress eaddr = address.toEnetAddress(); - myself->m_lan_network = new Network(1, 1, 0, 0, &eaddr); + m_lan_network = new Network(1, 1, 0, 0, &eaddr); } - while (!myself->mustStopListening()) + while (m_exit_flag.test_and_set()) { - if(myself->m_lan_network) + if(m_lan_network) { - myself->handleDirectSocketRequest(); + handleDirectSocketRequest(); } // if discovery host while (enet_host_service(host, &event, 20) != 0) @@ -561,7 +529,7 @@ void* STKHost::mainLoop(void* self) if (stk_event->getType() == EVENT_TYPE_CONNECTED) { Log::info("STKHost", "A client has just connected. There are " - "now %lu peers.", myself->m_peers.size()); + "now %lu peers.", m_peers.size()); Log::debug("STKHost", "Addresses are : %lx, %lx", stk_event->getPeer(), peer); } // EVENT_TYPE_CONNECTED @@ -588,12 +556,8 @@ void* STKHost::mainLoop(void* self) pm->propagateEvent(stk_event); } // while enet_host_service - } // while !mustStopListening - - free(myself->m_listening_thread); - myself->m_listening_thread = NULL; + } // while m_exit_flag.test_and_set() Log::info("STKHost", "Listening has been stopped"); - return NULL; } // mainLoop // ---------------------------------------------------------------------------- diff --git a/src/network/stk_host.hpp b/src/network/stk_host.hpp index 92d109732..ec9620b80 100644 --- a/src/network/stk_host.hpp +++ b/src/network/stk_host.hpp @@ -38,7 +38,8 @@ #define WIN32_LEAN_AND_MEAN #include -#include +#include +#include class GameSetup; class NetworkConsole; @@ -88,14 +89,14 @@ private: GameSetup* m_game_setup; /** Id of thread listening to enet events. */ - pthread_t* m_listening_thread; + std::thread m_listening_thread; /** Flag which is set from the protocol manager thread which * triggers a shutdown of the STKHost (and the Protocolmanager). */ - bool m_shutdown; + std::atomic_bool m_shutdown; - /** Mutex used to stop this thread. */ - pthread_mutex_t m_exit_mutex; + /** Atomic flag used to stop this thread. */ + std::atomic_flag m_exit_flag = ATOMIC_FLAG_INIT; /** If this is a server, it indicates if this server is registered * with the stk server. */ @@ -111,6 +112,9 @@ private: void init(); void handleDirectSocketRequest(); + // ------------------------------------------------------------------------ + void mainLoop(); + public: /** If a network console should be started. Note that the console can cause * a crash in release mode on windows (see #1529). */ @@ -139,15 +143,25 @@ public: // ------------------------------------------------------------------------ /** Checks if the STKHost has been created. */ static bool existHost() { return m_stk_host != NULL; } - // ------------------------------------------------------------------------ - - static void* mainLoop(void* self); virtual GameSetup* setupNewGame(); void abort(); void deleteAllPeers(); bool connect(const TransportAddress& peer); - void requestShutdown(); + + //------------------------------------------------------------------------- + /** Requests that the network infrastructure is to be shut down. This + * function is called from a thread, but the actual shutdown needs to be + * done from the main thread to avoid race conditions (e.g. + * ProtocolManager might still access data structures when the main thread + * tests if STKHost exist (which it does, but ProtocolManager might be + * shut down already. + */ + void requestShutdown() + { + m_shutdown.store(true); + } // requestExit + //------------------------------------------------------------------------- void shutdown(); void sendPacketExcept(STKPeer* peer, @@ -164,7 +178,6 @@ public: STKPeer *getPeer(ENetPeer *enet_peer); STKPeer *getServerPeerForClient() const; std::vector getMyPlayerProfiles(); - int mustStopListening(); uint16_t getPort() const; void setErrorMessage(const irr::core::stringw &message); bool isAuthorisedToControl() const; @@ -174,7 +187,7 @@ public: // -------------------------------------------------------------------- /** Returns true if a shutdown of the network infrastructure was * requested. */ - bool requestedShutdown() const { return m_shutdown; } + bool requestedShutdown() const { return m_shutdown.load(); } // -------------------------------------------------------------------- /** Returns the current game setup. */ GameSetup* getGameSetup() { return m_game_setup; } From 4c1cc509559f9e0be718a2d40b4bc8467e637c36 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 17 Feb 2018 15:21:26 +0800 Subject: [PATCH 122/413] Don't pause timer in network as it breaks rewind Also remove the unneed protocol manager checking --- src/modes/world_status.cpp | 6 ++++-- src/network/race_event_manager.cpp | 12 ++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index 8fcefd7e0..71e43add8 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -499,7 +499,8 @@ void WorldStatus::pause(Phase phase) m_phase = phase; IrrlichtDevice *device = irr_driver->getDevice(); - if (!device->getTimer()->isStopped()) + if (!device->getTimer()->isStopped() && + !NetworkConfig::get()->isNetworking()) device->getTimer()->stop(); } // pause @@ -514,6 +515,7 @@ void WorldStatus::unpause() m_previous_phase = UNDEFINED_PHASE; IrrlichtDevice *device = irr_driver->getDevice(); - if (device->getTimer()->isStopped()) + if (device->getTimer()->isStopped() && + !NetworkConfig::get()->isNetworking()) device->getTimer()->start(); } // unpause diff --git a/src/network/race_event_manager.cpp b/src/network/race_event_manager.cpp index 02a699646..6af40c6c0 100644 --- a/src/network/race_event_manager.cpp +++ b/src/network/race_event_manager.cpp @@ -25,11 +25,6 @@ RaceEventManager::~RaceEventManager() */ void RaceEventManager::update(float dt) { - // This can happen in case of disconnects - protocol manager is - // shut down, but still events to process. - if(!ProtocolManager::lock()) - return; - // Replay all recorded events up to the current time (only if the // timer isn't stopped, otherwise a potential rewind will trigger // an infinite loop since world time does not increase) @@ -42,11 +37,11 @@ void RaceEventManager::update(float dt) &dt); PROFILER_POP_CPU_MARKER(); } - World::getWorld()->updateWorld(dt); // if the race is over - if (World::getWorld()->getPhase() >= WorldStatus::RESULT_DISPLAY_PHASE) + if (World::getWorld()->getPhase() >= WorldStatus::RESULT_DISPLAY_PHASE && + World::getWorld()->getPhase() != WorldStatus::IN_GAME_MENU_PHASE) { // consider the world finished. stop(); @@ -71,7 +66,8 @@ bool RaceEventManager::isRaceOver() { if(!World::getWorld()) return false; - return (World::getWorld()->getPhase() > WorldStatus::RACE_PHASE); + return (World::getWorld()->getPhase() > WorldStatus::RACE_PHASE && + World::getWorld()->getPhase() != WorldStatus::IN_GAME_MENU_PHASE); } // isRaceOver // ---------------------------------------------------------------------------- From bcf8e4e5fe2c63744db2286eff5fc8cff59b10ad Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 17 Feb 2018 16:25:53 +0800 Subject: [PATCH 123/413] Allow shutdown STKHost from dialog --- src/main.cpp | 2 +- src/main_loop.cpp | 26 +++++++++--------- .../dialogs/race_paused_dialog.cpp | 27 ++++++++++++++++--- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 644f31a7a..5c07f31f5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -708,7 +708,7 @@ int handleCmdLinePreliminary() UserConfigParams::m_verbosity |= UserConfigParams::LOG_MISC; if(CommandLine::has("--debug=all") ) UserConfigParams::m_verbosity |= UserConfigParams::LOG_ALL; - if(CommandLine::has("--online")) + //if(CommandLine::has("--online")) MainMenuScreen::m_enable_online=true; std::string s; diff --git a/src/main_loop.cpp b/src/main_loop.cpp index a9d185049..5e0c79a2a 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -274,6 +274,12 @@ void MainLoop::run() float dt = 1.0f / stk_config->m_physics_fps; left_over_time -= num_steps * dt ; + if (!ProfileWorld::isNoGraphics() && STKHost::existHost() && + STKHost::get()->requestedShutdown()) + { + STKHost::get()->shutdown(); + } + // Add a Time step entry to the rewind list, which can store all // all input ecents being issued during the driver update. if (World::getWorld() && RewindManager::get()->isEnabled()) @@ -333,22 +339,14 @@ void MainLoop::run() // since the GUI engine is no more to be called then. if (m_abort) break; - if (STKHost::existHost()) + PROFILER_PUSH_CPU_MARKER("Protocol manager update", + 0x7F, 0x00, 0x7F); + if (auto pm = ProtocolManager::lock()) { - if (!ProfileWorld::isNoGraphics() && - STKHost::get()->requestedShutdown() ) - { - STKHost::get()->shutdown(); - } - - PROFILER_PUSH_CPU_MARKER("Protocol manager update", - 0x7F, 0x00, 0x7F); - if (auto pm = ProtocolManager::lock()) - { - pm->update(dt); - } - PROFILER_POP_CPU_MARKER(); + pm->update(dt); } + PROFILER_POP_CPU_MARKER(); + if (World::getWorld()) World::getWorld()->updateTime(dt); } // for i < num_steps diff --git a/src/states_screens/dialogs/race_paused_dialog.cpp b/src/states_screens/dialogs/race_paused_dialog.cpp index 8d35972a3..65e4ed0d5 100644 --- a/src/states_screens/dialogs/race_paused_dialog.cpp +++ b/src/states_screens/dialogs/race_paused_dialog.cpp @@ -27,11 +27,14 @@ #include "io/file_manager.hpp" #include "modes/overworld.hpp" #include "modes/world.hpp" +#include "network/network_config.hpp" +#include "network/stk_host.hpp" #include "race/race_manager.hpp" #include "states_screens/help_screen_1.hpp" #include "states_screens/main_menu_screen.hpp" #include "states_screens/race_setup_screen.hpp" #include "states_screens/options_screen_video.hpp" +#include "states_screens/online_screen.hpp" #include "states_screens/state_manager.hpp" #include "utils/translation.hpp" @@ -83,11 +86,17 @@ void RacePausedDialog::loadedFromFile() // a time of 0:00:00. if ((race_manager->getMinorMode() != RaceManager::MINOR_MODE_NORMAL_RACE && race_manager->getMinorMode() != RaceManager::MINOR_MODE_TIME_TRIAL ) || - World::getWorld()->isStartPhase()) + World::getWorld()->isStartPhase() || + NetworkConfig::get()->isNetworking()) { GUIEngine::RibbonWidget* choice_ribbon = getWidget("choiceribbon"); choice_ribbon->deleteChild("endrace"); + // No restart in network game + if (NetworkConfig::get()->isNetworking()) + { + choice_ribbon->deleteChild("restart"); + } } } @@ -119,6 +128,10 @@ GUIEngine::EventPropagation if (selection == "exit") { ModalDialog::dismiss(); + if (STKHost::existHost()) + { + STKHost::get()->shutdown(); + } race_manager->exitRace(); race_manager->setAIKartOverride(""); StateManager::get()->resetAndGoToScreen(MainMenuScreen::getInstance()); @@ -128,6 +141,7 @@ GUIEngine::EventPropagation OverWorld::enterOverWorld(); } + NetworkConfig::get()->unsetNetworking(); return GUIEngine::EVENT_BLOCK; } else if (selection == "help") @@ -145,7 +159,6 @@ GUIEngine::EventPropagation else if (selection == "restart") { ModalDialog::dismiss(); -// network_manager->setState(NetworkManager::NS_MAIN_MENU); World::getWorld()->scheduleUnpause(); race_manager->rerunRace(); return GUIEngine::EVENT_BLOCK; @@ -153,11 +166,19 @@ GUIEngine::EventPropagation else if (selection == "newrace") { ModalDialog::dismiss(); + if (STKHost::existHost()) + { + STKHost::get()->shutdown(); + } World::getWorld()->scheduleUnpause(); race_manager->exitRace(); Screen* newStack[] = {MainMenuScreen::getInstance(), - RaceSetupScreen::getInstance(), NULL}; + NetworkConfig::get()->isNetworking() ? + static_cast(OnlineScreen::getInstance()) : + static_cast(RaceSetupScreen::getInstance()), + NULL}; StateManager::get()->resetAndSetStack( newStack ); + NetworkConfig::get()->unsetNetworking(); return GUIEngine::EVENT_BLOCK; } else if (selection == "endrace") From 0a5c1a69c75dd7d3b45b807b99dcffeaf29413b3 Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 18 Feb 2018 12:39:05 +0800 Subject: [PATCH 124/413] Use shared_ptr for protocol to avoid leaking With weak_ptr it's also possible to auto clear LobbyProtocol when STKHost is shutdown --- .../controller/local_player_controller.cpp | 2 +- src/modes/world_status.cpp | 3 +- src/network/network_console.cpp | 23 +++++------- src/network/protocol.cpp | 8 ++--- src/network/protocol.hpp | 4 ++- src/network/protocol_manager.cpp | 27 +++++++------- src/network/protocol_manager.hpp | 33 ++++++++--------- src/network/protocols/client_lobby.cpp | 5 ++- src/network/protocols/connect_to_peer.cpp | 11 +++--- src/network/protocols/connect_to_peer.hpp | 2 +- src/network/protocols/connect_to_server.cpp | 35 ++++++++----------- src/network/protocols/connect_to_server.hpp | 2 +- src/network/protocols/game_protocol.cpp | 15 ++++++++ src/network/protocols/game_protocol.hpp | 14 +++++++- src/network/protocols/lobby_protocol.cpp | 6 ++-- src/network/protocols/lobby_protocol.hpp | 23 +++++++----- src/network/protocols/server_lobby.cpp | 12 +++---- src/network/protocols/server_lobby.hpp | 2 +- src/network/race_event_manager.cpp | 4 +-- src/network/rewind_manager.cpp | 6 ++-- src/network/stk_host.cpp | 9 ++--- src/race/race_manager.cpp | 2 +- src/states_screens/network_kart_selection.cpp | 15 +++----- src/states_screens/networking_lobby.cpp | 10 +++--- src/states_screens/online_profile_servers.cpp | 2 +- src/states_screens/race_result_gui.cpp | 4 +-- src/states_screens/tracks_screen.cpp | 12 +++---- 27 files changed, 144 insertions(+), 147 deletions(-) diff --git a/src/karts/controller/local_player_controller.cpp b/src/karts/controller/local_player_controller.cpp index f5132bcec..27ef87122 100644 --- a/src/karts/controller/local_player_controller.cpp +++ b/src/karts/controller/local_player_controller.cpp @@ -161,7 +161,7 @@ bool LocalPlayerController::action(PlayerAction action, int value, NetworkConfig::get()->isClient() && !RewindManager::get()->isRewinding() ) { - GameProtocol::getInstance() + GameProtocol::lock() ->controllerAction(m_kart->getWorldKartId(), action, value, m_steer_val_l, m_steer_val_r); diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index 71e43add8..0659771d1 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -262,8 +262,7 @@ void WorldStatus::updateTime(const float dt) if (!m_server_is_ready) return; m_phase = READY_PHASE; - Protocol *p = LobbyProtocol::get(); - ClientLobby *cl = dynamic_cast(p); + auto cl = LobbyProtocol::get(); if (cl) cl->startingRaceNow(); return; // Don't increase time diff --git a/src/network/network_console.cpp b/src/network/network_console.cpp index 9f673ed81..3b419766a 100644 --- a/src/network/network_console.cpp +++ b/src/network/network_console.cpp @@ -78,23 +78,19 @@ void* NetworkConsole::mainLoop(void* data) } else if (str == "start" && NetworkConfig::get()->isServer()) { - ServerLobby* protocol = - dynamic_cast(LobbyProtocol::get()); - protocol->signalRaceStartToClients(); + auto sl = LobbyProtocol::get(); + sl->signalRaceStartToClients(); } else if (str == "selection" && NetworkConfig::get()->isServer()) { - ServerLobby* protocol = - dynamic_cast(LobbyProtocol::get()); - protocol->startSelection(); + auto sl = LobbyProtocol::get(); + sl->startSelection(); } else if (str == "select" && NetworkConfig::get()->isClient()) { std::string str2; getline(std::cin, str2); - ServerLobby* protocol = - dynamic_cast(LobbyProtocol::get()); - ClientLobby* clrp = dynamic_cast(protocol); + auto clrp = LobbyProtocol::get(); std::vector players = STKHost::get()->getMyPlayerProfiles(); // For now send a vote for each local player @@ -109,9 +105,7 @@ void* NetworkConsole::mainLoop(void* data) std::cout << "Vote for ? (track/laps/reversed/major/minor/race#) :"; std::string str2; getline(std::cin, str2); - LobbyProtocol* protocol = LobbyProtocol::get(); - ClientLobby* clrp = - dynamic_cast(protocol); + auto clrp = LobbyProtocol::get(); std::vector players = STKHost::get()->getMyPlayerProfiles(); if (str2 == "track") @@ -164,14 +158,13 @@ void* NetworkConsole::mainLoop(void* data) } } // while !stop - Protocol *p = new StopServer(); + auto p = std::make_shared(); + p->requestStart(); while(p->getState() != PROTOCOL_STATE_TERMINATED) { StkTime::sleep(1); } - delete p; - main_loop->abort(); return NULL; diff --git a/src/network/protocol.cpp b/src/network/protocol.cpp index 29d54192e..0f9a7a068 100644 --- a/src/network/protocol.cpp +++ b/src/network/protocol.cpp @@ -79,7 +79,7 @@ bool Protocol::checkDataSize(Event* event, unsigned int minimum_size) */ void Protocol::requestStart() { - ProtocolManager::lock()->requestStart(this); + ProtocolManager::lock()->requestStart(shared_from_this()); } // requestStart // ---------------------------------------------------------------------------- @@ -87,7 +87,7 @@ void Protocol::requestStart() */ void Protocol::requestPause() { - ProtocolManager::lock()->requestPause(this); + ProtocolManager::lock()->requestPause(shared_from_this()); } // requestPause // ---------------------------------------------------------------------------- @@ -95,7 +95,7 @@ void Protocol::requestPause() */ void Protocol::requestUnpause() { - ProtocolManager::lock()->requestUnpause(this); + ProtocolManager::lock()->requestUnpause(shared_from_this()); } // requestUnpause // ---------------------------------------------------------------------------- @@ -103,7 +103,7 @@ void Protocol::requestUnpause() */ void Protocol::requestTerminate() { - ProtocolManager::lock()->requestTerminate(this); + ProtocolManager::lock()->requestTerminate(shared_from_this()); } // requestTerminate // ---------------------------------------------------------------------------- diff --git a/src/network/protocol.hpp b/src/network/protocol.hpp index f962cfe2c..0f3d0794e 100644 --- a/src/network/protocol.hpp +++ b/src/network/protocol.hpp @@ -27,6 +27,7 @@ #include "utils/no_copy.hpp" #include "utils/types.hpp" +#include #include class Event; @@ -90,7 +91,8 @@ public: * to make any network job. * \ingroup network */ -class Protocol : public NoCopy +class Protocol : public std::enable_shared_from_this, + public NoCopy { LEAK_CHECK() protected: diff --git a/src/network/protocol_manager.cpp b/src/network/protocol_manager.cpp index f1b2c025b..507415222 100644 --- a/src/network/protocol_manager.cpp +++ b/src/network/protocol_manager.cpp @@ -99,8 +99,6 @@ ProtocolManager::~ProtocolManager() // ---------------------------------------------------------------------------- void ProtocolManager::OneProtocolType::abort() { - for (unsigned int i = 0; i < m_protocols.getData().size(); i++) - delete m_protocols.getData()[i]; m_protocols.getData().clear(); } // OneProtocolType::abort @@ -143,7 +141,7 @@ void ProtocolManager::propagateEvent(Event* event) * \param protocol : A pointer to the protocol to start * \return The unique id of the protocol that is being started. */ -void ProtocolManager::requestStart(Protocol* protocol) +void ProtocolManager::requestStart(std::shared_ptr protocol) { // create the request ProtocolRequest req(PROTOCOL_REQUEST_START, protocol); @@ -159,7 +157,7 @@ void ProtocolManager::requestStart(Protocol* protocol) * thread-safe. * \param protocol : A pointer to the protocol to pause */ -void ProtocolManager::requestPause(Protocol* protocol) +void ProtocolManager::requestPause(std::shared_ptr protocol) { if (!protocol) return; @@ -177,12 +175,12 @@ void ProtocolManager::requestPause(Protocol* protocol) * thread-safe. * \param protocol : A pointer to the protocol to unpause */ -void ProtocolManager::requestUnpause(Protocol* protocol) +void ProtocolManager::requestUnpause(std::shared_ptr protocol) { if (!protocol) return; // create the request - ProtocolRequest req(PROTOCOL_REQUEST_UNPAUSE, protocol);; + ProtocolRequest req(PROTOCOL_REQUEST_UNPAUSE, protocol); // add it to the request stack m_requests.lock(); m_requests.getData().push_back(req); @@ -195,7 +193,7 @@ void ProtocolManager::requestUnpause(Protocol* protocol) * thread-safe. * \param protocol : A pointer to the protocol that is finished */ -void ProtocolManager::requestTerminate(Protocol* protocol) +void ProtocolManager::requestTerminate(std::shared_ptr protocol) { if (!protocol) return; @@ -222,7 +220,7 @@ void ProtocolManager::requestTerminate(Protocol* protocol) * Add the protocol info to the m_protocols vector. * \param protocol : ProtocolInfo to start. */ -void ProtocolManager::startProtocol(Protocol *protocol) +void ProtocolManager::startProtocol(std::shared_ptr protocol) { assert(std::this_thread::get_id() == m_asynchronous_update_thread.get_id()); OneProtocolType &opt = m_all_protocols[protocol->getProtocolType()]; @@ -242,7 +240,7 @@ void ProtocolManager::startProtocol(Protocol *protocol) * Pauses a protocol and tells it that it's being paused. * \param protocol : Protocol to pause. */ -void ProtocolManager::pauseProtocol(Protocol *protocol) +void ProtocolManager::pauseProtocol(std::shared_ptr protocol) { assert(std::this_thread::get_id() == m_asynchronous_update_thread.get_id()); assert(protocol->getState() == PROTOCOL_STATE_RUNNING); @@ -259,7 +257,7 @@ void ProtocolManager::pauseProtocol(Protocol *protocol) * Unpauses a protocol and notifies it. * \param protocol : Protocol to unpause. */ -void ProtocolManager::unpauseProtocol(Protocol *protocol) +void ProtocolManager::unpauseProtocol(std::shared_ptr protocol) { assert(std::this_thread::get_id() == m_asynchronous_update_thread.get_id()); assert(protocol->getState() == PROTOCOL_STATE_PAUSED); @@ -274,10 +272,9 @@ void ProtocolManager::unpauseProtocol(Protocol *protocol) * Note that the protocol is not deleted. * \param p The protocol to be removed. */ -void ProtocolManager::OneProtocolType::removeProtocol(Protocol *p) +void ProtocolManager::OneProtocolType::removeProtocol(std::shared_ptr p) { - std::vector::iterator i = - std::find(m_protocols.getData().begin(), + auto i = std::find(m_protocols.getData().begin(), m_protocols.getData().end(), p); if (i == m_protocols.getData().end()) { @@ -296,7 +293,7 @@ void ProtocolManager::OneProtocolType::removeProtocol(Protocol *p) * Remove a protocol from the protocols vector. * \param protocol : Protocol concerned. */ -void ProtocolManager::terminateProtocol(Protocol *protocol) +void ProtocolManager::terminateProtocol(std::shared_ptr protocol) { assert(std::this_thread::get_id() == m_asynchronous_update_thread.get_id()); @@ -551,7 +548,7 @@ void ProtocolManager::asynchronousUpdate() * \param type : The type of the protocol. * \return The protocol that matches the given type. */ -Protocol* ProtocolManager::getProtocol(ProtocolType type) +std::shared_ptr ProtocolManager::getProtocol(ProtocolType type) { OneProtocolType &opt = m_all_protocols[type]; if (opt.isEmpty()) return NULL; diff --git a/src/network/protocol_manager.hpp b/src/network/protocol_manager.hpp index ed1834ea5..9712c3453 100644 --- a/src/network/protocol_manager.hpp +++ b/src/network/protocol_manager.hpp @@ -67,10 +67,10 @@ public: ProtocolRequestType m_type; /** The concerned protocol information. */ - Protocol *m_protocol; + std::shared_ptr m_protocol; public: - ProtocolRequest(ProtocolRequestType type, Protocol *protocol) + ProtocolRequest(ProtocolRequestType type, std::shared_ptr protocol) { m_type = type; m_protocol = protocol; @@ -80,7 +80,7 @@ public: ProtocolRequestType getType() const { return m_type; } // ------------------------------------------------------------------------ /** Returns the protocol for this request. */ - Protocol *getProtocol() { return m_protocol; } + std::shared_ptr getProtocol() { return m_protocol; } }; // class ProtocolRequest; // ============================================================================ @@ -144,9 +144,9 @@ private: class OneProtocolType { private: - Synchronised< std::vector > m_protocols; + Synchronised< std::vector > > m_protocols; public: - void removeProtocol(Protocol *p); + void removeProtocol(std::shared_ptr p); void requestTerminateAll(); bool notifyEvent(Event *event); void update(float dt, bool async); @@ -154,7 +154,8 @@ private: // -------------------------------------------------------------------- /** Returns the first protocol of a given type. It is assumed that * there is a protocol of that type. */ - Protocol *getFirstProtocol() { return m_protocols.getData()[0]; } + std::shared_ptr getFirstProtocol() + { return m_protocols.getData()[0]; } // -------------------------------------------------------------------- /** Returns if this protocol class handles connect events. Protocols * of the same class either all handle a connect event, or none, so @@ -180,7 +181,7 @@ private: /** Locks access to this list of all protocols of a certain type. */ void unlock() { m_protocols.unlock(); } // -------------------------------------------------------------------- - void addProtocol(Protocol *p) + void addProtocol(std::shared_ptr p) { m_protocols.getData().push_back(p); } // addProtocol @@ -222,11 +223,11 @@ private: bool sendEvent(Event* event); - virtual void startProtocol(Protocol *protocol); - virtual void terminateProtocol(Protocol *protocol); + virtual void startProtocol(std::shared_ptr protocol); + virtual void terminateProtocol(std::shared_ptr protocol); virtual void asynchronousUpdate(); - virtual void pauseProtocol(Protocol *protocol); - virtual void unpauseProtocol(Protocol *protocol); + virtual void pauseProtocol(std::shared_ptr protocol); + virtual void unpauseProtocol(std::shared_ptr protocol); public: // =========================================== @@ -235,11 +236,11 @@ public: virtual ~ProtocolManager(); void abort(); void propagateEvent(Event* event); - Protocol* getProtocol(ProtocolType type); - void requestStart(Protocol* protocol); - void requestPause(Protocol* protocol); - void requestUnpause(Protocol* protocol); - void requestTerminate(Protocol* protocol); + std::shared_ptr getProtocol(ProtocolType type); + void requestStart(std::shared_ptr protocol); + void requestPause(std::shared_ptr protocol); + void requestUnpause(std::shared_ptr protocol); + void requestTerminate(std::shared_ptr protocol); void findAndTerminate(ProtocolType type); void update(float dt); // ------------------------------------------------------------------------ diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index d0ec62168..d01e987d2 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -357,8 +357,7 @@ void ClientLobby::update(float dt) screen->push(); m_state = SELECTING_KARTS; - Protocol *p = new LatencyProtocol(); - p->requestStart(); + std::make_shared()->requestStart(); Log::info("LobbyProtocol", "LatencyProtocol started."); } break; @@ -370,7 +369,7 @@ void ClientLobby::update(float dt) break; case DONE: m_state = EXITING; - ProtocolManager::lock()->requestTerminate(this); + ProtocolManager::lock()->requestTerminate(shared_from_this()); break; case EXITING: break; diff --git a/src/network/protocols/connect_to_peer.cpp b/src/network/protocols/connect_to_peer.cpp index 70b505e16..6157a9dc3 100644 --- a/src/network/protocols/connect_to_peer.cpp +++ b/src/network/protocols/connect_to_peer.cpp @@ -40,7 +40,6 @@ ConnectToPeer::ConnectToPeer(uint32_t peer_id) : Protocol(PROTOCOL_CONNECTION) m_peer_address.clear(); m_peer_id = peer_id; m_state = NONE; - m_current_protocol = NULL; m_is_lan = false; setHandleConnections(true); } // ConnectToPeer(peer_id) @@ -56,7 +55,6 @@ ConnectToPeer::ConnectToPeer(const TransportAddress &address) // We don't need to find the peer address, so we can start // with the state when we found the peer address. m_state = RECEIVED_PEER_ADDRESS; - m_current_protocol = NULL; m_is_lan = true; setHandleConnections(true); } // ConnectToPeers(TransportAddress) @@ -99,7 +97,7 @@ void ConnectToPeer::asynchronousUpdate() { case NONE: { - m_current_protocol = new GetPeerAddress(m_peer_id, this); + m_current_protocol = std::make_shared(m_peer_id, this); m_current_protocol->requestStart(); // Pause this protocol till we receive an answer @@ -118,8 +116,7 @@ void ConnectToPeer::asynchronousUpdate() m_state = DONE; break; } - delete m_current_protocol; - m_current_protocol = 0; + m_current_protocol = nullptr; // Now we know the peer address. If it's a non-local host, start // the Ping protocol to keep the port available. We can't rely on @@ -130,7 +127,7 @@ void ConnectToPeer::asynchronousUpdate() NetworkConfig::get()->getMyAddress().getIP() ) || NetworkConfig::m_disable_lan ) { - m_current_protocol = new PingProtocol(m_peer_address, + m_current_protocol = std::make_shared(m_peer_address, /*time-between-ping*/2.0); ProtocolManager::lock()->requestStart(m_current_protocol); m_state = CONNECTING; @@ -202,7 +199,7 @@ void ConnectToPeer::asynchronousUpdate() { // Kill the ping protocol because we're connected m_current_protocol->requestTerminate(); - m_current_protocol = NULL; + m_current_protocol = nullptr; } m_state = DONE; break; diff --git a/src/network/protocols/connect_to_peer.hpp b/src/network/protocols/connect_to_peer.hpp index 6f5532fcf..87854b88c 100644 --- a/src/network/protocols/connect_to_peer.hpp +++ b/src/network/protocols/connect_to_peer.hpp @@ -34,7 +34,7 @@ protected: uint32_t m_peer_id; /** Pointer to the protocol which is monitored for state changes. */ - Protocol *m_current_protocol; + std::shared_ptr m_current_protocol; /** True if this is a LAN connection. */ bool m_is_lan; diff --git a/src/network/protocols/connect_to_server.cpp b/src/network/protocols/connect_to_server.cpp index 5095d7ed1..c950939be 100644 --- a/src/network/protocols/connect_to_server.cpp +++ b/src/network/protocols/connect_to_server.cpp @@ -82,7 +82,7 @@ ConnectToServer::~ConnectToServer() void ConnectToServer::setup() { Log::info("ConnectToServer", "SETUP"); - m_current_protocol = NULL; + m_current_protocol = nullptr; // In case of LAN we already have the server's and our ip address, // so we can immediately start requesting a connection. m_state = NetworkConfig::get()->isLAN() ? GOT_SERVER_ADDRESS : NONE; @@ -108,7 +108,7 @@ void ConnectToServer::asynchronousUpdate() Log::info("ConnectToServer", "Protocol starting"); // This protocol will write the public address of this // instance to STKHost. - m_current_protocol = new GetPublicAddress(this); + m_current_protocol = std::make_shared(this); m_current_protocol->requestStart(); // This protocol will be unpaused in the callback from // GetPublicAddress @@ -118,8 +118,8 @@ void ConnectToServer::asynchronousUpdate() } case GETTING_SELF_ADDRESS: { - delete m_current_protocol; // delete GetPublicAddress - m_current_protocol = NULL; + // drop GetPublicAddress + m_current_protocol = nullptr; registerWithSTKServer(); // Register us with STK server @@ -139,8 +139,7 @@ void ConnectToServer::asynchronousUpdate() case GOT_SERVER_ADDRESS: { assert(!m_quick_join); - delete m_current_protocol; - m_current_protocol = NULL; + m_current_protocol = nullptr; Log::info("ConnectToServer", "Server's address known"); // we're in the same lan (same public ip address) !! @@ -151,7 +150,7 @@ void ConnectToServer::asynchronousUpdate() "Server appears to be in the same LAN."); } m_state = REQUESTING_CONNECTION; - m_current_protocol = new RequestConnection(m_server_id); + m_current_protocol = std::make_shared(m_server_id); m_current_protocol->requestStart(); break; } @@ -160,8 +159,7 @@ void ConnectToServer::asynchronousUpdate() if (!m_current_protocol || m_current_protocol->getState() == PROTOCOL_STATE_TERMINATED) { - delete m_current_protocol; - m_current_protocol = NULL; + m_current_protocol = nullptr; // Server knows we want to connect Log::info("ConnectToServer", "Connection request made"); if (m_server_address.getIP() == 0 || @@ -171,7 +169,7 @@ void ConnectToServer::asynchronousUpdate() m_state = HIDING_ADDRESS; Log::error("ConnectToServer", "Server address is %s", m_server_address.toString().c_str()); - m_current_protocol = new HidePublicAddress(); + m_current_protocol = std::make_shared(); m_current_protocol->requestStart(); return; } @@ -187,7 +185,7 @@ void ConnectToServer::asynchronousUpdate() else { m_state = CONNECTING; - m_current_protocol = new PingProtocol(m_server_address, 2.0); + m_current_protocol = std::make_shared(m_server_address, 2.0); m_current_protocol->requestStart(); } } @@ -212,12 +210,11 @@ void ConnectToServer::asynchronousUpdate() // Kill the ping protocol because we're connected m_current_protocol->requestTerminate(); } - delete m_current_protocol; - m_current_protocol = NULL; + m_current_protocol = nullptr; // LAN networking does not use the stk server tables. if(NetworkConfig::get()->isWAN()) { - m_current_protocol = new HidePublicAddress(); + m_current_protocol = std::make_shared(); m_current_protocol->requestStart(); } m_state = HIDING_ADDRESS; @@ -230,18 +227,16 @@ void ConnectToServer::asynchronousUpdate() { if(m_current_protocol) { - delete m_current_protocol; - m_current_protocol = NULL; + m_current_protocol = nullptr; Log::info("ConnectToServer", "Address hidden"); } m_state = DONE; // lobby room protocol if we're connected only if(STKHost::get()->getPeers()[0]->isConnected()) { - ClientLobby *p = - LobbyProtocol::create(); - p->setAddress(m_server_address); - p->requestStart(); + auto cl = LobbyProtocol::create(); + cl->setAddress(m_server_address); + cl->requestStart(); } } break; diff --git a/src/network/protocols/connect_to_server.hpp b/src/network/protocols/connect_to_server.hpp index e69a8e370..42690fef0 100644 --- a/src/network/protocols/connect_to_server.hpp +++ b/src/network/protocols/connect_to_server.hpp @@ -32,7 +32,7 @@ private: uint32_t m_host_id; /** Protocol currently being monitored. */ - Protocol *m_current_protocol; + std::shared_ptr m_current_protocol; bool m_quick_join; /** State for finite state machine. */ diff --git a/src/network/protocols/game_protocol.cpp b/src/network/protocols/game_protocol.cpp index a4aeaa028..c642e9882 100644 --- a/src/network/protocols/game_protocol.cpp +++ b/src/network/protocols/game_protocol.cpp @@ -32,6 +32,21 @@ #include "network/stk_peer.hpp" #include "utils/log.hpp" +// ============================================================================ +std::weak_ptr GameProtocol::m_game_protocol; +// ============================================================================ +std::shared_ptr GameProtocol::createInstance() +{ + if (!emptyInstance()) + { + Log::fatal("GameProtocol", "Create only 1 instance of GameProtocol!"); + return NULL; + } + auto gm = std::make_shared(); + m_game_protocol = gm; + return gm; +} // createInstance + //----------------------------------------------------------------------------- /** Constructor. Allocates the buffer for events to send to the server. */ GameProtocol::GameProtocol() diff --git a/src/network/protocols/game_protocol.hpp b/src/network/protocols/game_protocol.hpp index 5f211764f..831f5016f 100644 --- a/src/network/protocols/game_protocol.hpp +++ b/src/network/protocols/game_protocol.hpp @@ -34,7 +34,6 @@ class STKPeer; class GameProtocol : public Protocol , public EventRewinder - , public Singleton { private: @@ -69,6 +68,7 @@ private: void handleControllerAction(Event *event); void handleState(Event *event); void handleAdjustTime(Event *event); + static std::weak_ptr m_game_protocol; public: GameProtocol(); virtual ~GameProtocol(); @@ -90,6 +90,18 @@ public: // ------------------------------------------------------------------------ virtual void asynchronousUpdate() OVERRIDE {} // ------------------------------------------------------------------------ + static std::shared_ptr createInstance(); + // ------------------------------------------------------------------------ + static bool emptyInstance() + { + return m_game_protocol.expired(); + } // emptyInstance + // ------------------------------------------------------------------------ + static std::shared_ptr lock() + { + return m_game_protocol.lock(); + } // lock + }; // class GameProtocol #endif // GAME_PROTOCOL_HPP diff --git a/src/network/protocols/lobby_protocol.cpp b/src/network/protocols/lobby_protocol.cpp index 4b4f489b0..e1f7a444c 100644 --- a/src/network/protocols/lobby_protocol.cpp +++ b/src/network/protocols/lobby_protocol.cpp @@ -33,7 +33,7 @@ #include "race/race_manager.hpp" #include "states_screens/state_manager.hpp" -LobbyProtocol *LobbyProtocol::m_lobby = NULL; +std::weak_ptr LobbyProtocol::m_lobby; LobbyProtocol::LobbyProtocol(CallbackObject* callback_object) : Protocol(PROTOCOL_LOBBY_ROOM, callback_object) @@ -125,8 +125,8 @@ void LobbyProtocol::loadWorld() // Load the actual world. m_game_setup->getRaceConfig()->loadWorld(); World::getWorld()->setNetworkWorld(true); - GameProtocol::getInstance()->requestStart(); - (new GameEventsProtocol())->requestStart(); + GameProtocol::createInstance()->requestStart(); + std::make_shared()->requestStart(); } // loadWorld diff --git a/src/network/protocols/lobby_protocol.hpp b/src/network/protocols/lobby_protocol.hpp index 8b00cbb7f..52f31bf27 100644 --- a/src/network/protocols/lobby_protocol.hpp +++ b/src/network/protocols/lobby_protocol.hpp @@ -63,28 +63,33 @@ public: }; protected: - static LobbyProtocol *m_lobby; + static std::weak_ptr m_lobby; /** The game setup. */ GameSetup* m_game_setup; - public: /** Creates either a client or server lobby protocol as a singleton. */ - template static S* create() + template static std::shared_ptr create() { - assert(m_lobby == NULL); - m_lobby = new S(); - return dynamic_cast(m_lobby); + assert(m_lobby.expired()); + auto ret = std::make_shared(); + m_lobby = ret; + return std::dynamic_pointer_cast(ret); } // create // ------------------------------------------------------------------------ /** Returns the singleton client or server lobby protocol. */ - static LobbyProtocol *get() + template static std::shared_ptr get() { - assert(m_lobby); - return m_lobby; + if (std::shared_ptr lp = m_lobby.lock()) + { + std::shared_ptr new_type = std::dynamic_pointer_cast(lp); + if (new_type) + return new_type; + } + return nullptr; } // get // ------------------------------------------------------------------------ diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 3ae85c425..62ec55406 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -115,7 +115,7 @@ void ServerLobby::setup() m_state = NetworkConfig::get()->isLAN() ? ACCEPTING_CLIENTS : INIT_WAN; m_selection_enabled = false; - m_current_protocol = NULL; + m_current_protocol = nullptr; Log::info("ServerLobby", "Starting the protocol."); // Initialise the data structures to detect if all clients and @@ -202,7 +202,7 @@ void ServerLobby::update(float dt) { case INIT_WAN: // Start the protocol to find the public ip address. - m_current_protocol = new GetPublicAddress(this); + m_current_protocol = std::make_shared(this); m_current_protocol->requestStart(); m_state = GETTING_PUBLIC_ADDRESS; // The callback from GetPublicAddress will wake this protocol up @@ -212,7 +212,7 @@ void ServerLobby::update(float dt) { Log::debug("ServerLobby", "Public address known."); // Free GetPublicAddress protocol - delete m_current_protocol; + m_current_protocol = nullptr; // Register this server with the STK server. This will block // this thread, but there is no need for the protocol manager @@ -420,8 +420,7 @@ void ServerLobby::startSelection(const Event *event) m_state = SELECTING; WaitingForOthersScreen::getInstance()->push(); - Protocol *p = new LatencyProtocol(); - p->requestStart(); + std::make_shared()->requestStart(); Log::info("LobbyProtocol", "LatencyProtocol started."); } // startSelection @@ -468,8 +467,7 @@ void ServerLobby::checkIncomingConnectionRequests() users_xml->getNode(i)->get("id", &id); Log::debug("ServerLobby", "User with id %d wants to connect.", id); - Protocol *p = new ConnectToPeer(id); - p->requestStart(); + std::make_shared(id)->requestStart(); } delete request; } // checkIncomingConnectionRequests diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 744aefae3..fd58f2bb7 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -54,7 +54,7 @@ private: * seconds), which is the real time at which the server should start. */ double m_server_delay; - Protocol *m_current_protocol; + std::shared_ptr m_current_protocol; bool m_selection_enabled; /** Counts how many players are ready to go on. */ diff --git a/src/network/race_event_manager.cpp b/src/network/race_event_manager.cpp index 6af40c6c0..20c79c0ef 100644 --- a/src/network/race_event_manager.cpp +++ b/src/network/race_event_manager.cpp @@ -73,7 +73,7 @@ bool RaceEventManager::isRaceOver() // ---------------------------------------------------------------------------- void RaceEventManager::kartFinishedRace(AbstractKart *kart, float time) { - GameEventsProtocol* protocol = static_cast( + auto protocol = std::static_pointer_cast( ProtocolManager::lock()->getProtocol(PROTOCOL_GAME_EVENTS)); protocol->kartFinishedRace(kart, time); } // kartFinishedRace @@ -89,7 +89,7 @@ void RaceEventManager::collectedItem(Item *item, AbstractKart *kart) // this is only called in the server assert(NetworkConfig::get()->isServer()); - GameEventsProtocol* protocol = static_cast( + auto protocol = std::static_pointer_cast( ProtocolManager::lock()->getProtocol(PROTOCOL_GAME_EVENTS)); protocol->collectedItem(item,kart); } // collectedItem diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 4aa876812..2468c2562 100755 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -191,7 +191,7 @@ void RewindManager::update(float dt) PROFILER_PUSH_CPU_MARKER("RewindManager - save state", 0x20, 0x7F, 0x20); // Save state - GameProtocol::getInstance()->startNewState(); + GameProtocol::lock()->startNewState(); AllRewinder::const_iterator rewinder; for(rewinder=m_all_rewinder.begin(); rewinder!=m_all_rewinder.end(); ++rewinder) { @@ -202,14 +202,14 @@ void RewindManager::update(float dt) // Add to the previously created container m_rewind_queue.addLocalState(*rewinder, buffer, /*confirmed*/true, World::getWorld()->getTime()); - GameProtocol::getInstance()->addState(buffer); + GameProtocol::lock()->addState(buffer); } // size >= 0 else delete buffer; // NULL or 0 byte buffer } PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("RewindManager - send state", 0x20, 0x7F, 0x40); - GameProtocol::getInstance()->sendState(); + GameProtocol::lock()->sendState(); PROFILER_POP_CPU_MARKER(); m_last_saved_state = time; } // update diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index 3bf92ed4f..d2719bade 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -257,8 +257,7 @@ STKHost::STKHost(uint32_t server_id, uint32_t host_id) "an ENet client host."); } - Protocol *connect = new ConnectToServer(server_id, host_id); - connect->requestStart(); + std::make_shared(server_id, host_id)->requestStart(); } // STKHost // ---------------------------------------------------------------------------- @@ -288,8 +287,7 @@ STKHost::STKHost(const irr::core::stringw &server_name) } startListening(); - Protocol *p = LobbyProtocol::create(); - ProtocolManager::lock()->requestStart(p); + ProtocolManager::lock()->requestStart(LobbyProtocol::create()); } // STKHost(server_name) @@ -608,8 +606,7 @@ void STKHost::handleDirectSocketRequest() { // In case of a LAN connection, we only allow connections from // a LAN address (192.168*, ..., and 127.*). - Protocol *c = new ConnectToPeer(sender); - c->requestStart(); + std::make_shared(sender)->requestStart(); } else Log::info("STKHost", "Received unknown command '%s'", diff --git a/src/race/race_manager.cpp b/src/race/race_manager.cpp index 2daeb829e..0501ce06f 100644 --- a/src/race/race_manager.cpp +++ b/src/race/race_manager.cpp @@ -562,7 +562,7 @@ void RaceManager::startNextRace() // the world has been setup if(NetworkConfig::get()->isNetworking()) { - LobbyProtocol *lobby = LobbyProtocol::get(); + auto lobby = LobbyProtocol::get(); assert(lobby); lobby->finishedLoadingWorld(); } diff --git a/src/states_screens/network_kart_selection.cpp b/src/states_screens/network_kart_selection.cpp index 559e165f2..2769fac66 100644 --- a/src/states_screens/network_kart_selection.cpp +++ b/src/states_screens/network_kart_selection.cpp @@ -144,9 +144,7 @@ void NetworkKartSelectionScreen::playerConfirm(const int playerID) } if(playerID == PLAYER_ID_GAME_MASTER) // self { - - LobbyProtocol* protocol = LobbyProtocol::get(); - ClientLobby *clrp = dynamic_cast(protocol); + auto clrp = LobbyProtocol::get(); assert(clrp); // FIXME SPLITSCREEN: we need to supply the global player id of the // player selecting the kart here. For now ... just vote the same kart @@ -196,10 +194,7 @@ void NetworkKartSelectionScreen::playerSelected(uint8_t player_id, // to the server. if(STKHost::get()->isAuthorisedToControl()) { - Protocol* protocol = ProtocolManager::lock() - ->getProtocol(PROTOCOL_LOBBY_ROOM); - ClientLobby* clrp = - dynamic_cast(protocol); + auto clrp = LobbyProtocol::get(); assert(clrp); // FIXME: for now we submit a vote from the authorised user // for the various modes based on the settings in the race manager. @@ -214,7 +209,7 @@ void NetworkKartSelectionScreen::playerSelected(uint8_t player_id, clrp->voteMinor(id, race_manager->getMinorMode()); clrp->voteReversed(id, race_manager->getReverseTrack()); clrp->voteRaceCount(id, 1); - clrp->voteLaps(id, 3); + clrp->voteLaps(id, 1); } //WaitingForOthersScreen::getInstance()->push(); //return; @@ -228,10 +223,8 @@ bool NetworkKartSelectionScreen::onEscapePressed() // then remove the lobby screen (you left the server) StateManager::get()->popMenu(); ServerSelection::getInstance()->refresh(); - Protocol *lobby = LobbyProtocol::get(); // notify the server that we left - ClientLobby* clrp = - dynamic_cast(lobby); + auto clrp = LobbyProtocol::get(); if (clrp) clrp->leave(); return true; // remove the screen diff --git a/src/states_screens/networking_lobby.cpp b/src/states_screens/networking_lobby.cpp index 2a3a68b84..62d751d9f 100644 --- a/src/states_screens/networking_lobby.cpp +++ b/src/states_screens/networking_lobby.cpp @@ -162,8 +162,7 @@ void NetworkingLobby::eventCallback(Widget* widget, const std::string& name, { if(NetworkConfig::get()->isServer()) { - Protocol *p = LobbyProtocol::get(); - ServerLobby* slrp = dynamic_cast(p); + auto slrp = LobbyProtocol::get(); slrp->startSelection(); } else // client @@ -198,10 +197,9 @@ void NetworkingLobby::tearDown() bool NetworkingLobby::onEscapePressed() { // notify the server that we left - ClientLobby* protocol = - dynamic_cast(LobbyProtocol::get()); - if (protocol) - protocol->leave(); + auto clrp = LobbyProtocol::get(); + if (clrp) + clrp->leave(); STKHost::get()->shutdown(); return true; // close the screen } // onEscapePressed diff --git a/src/states_screens/online_profile_servers.cpp b/src/states_screens/online_profile_servers.cpp index d7ca56184..1e9a9c355 100644 --- a/src/states_screens/online_profile_servers.cpp +++ b/src/states_screens/online_profile_servers.cpp @@ -149,7 +149,7 @@ void OnlineProfileServers::doQuickPlay() { delete join_request; NetworkingLobby::getInstance()->push(); - ConnectToServer *cts = new ConnectToServer(server->getServerId(), + auto cts = std::make_shared(server->getServerId(), server->getHostId()); ProtocolManager::lock()->requestStart(cts); } diff --git a/src/states_screens/race_result_gui.cpp b/src/states_screens/race_result_gui.cpp index 8df14e439..20830e17a 100644 --- a/src/states_screens/race_result_gui.cpp +++ b/src/states_screens/race_result_gui.cpp @@ -348,9 +348,7 @@ void RaceResultGUI::eventCallback(GUIEngine::Widget* widget, if (name == "middle") // Continue button (return to server lobby) { // Signal to the server that this client is back in the lobby now. - Protocol* protocol = LobbyProtocol::get(); - ClientLobby* clrp = - dynamic_cast(protocol); + auto clrp = LobbyProtocol::get(); if(clrp) clrp->doneWithResults(); backToLobby(); diff --git a/src/states_screens/tracks_screen.cpp b/src/states_screens/tracks_screen.cpp index cb207ac37..259cec128 100644 --- a/src/states_screens/tracks_screen.cpp +++ b/src/states_screens/tracks_screen.cpp @@ -89,10 +89,9 @@ void TracksScreen::eventCallback(Widget* widget, const std::string& name, { if(STKHost::existHost()) { - Protocol* protocol = LobbyProtocol::get(); - ClientLobby* clrp = - dynamic_cast(protocol); - assert(clrp); // server never shows the track screen. + auto clrp = LobbyProtocol::get(); + // server never shows the track screen. + assert(clrp); // FIXME SPLITSCREEN: we need to supply the global player id of the // player selecting the track here. For now ... just vote the same // track for each local player. @@ -200,11 +199,10 @@ void TracksScreen::buildTrackList() // First build a list of all tracks to be displayed // (e.g. exclude arenas, ...) bool is_network = (STKHost::existHost()); - ClientLobby* clrp = NULL; + std::shared_ptr clrp; if (is_network) { - Protocol* protocol = LobbyProtocol::get(); - clrp = dynamic_cast(protocol); + clrp = LobbyProtocol::get(); assert(clrp); } PtrVector tracks; From 6fd7fb0e0eb29bd4e7f2bf65556bb5f30f4f6518 Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 18 Feb 2018 13:24:29 +0800 Subject: [PATCH 125/413] Allow reconnecting to lan servers as many time as possible --- src/states_screens/network_kart_selection.cpp | 1 + src/states_screens/online_lan.cpp | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/states_screens/network_kart_selection.cpp b/src/states_screens/network_kart_selection.cpp index 2769fac66..a8da06242 100644 --- a/src/states_screens/network_kart_selection.cpp +++ b/src/states_screens/network_kart_selection.cpp @@ -227,5 +227,6 @@ bool NetworkKartSelectionScreen::onEscapePressed() auto clrp = LobbyProtocol::get(); if (clrp) clrp->leave(); + STKHost::get()->shutdown(); return true; // remove the screen } // onEscapePressed diff --git a/src/states_screens/online_lan.cpp b/src/states_screens/online_lan.cpp index 9e22fa104..b71bd67a4 100644 --- a/src/states_screens/online_lan.cpp +++ b/src/states_screens/online_lan.cpp @@ -75,7 +75,7 @@ void OnlineLanScreen::eventCallback(Widget* widget, const std::string& name, con { if (name == "back") { - StateManager::get()->popMenu(); + StateManager::get()->escapePressed(); return; } if (name == "lan") @@ -106,7 +106,6 @@ void OnlineLanScreen::eventCallback(Widget* widget, const std::string& name, con bool OnlineLanScreen::onEscapePressed() { NetworkConfig::get()->unsetNetworking(); - //StateManager::get()->popMenu(); return true; } // onEscapePressed From c5b986e87408a47c14d6bf509a4de3c202eb1f0d Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 18 Feb 2018 14:06:17 +0800 Subject: [PATCH 126/413] Allow going back to online menu if server connection is lost --- src/main_loop.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 5e0c79a2a..b5d473b68 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -26,6 +26,7 @@ #include "graphics/irr_driver.hpp" #include "graphics/material_manager.hpp" #include "guiengine/engine.hpp" +#include "guiengine/message_queue.hpp" #include "input/input_manager.hpp" #include "input/wiimote_manager.hpp" #include "modes/profile_world.hpp" @@ -38,6 +39,8 @@ #include "online/request_manager.hpp" #include "race/history.hpp" #include "race/race_manager.hpp" +#include "states_screens/main_menu_screen.hpp" +#include "states_screens/online_screen.hpp" #include "states_screens/state_manager.hpp" #include "utils/profiler.hpp" @@ -278,6 +281,17 @@ void MainLoop::run() STKHost::get()->requestedShutdown()) { STKHost::get()->shutdown(); + if (World::getWorld()) + { + race_manager->exitRace(); + } + GUIEngine::Screen* new_stack[] = + { + MainMenuScreen::getInstance(), OnlineScreen::getInstance(), NULL + }; + StateManager::get()->resetAndSetStack(new_stack); + NetworkConfig::get()->unsetNetworking(); + MessageQueue::add(MessageQueue::MT_ERROR, _("Connection to server is lost.")); } // Add a Time step entry to the rewind list, which can store all From e52ab5888cd6c0e73dba83746febd0e555d82cbb Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 18 Feb 2018 14:13:24 +0800 Subject: [PATCH 127/413] Don't crash when close STK window directly --- src/scriptengine/scriptstdstring.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/scriptengine/scriptstdstring.cpp b/src/scriptengine/scriptstdstring.cpp index b46cc3d21..1c4f1be13 100644 --- a/src/scriptengine/scriptstdstring.cpp +++ b/src/scriptengine/scriptstdstring.cpp @@ -66,7 +66,10 @@ public: { // The script engine must release each string // constant that it has requested - assert(stringCache.size() == 0); + if (!stringCache.empty()) + { + printf("Scripting engine was not cleared properly.\n"); + } } const void *GetStringConstant(const char *data, asUINT length) From e393e3d4f07589d679560580e1f26bfecdfb1676 Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 18 Feb 2018 14:50:06 +0800 Subject: [PATCH 128/413] With shared_ptr protocol is not leakable --- src/network/protocol.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/network/protocol.hpp b/src/network/protocol.hpp index 0f3d0794e..2b9bcd9c9 100644 --- a/src/network/protocol.hpp +++ b/src/network/protocol.hpp @@ -23,7 +23,6 @@ #ifndef PROTOCOL_HPP #define PROTOCOL_HPP -#include "utils/leak_check.hpp" #include "utils/no_copy.hpp" #include "utils/types.hpp" @@ -94,7 +93,6 @@ public: class Protocol : public std::enable_shared_from_this, public NoCopy { - LEAK_CHECK() protected: /** The type of the protocol. */ ProtocolType m_type; From 4ab823cfbcf1dde69497d6a18f263481ea09afa5 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 19 Feb 2018 11:36:32 +0800 Subject: [PATCH 129/413] Move lan network socket to thread main loop, fixed leaking of it too Also call enet_deinitialize when destroy STKHost --- src/network/stk_host.cpp | 22 +++++++++++++--------- src/network/stk_host.hpp | 5 +---- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index d2719bade..a8f889fda 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -299,7 +299,6 @@ void STKHost::init() { m_shutdown = false; m_network = NULL; - m_lan_network = NULL; m_game_setup = NULL; m_is_registered = false; m_error_message = ""; @@ -350,6 +349,7 @@ STKHost::~STKHost() stopListening(); delete m_network; + enet_deinitialize(); } // ~STKHost //----------------------------------------------------------------------------- @@ -493,19 +493,22 @@ void STKHost::mainLoop() ENetEvent event; ENetHost* host = m_network->getENetHost(); - if(NetworkConfig::get()->isServer() && + // A separate network connection (socket) to handle LAN requests. + Network* lan_network = NULL; + + if (NetworkConfig::get()->isServer() && (NetworkConfig::get()->isLAN() || NetworkConfig::get()->isPublicServer()) ) { TransportAddress address(0, NetworkConfig::get()->getServerDiscoveryPort()); ENetAddress eaddr = address.toEnetAddress(); - m_lan_network = new Network(1, 1, 0, 0, &eaddr); + lan_network = new Network(1, 1, 0, 0, &eaddr); } while (m_exit_flag.test_and_set()) { - if(m_lan_network) + if (lan_network) { - handleDirectSocketRequest(); + handleDirectSocketRequest(lan_network); } // if discovery host while (enet_host_service(host, &event, 20) != 0) @@ -552,9 +555,10 @@ void STKHost::mainLoop() // notify for the event now. pm->propagateEvent(stk_event); - + } // while enet_host_service } // while m_exit_flag.test_and_set() + delete lan_network; Log::info("STKHost", "Listening has been stopped"); } // mainLoop @@ -567,13 +571,13 @@ void STKHost::mainLoop() * message is received, will answer with a message containing server details * (and sender IP address and port). */ -void STKHost::handleDirectSocketRequest() +void STKHost::handleDirectSocketRequest(Network* lan_network) { const int LEN=2048; char buffer[LEN]; TransportAddress sender; - int len = m_lan_network->receiveRawPacket(buffer, LEN, &sender, 1); + int len = lan_network->receiveRawPacket(buffer, LEN, &sender, 1); if(len<=0) return; BareNetworkString message(buffer, len); std::string command; @@ -600,7 +604,7 @@ void STKHost::handleDirectSocketRequest() s.addUInt16(sender.getPort()); s.addUInt16((uint16_t)race_manager->getMinorMode()); s.addUInt8((uint8_t)race_manager->getDifficulty()); - m_lan_network->sendRawPacket(s, sender); + lan_network->sendRawPacket(s, sender); } // if message is server-requested else if (command == "connection-request") { diff --git a/src/network/stk_host.hpp b/src/network/stk_host.hpp index ec9620b80..ac78aa3a2 100644 --- a/src/network/stk_host.hpp +++ b/src/network/stk_host.hpp @@ -67,9 +67,6 @@ private: /** ENet host interfacing sockets. */ Network* m_network; - /** A separate network connection (socket) to handle LAN requests. */ - Network *m_lan_network; - /** Network console */ NetworkConsole *m_network_console; @@ -110,7 +107,7 @@ private: STKHost(const irr::core::stringw &server_name); virtual ~STKHost(); void init(); - void handleDirectSocketRequest(); + void handleDirectSocketRequest(Network* lan_network); // ------------------------------------------------------------------------ void mainLoop(); From 04a6f6d08b02313eb66392a847f14401c01de30d Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 20 Feb 2018 10:11:09 +0800 Subject: [PATCH 130/413] Warn user about multiple instance of STK server --- src/network/network.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index 281e5b9be..d8ec5f4af 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -55,7 +55,8 @@ Network::Network(int peer_count, int channel_limit, if (!m_host) { Log::fatal("Network", "An error occurred while trying to create an " - "ENet client host."); + "ENet client host, maybe you started multiple instance " + "of STK server?"); } } // Network @@ -215,4 +216,4 @@ void Network::closeLog() m_log_file.getData() = NULL; m_log_file.unlock(); } -} // closeLog \ No newline at end of file +} // closeLog From 14b401b54e2acb908f775f0e63b1be98f0fe1f26 Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 20 Feb 2018 11:14:57 +0800 Subject: [PATCH 131/413] Use std atomic for server state --- src/network/protocols/server_lobby.cpp | 7 ++++--- src/network/protocols/server_lobby.hpp | 11 +++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 62ec55406..df45a08ff 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -198,7 +198,7 @@ bool ServerLobby::notifyEventAsynchronous(Event* event) */ void ServerLobby::update(float dt) { - switch (m_state) + switch (m_state.load()) { case INIT_WAN: // Start the protocol to find the public ip address. @@ -382,7 +382,8 @@ void ServerLobby::startSelection(const Event *event) if (m_state != ACCEPTING_CLIENTS) { Log::warn("ServerLobby", - "Received startSelection while being in state %d", m_state); + "Received startSelection while being in state %d", + m_state.load()); return; } if(event && !event->getPeer()->isAuthorised()) @@ -730,7 +731,7 @@ void ServerLobby::kartSelectionRequested(Event* event) if(m_state!=SELECTING) { Log::warn("Server", "Received kart selection while in state %d.", - m_state); + m_state.load()); return; } diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index fd58f2bb7..82dbf379c 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -5,14 +5,15 @@ #include "utils/cpp2011.hpp" #include "utils/synchronised.hpp" +#include #include class ServerLobby : public LobbyProtocol , public CallbackObject { -private: +public: /* The state for a small finite state machine. */ - enum + enum ServerState : unsigned int { INIT_WAN, // Start state for WAN game GETTING_PUBLIC_ADDRESS, // Waiting to receive its public ip address @@ -27,7 +28,9 @@ private: RESULT_DISPLAY, // Show result screen DONE, // shutting down server EXITING - } m_state; + }; +private: + std::atomic m_state; /** Available karts and tracks for all clients, this will be initialized * with data in server first. */ @@ -94,7 +97,7 @@ public: void checkIncomingConnectionRequests(); void checkRaceFinished(); void finishedLoadingWorld(); - + ServerState getCurrentState() const { return m_state.load(); } virtual void callback(Protocol *protocol) OVERRIDE; }; // class ServerLobby From 22c755a328ab6e7927261dcc1bea6ae8ff21a88a Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 20 Feb 2018 13:33:43 +0800 Subject: [PATCH 132/413] Show protocol name when it is terminated (easier for debugging) --- src/network/protocol_manager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/network/protocol_manager.cpp b/src/network/protocol_manager.cpp index 507415222..af7736e33 100644 --- a/src/network/protocol_manager.cpp +++ b/src/network/protocol_manager.cpp @@ -305,6 +305,8 @@ void ProtocolManager::terminateProtocol(std::shared_ptr protocol) opt.unlock(); protocol->setState(PROTOCOL_STATE_TERMINATED); protocol->terminated(); + Log::info("ProtocolManager", + "A %s protocol has been terminated.", typeid(*protocol).name()); } // terminateProtocol // ---------------------------------------------------------------------------- From 8daebe06e1436823779c46364ea941483c331015 Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 20 Feb 2018 15:11:58 +0800 Subject: [PATCH 133/413] Allow creating wan server in command line It can only be used with a saved online player, also fix a weird crash if server failed to create (wan is NULL) --- src/main.cpp | 31 +++++++++++++++++++++------- src/states_screens/online_screen.cpp | 8 ++++--- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 9dd2100a3..3453cc9e3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -579,7 +579,7 @@ void cmdLineHelp() // " --test-ai=n Use the test-ai for every n-th AI kart.\n" // " (so n=1 means all Ais will be the test ai)\n" // " - " --server=name Start a server (not a playing client).\n" + " --wan-server=name Start a Wan server (not a playing client).\n" " --public-server Allow direct connection to the server (without stk server)\n" " --lan-server=name Start a LAN server (not a playing client).\n" " --server-password= Sets a password for a server (both client&server).\n" @@ -1018,13 +1018,30 @@ int handleCmdLine() STKHost::create(); } - if(CommandLine::has("--server", &s)) + if (CommandLine::has("--wan-server", &s)) { - NetworkConfig::get()->setServerName(core::stringw(s.c_str())); - NetworkConfig::get()->setIsServer(true); - NetworkConfig::get()->setIsWAN(); - STKHost::create(); - Log::info("main", "Creating a WAN server '%s'.", s.c_str()); + PlayerProfile* player = PlayerManager::getCurrentPlayer(); + if (player && player->wasOnlineLastTime() && player->wasOnlineLastTime() && + player->hasSavedSession()) + { + while (true) + { + Online::RequestManager::get()->update(0.0f); + if (PlayerManager::getCurrentOnlineState() == PlayerProfile::OS_SIGNED_IN) + { + break; + } + } + NetworkConfig::get()->setServerName(core::stringw(s.c_str())); + NetworkConfig::get()->setIsServer(true); + NetworkConfig::get()->setIsWAN(); + STKHost::create(); + Log::info("main", "Creating a WAN server '%s'.", s.c_str()); + } + else + { + Log::warn("main", "No saved online player session to create a wan server"); + } } if (CommandLine::has("--lan-server", &s)) { diff --git a/src/states_screens/online_screen.cpp b/src/states_screens/online_screen.cpp index e0601c56e..629399389 100644 --- a/src/states_screens/online_screen.cpp +++ b/src/states_screens/online_screen.cpp @@ -71,7 +71,6 @@ void OnlineScreen::loadedFromFile() void OnlineScreen::beforeAddingWidget() { bool is_logged_in = false; - PlayerProfile *player = PlayerManager::getCurrentPlayer(); if (PlayerManager::getCurrentOnlineState() == PlayerProfile::OS_GUEST || PlayerManager::getCurrentOnlineState() == PlayerProfile::OS_SIGNED_IN) { @@ -79,8 +78,11 @@ void OnlineScreen::beforeAddingWidget() } IconButtonWidget* wan = getWidget("wan"); - wan->setActive(is_logged_in); - wan->setVisible(is_logged_in); + if (wan) + { + wan->setActive(is_logged_in); + wan->setVisible(is_logged_in); + } } // beforeAddingWidget // ---------------------------------------------------------------------------- From 1458f3ef8e99dbd58270da42deb78de4ca6c8ff3 Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 22 Feb 2018 15:10:30 +0800 Subject: [PATCH 134/413] Fix wan connection, move get public address from stun to stk host --- src/main.cpp | 4 +- src/main_loop.cpp | 37 +- src/network/protocols/connect_to_peer.cpp | 151 +++----- src/network/protocols/connect_to_peer.hpp | 23 +- src/network/protocols/connect_to_server.cpp | 322 ++++++++---------- src/network/protocols/connect_to_server.hpp | 16 +- src/network/protocols/get_peer_address.cpp | 16 +- src/network/protocols/get_peer_address.hpp | 5 +- src/network/protocols/get_public_address.cpp | 88 ++--- src/network/protocols/get_public_address.hpp | 17 +- src/network/protocols/hide_public_address.cpp | 2 +- src/network/protocols/ping_protocol.cpp | 9 +- src/network/protocols/ping_protocol.hpp | 5 +- src/network/protocols/server_lobby.cpp | 37 +- src/network/protocols/server_lobby.hpp | 3 - src/network/stk_host.cpp | 263 ++++++++++++-- src/network/stk_host.hpp | 23 +- src/network/transport_address.hpp | 2 + 18 files changed, 574 insertions(+), 449 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 3453cc9e3..03e5dbd64 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1027,6 +1027,7 @@ int handleCmdLine() while (true) { Online::RequestManager::get()->update(0.0f); + StkTime::sleep(1); if (PlayerManager::getCurrentOnlineState() == PlayerProfile::OS_SIGNED_IN) { break; @@ -1074,9 +1075,6 @@ int handleCmdLine() if(CommandLine::has("--password", &s)) password = s.c_str(); - if (CommandLine::has("--my-address", &s)) - GetPublicAddress::setMyIPAddress(s); - /** Disable detection of LAN connection when connecting via WAN. This is * mostly a debugging feature to force using WAN connection. */ if (CommandLine::has("--disable-lan")) diff --git a/src/main_loop.cpp b/src/main_loop.cpp index b5d473b68..352325e3d 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -302,31 +302,32 @@ void MainLoop::run() ->addNextTimeStep(World::getWorld()->getTime(), dt); } - if (!m_abort && !ProfileWorld::isNoGraphics()) + if (!m_abort) { float frame_duration = num_steps * dt; + if (!ProfileWorld::isNoGraphics()) + { + // Render the previous frame, and also handle all user input. + PROFILER_PUSH_CPU_MARKER("IrrDriver update", 0x00, 0x00, 0x7F); + irr_driver->update(frame_duration); + PROFILER_POP_CPU_MARKER(); - // Render the previous frame, and also handle all user input. - PROFILER_PUSH_CPU_MARKER("IrrDriver update", 0x00, 0x00, 0x7F); - irr_driver->update(frame_duration); - PROFILER_POP_CPU_MARKER(); - - PROFILER_PUSH_CPU_MARKER("Input/GUI", 0x7F, 0x00, 0x00); -#ifdef ENABLE_WIIUSE - wiimote_manager->update(); -#endif - input_manager->update(frame_duration); - GUIEngine::update(frame_duration); - PROFILER_POP_CPU_MARKER(); - - PROFILER_PUSH_CPU_MARKER("Music", 0x7F, 0x00, 0x00); - SFXManager::get()->update(); - PROFILER_POP_CPU_MARKER(); + PROFILER_PUSH_CPU_MARKER("Input/GUI", 0x7F, 0x00, 0x00); + #ifdef ENABLE_WIIUSE + wiimote_manager->update(); + #endif + input_manager->update(frame_duration); + GUIEngine::update(frame_duration); + PROFILER_POP_CPU_MARKER(); + PROFILER_PUSH_CPU_MARKER("Music", 0x7F, 0x00, 0x00); + SFXManager::get()->update(); + PROFILER_POP_CPU_MARKER(); + } + // Some protocols in network will use RequestManager PROFILER_PUSH_CPU_MARKER("Database polling update", 0x00, 0x7F, 0x7F); Online::RequestManager::get()->update(frame_duration); PROFILER_POP_CPU_MARKER(); - } for(int i=0; igetType() == EVENT_TYPE_CONNECTED) @@ -97,93 +91,81 @@ void ConnectToPeer::asynchronousUpdate() { case NONE: { - m_current_protocol = std::make_shared(m_peer_id, this); + m_current_protocol = std::make_shared(m_peer_id); m_current_protocol->requestStart(); - - // Pause this protocol till we receive an answer - // The GetPeerAddress protocol will change the state and - // unpause this protocol - requestPause(); m_state = RECEIVED_PEER_ADDRESS; break; } case RECEIVED_PEER_ADDRESS: { - if (m_peer_address.getIP() == 0 || m_peer_address.getPort() == 0) + // Wait until we have peer address + auto get_peer_address = + std::dynamic_pointer_cast(m_current_protocol); + assert(get_peer_address); + if (get_peer_address->getAddress().isUnset()) + return; + m_peer_address.copy(get_peer_address->getAddress()); + m_current_protocol = nullptr; + if (m_peer_address.isUnset()) { Log::error("ConnectToPeer", "The peer you want to connect to has hidden his address."); m_state = DONE; break; } - m_current_protocol = nullptr; - // Now we know the peer address. If it's a non-local host, start - // the Ping protocol to keep the port available. We can't rely on - // STKHost::isLAN(), since we might get a LAN connection even if - // the server itself accepts connections from anywhere. - if ( (!m_is_lan && - m_peer_address.getIP() != - NetworkConfig::get()->getMyAddress().getIP() ) || - NetworkConfig::m_disable_lan ) - { - m_current_protocol = std::make_shared(m_peer_address, - /*time-between-ping*/2.0); - ProtocolManager::lock()->requestStart(m_current_protocol); - m_state = CONNECTING; - } - else - { - m_broadcast_count = 0; - // Make sure we trigger the broadcast operation next - m_time_last_broadcast = float(StkTime::getRealTime()-100.0f); - m_state = WAIT_FOR_LAN; - } + m_state = WAIT_FOR_CONNECTION; + resetTimer(); break; } - case WAIT_FOR_LAN: + case WAIT_FOR_CONNECTION: { - // Broadcast once per second - if (StkTime::getRealTime() < m_time_last_broadcast + 1.0f) + // Each 2 second for a ping or broadcast + if (m_timer > m_timer + std::chrono::seconds(2)) { - break; - } - m_time_last_broadcast = float(StkTime::getRealTime()); - m_broadcast_count++; - if (m_broadcast_count > 100) - { - // Not much we can do about if we don't receive the client - // connection - it could have stopped, lost network, ... - // Terminate this protocol. - Log::error("ConnectToPeer", "Time out trying to connect to %s", - m_peer_address.toString().c_str()); - requestTerminate(); - } + resetTimer(); + // Now we know the peer address. If it's a non-local host, start + // the Ping protocol to keep the port available. We can't rely + // on STKHost::isLAN(), since we might get a LAN connection even + // if the server itself accepts connections from anywhere. + if ((!m_is_lan && + m_peer_address.getIP() != + STKHost::get()->getPublicAddress().getIP()) || + NetworkConfig::m_disable_lan) + { + BareNetworkString data; + data.addUInt8(0); + STKHost::get()->sendRawPacket(data, m_peer_address); + } - // Otherwise we are in the same LAN (same public ip address). - // Just send a broadcast packet with the string aloha_stk inside, - // the client will know our ip address and will connect - TransportAddress broadcast_address; - if(NetworkConfig::get()->isWAN()) - { - broadcast_address.setIP(-1); // 255.255.255.255 - broadcast_address.setPort(m_peer_address.getPort()); - } - else + // Send a broadcast packet with the string aloha_stk inside, + // the client will know our ip address and will connect + // The wan remote should already start its ping message to us now + // so we can send packet directly to it. + TransportAddress broadcast_address; broadcast_address.copy(m_peer_address); + BareNetworkString aloha(std::string("aloha_stk")); + STKHost::get()->sendRawPacket(aloha, broadcast_address); + Log::info("ConnectToPeer", "Broadcast aloha sent."); + StkTime::sleep(1); - broadcast_address.copy(m_peer_address); + broadcast_address.setIP(0x7f000001); // 127.0.0.1 (localhost) + broadcast_address.setPort(m_peer_address.getPort()); + STKHost::get()->sendRawPacket(aloha, broadcast_address); + Log::info("ConnectToPeer", "Broadcast aloha to self."); - BareNetworkString aloha(std::string("aloha_stk")); - STKHost::get()->sendRawPacket(aloha, broadcast_address); - Log::info("ConnectToPeer", "Broadcast aloha sent."); - StkTime::sleep(1); - - broadcast_address.setIP(0x7f000001); // 127.0.0.1 (localhost) - broadcast_address.setPort(m_peer_address.getPort()); - STKHost::get()->sendRawPacket(aloha, broadcast_address); - Log::info("ConnectToPeer", "Broadcast aloha to self."); + // 30 seconds timeout + if (m_tried_connection++ > 15) + { + // Not much we can do about if we don't receive the client + // connection - it could have stopped, lost network, ... + // Terminate this protocol. + Log::error("ConnectToPeer", "Time out trying to connect to %s", + m_peer_address.toString().c_str()); + requestTerminate(); + } + } break; } case CONNECTING: // waiting for the peer to connect @@ -193,14 +175,6 @@ void ConnectToPeer::asynchronousUpdate() break; case CONNECTED: { - // If the ping protocol is there for NAT traversal terminate it. - // Ping is not running when connecting to a LAN peer. - if (m_current_protocol) - { - // Kill the ping protocol because we're connected - m_current_protocol->requestTerminate(); - m_current_protocol = nullptr; - } m_state = DONE; break; } @@ -212,16 +186,3 @@ void ConnectToPeer::asynchronousUpdate() break; } } // asynchronousUpdate - -// ---------------------------------------------------------------------------- -/** Callback from the GetPeerAddress protocol. It copies the received peer - * address so that it can be used in the next states of the connection - * protocol. - */ -void ConnectToPeer::callback(Protocol *protocol) -{ - assert(m_state==RECEIVED_PEER_ADDRESS); - m_peer_address.copy( ((GetPeerAddress*)protocol)->getAddress() ); - // Reactivate this protocol - requestUnpause(); -} // callback diff --git a/src/network/protocols/connect_to_peer.hpp b/src/network/protocols/connect_to_peer.hpp index 87854b88c..483b36cf5 100644 --- a/src/network/protocols/connect_to_peer.hpp +++ b/src/network/protocols/connect_to_peer.hpp @@ -23,49 +23,54 @@ #include "network/transport_address.hpp" #include "utils/cpp2011.hpp" +#include + /** One instance of this is started for every peer who tries to * connect to this server. */ -class ConnectToPeer : public Protocol, public CallbackObject +class ConnectToPeer : public Protocol { protected: TransportAddress m_peer_address; uint32_t m_peer_id; - /** Pointer to the protocol which is monitored for state changes. */ + /** Pointer to the protocol which is monitored for state changes, this + * need to be shared_ptr because we need to get the result from + * \ref GetPeerAddress, otherwise when it terminated the result will be + * gone. */ std::shared_ptr m_current_protocol; /** True if this is a LAN connection. */ bool m_is_lan; - /** We might need to broadcast several times (in case the client is not - * ready in time). This keep track of broadcastst. */ - float m_time_last_broadcast; + /** Timer use for tracking broadcast. */ + std::chrono::system_clock::time_point m_timer; - int m_broadcast_count; + unsigned m_tried_connection = 0; enum STATE { NONE, RECEIVED_PEER_ADDRESS, - WAIT_FOR_LAN, + WAIT_FOR_CONNECTION, CONNECTING, CONNECTED, DONE, EXITING } m_state; + void resetTimer() { m_timer = std::chrono::system_clock::now(); } + public: ConnectToPeer(uint32_t peer_id); ConnectToPeer(const TransportAddress &address); virtual ~ConnectToPeer(); virtual bool notifyEventAsynchronous(Event* event) OVERRIDE; - virtual void setup() OVERRIDE; + virtual void setup() OVERRIDE {} virtual void update(float dt) OVERRIDE {} virtual void asynchronousUpdate() OVERRIDE; - virtual void callback(Protocol *protocol) OVERRIDE; }; // class ConnectToPeer #endif // CONNECT_TO_SERVER_HPP diff --git a/src/network/protocols/connect_to_server.cpp b/src/network/protocols/connect_to_server.cpp index c950939be..0b4c47c37 100644 --- a/src/network/protocols/connect_to_server.cpp +++ b/src/network/protocols/connect_to_server.cpp @@ -21,11 +21,9 @@ #include "config/player_manager.hpp" #include "network/event.hpp" #include "network/network_config.hpp" -#include "network/protocols/get_public_address.hpp" #include "network/protocols/get_peer_address.hpp" #include "network/protocols/hide_public_address.hpp" #include "network/protocols/request_connection.hpp" -#include "network/protocols/ping_protocol.hpp" #include "network/protocols/client_lobby.hpp" #include "network/protocol_manager.hpp" #include "network/servers_manager.hpp" @@ -82,45 +80,21 @@ ConnectToServer::~ConnectToServer() void ConnectToServer::setup() { Log::info("ConnectToServer", "SETUP"); - m_current_protocol = nullptr; + m_current_protocol.reset(); // In case of LAN we already have the server's and our ip address, // so we can immediately start requesting a connection. - m_state = NetworkConfig::get()->isLAN() ? GOT_SERVER_ADDRESS : NONE; + m_state = NetworkConfig::get()->isLAN() ? GOT_SERVER_ADDRESS : + REGISTER_SELF_ADDRESS; + } // setup -// ---------------------------------------------------------------------------- -/** Sets the server transport address. This is used in case of LAN networking, - * when we do not query the stk server and instead have the address from the - * LAN server directly. - * \param address Address of server to connect to. - */ -void ConnectToServer::setServerAddress(const TransportAddress &address) -{ -} // setServerAddress - // ---------------------------------------------------------------------------- void ConnectToServer::asynchronousUpdate() { switch(m_state) { - case NONE: + case REGISTER_SELF_ADDRESS: { - Log::info("ConnectToServer", "Protocol starting"); - // This protocol will write the public address of this - // instance to STKHost. - m_current_protocol = std::make_shared(this); - m_current_protocol->requestStart(); - // This protocol will be unpaused in the callback from - // GetPublicAddress - requestPause(); - m_state = GETTING_SELF_ADDRESS; - break; - } - case GETTING_SELF_ADDRESS: - { - // drop GetPublicAddress - m_current_protocol = nullptr; - registerWithSTKServer(); // Register us with STK server if (m_quick_join) @@ -139,135 +113,125 @@ void ConnectToServer::asynchronousUpdate() case GOT_SERVER_ADDRESS: { assert(!m_quick_join); - m_current_protocol = nullptr; Log::info("ConnectToServer", "Server's address known"); - - // we're in the same lan (same public ip address) !! - if (m_server_address.getIP() == - NetworkConfig::get()->getMyAddress().getIP()) - { - Log::info("ConnectToServer", - "Server appears to be in the same LAN."); - } m_state = REQUESTING_CONNECTION; - m_current_protocol = std::make_shared(m_server_id); - m_current_protocol->requestStart(); + auto request_connection = + std::make_shared(m_server_id); + request_connection->requestStart(); + m_current_protocol = request_connection; + // Reset timer for next usage + resetTimer(); break; } case REQUESTING_CONNECTION: - // In case of a LAN server, m_crrent_protocol is NULL - if (!m_current_protocol || - m_current_protocol->getState() == PROTOCOL_STATE_TERMINATED) + if (!m_current_protocol.expired()) { - m_current_protocol = nullptr; - // Server knows we want to connect - Log::info("ConnectToServer", "Connection request made"); - if (m_server_address.getIP() == 0 || - m_server_address.getPort() == 0 ) - { - // server data not correct, hide address and stop - m_state = HIDING_ADDRESS; - Log::error("ConnectToServer", "Server address is %s", - m_server_address.toString().c_str()); - m_current_protocol = std::make_shared(); - m_current_protocol->requestStart(); - return; - } - if( ( !NetworkConfig::m_disable_lan && - m_server_address.getIP() - == NetworkConfig::get()->getMyAddress().getIP() ) || - NetworkConfig::get()->isLAN() ) + return; + } + + // Server knows we want to connect + Log::info("ConnectToServer", "Connection request made"); + if (m_server_address.isUnset()) + { + // server data not correct, hide address and stop + m_state = HIDING_ADDRESS; + Log::error("ConnectToServer", "Server address is %s", + m_server_address.toString().c_str()); + auto hide_address = std::make_shared(); + hide_address->requestStart(); + m_current_protocol = hide_address; + return; + } + if (m_tried_connection++ > 5) + { + Log::error("ConnectToServer", "Timeout waiting for aloha"); + m_state = NetworkConfig::get()->isWAN() ? + HIDING_ADDRESS : DONE; + } + if ((!NetworkConfig::m_disable_lan && + m_server_address.getIP() == + STKHost::get()->getPublicAddress().getIP()) || + NetworkConfig::get()->isLAN()) + { + // We're in the same lan (same public ip address). + // The state will change to CONNECTING + waitingAloha(false/*is_wan*/); + } + else + { + // Send a 1-byte datagram, the remote host can simply ignore + // this datagram, to keep the port open (2 second each) + if (m_timer > m_timer + std::chrono::seconds(2)) { - // We're in the same lan (same public ip address). - // The state will change to CONNECTING - handleSameLAN(); - } - else - { - m_state = CONNECTING; - m_current_protocol = std::make_shared(m_server_address, 2.0); - m_current_protocol->requestStart(); + resetTimer(); + BareNetworkString data; + data.addUInt8(0); + STKHost::get()->sendRawPacket(data, m_server_address); } + waitingAloha(true/*is_wan*/); } break; case CONNECTING: // waiting the server to answer our connection + { + if (m_timer > m_timer + std::chrono::seconds(5)) // every 5 seconds { - static double timer = 0; - if (StkTime::getRealTime() > timer+5.0) // every 5 seconds + STKHost::get()->connect(m_server_address); + resetTimer(); + Log::info("ConnectToServer", "Trying to connect to %s", + m_server_address.toString().c_str()); + if (m_tried_connection++ > 3) { - STKHost::get()->connect(m_server_address); - timer = StkTime::getRealTime(); - Log::info("ConnectToServer", "Trying to connect to %s", - m_server_address.toString().c_str()); + Log::error("ConnectToServer", "Timeout connect to %s", + m_server_address.toString().c_str()); + m_state = NetworkConfig::get()->isWAN() ? + HIDING_ADDRESS : DONE; } - break; } + break; + } case CONNECTED: { Log::info("ConnectToServer", "Connected"); - if(m_current_protocol) - { - // Kill the ping protocol because we're connected - m_current_protocol->requestTerminate(); - } - m_current_protocol = nullptr; // LAN networking does not use the stk server tables. - if(NetworkConfig::get()->isWAN()) + if (NetworkConfig::get()->isWAN()) { - m_current_protocol = std::make_shared(); - m_current_protocol->requestStart(); + auto hide_address = std::make_shared(); + hide_address->requestStart(); + m_current_protocol = hide_address; } m_state = HIDING_ADDRESS; break; } case HIDING_ADDRESS: // Wait till we have hidden our address - if (!m_current_protocol || - m_current_protocol->getState() == PROTOCOL_STATE_TERMINATED) + if (!m_current_protocol.expired()) { - if(m_current_protocol) - { - m_current_protocol = nullptr; - Log::info("ConnectToServer", "Address hidden"); - } - m_state = DONE; - // lobby room protocol if we're connected only - if(STKHost::get()->getPeers()[0]->isConnected()) - { - auto cl = LobbyProtocol::create(); - cl->setAddress(m_server_address); - cl->requestStart(); - } + return; + } + m_state = DONE; + // lobby room protocol if we're connected only + if (STKHost::get()->getPeers()[0]->isConnected() && + !m_server_address.isUnset()) + { + auto cl = LobbyProtocol::create(); + cl->setAddress(m_server_address); + cl->requestStart(); } break; case DONE: requestTerminate(); m_state = EXITING; + if (STKHost::get()->getPeerCount() == 0) + { + // Shutdown STKHost (go back to online menu too) + STKHost::get()->requestShutdown(); + } break; case EXITING: break; } } // asynchronousUpdate - // ---------------------------------------------------------------------------- -/** Called when the GetPeerAddress protocol terminates. - */ -void ConnectToServer::callback(Protocol *protocol) -{ - switch(m_state) - { - case GETTING_SELF_ADDRESS: - // The GetPublicAddress protocol stores our address in - // STKHost, so we only need to unpause this protocol - requestUnpause(); - break; - default: - Log::error("ConnectToServer", - "Received unexpected callback while in state %d.", - m_state); - } // case m_state -} // callback - // ---------------------------------------------------------------------------- /** Register this client with the STK server. */ @@ -275,14 +239,13 @@ void ConnectToServer::registerWithSTKServer() { // Our public address is now known, register details with // STK server. - const TransportAddress& addr = NetworkConfig::get()->getMyAddress(); + const TransportAddress& addr = STKHost::get()->getPublicAddress(); Online::XMLRequest *request = new Online::XMLRequest(); PlayerManager::setUserDetails(request, "set", Online::API::SERVER_PATH); request->addParameter("address", addr.getIP()); request->addParameter("port", addr.getPort()); - request->addParameter("private_port", - NetworkConfig::get()->getClientPort()); + request->addParameter("private_port", STKHost::get()->getPrivatePort()); Log::info("ConnectToServer", "Registering addr %s", addr.toString().c_str()); @@ -318,7 +281,6 @@ void ConnectToServer::handleQuickConnect() request->executeNow(); const XMLNode * result = request->getXMLData(); - delete request; std::string success; if(result->get("success", &success) && success=="yes") @@ -330,7 +292,7 @@ void ConnectToServer::handleQuickConnect() uint16_t port; // If we are using a LAN connection, we need the private (local) port if (m_server_address.getIP() == - NetworkConfig::get()->getMyAddress().getIP()) + STKHost::get()->getPublicAddress().getIP()) { result->get("private_port", &port); } @@ -345,13 +307,15 @@ void ConnectToServer::handleQuickConnect() { Log::error("GetPeerAddress", "Failed to get address."); } + delete request; } // handleQuickConnect // ---------------------------------------------------------------------------- /** Called when the server is on the same LAN. It uses broadcast to - * find and conntect to the server. + * find and conntect to the server. For WAN game, it makes sure server recieve + * request from stk addons first before continuing. */ -void ConnectToServer::handleSameLAN() +void ConnectToServer::waitingAloha(bool is_wan) { // just send a broadcast packet, the client will know our // ip address and will connect @@ -379,65 +343,70 @@ void ConnectToServer::handleSameLAN() std::string aloha("aloha_stk"); if (received==aloha) { - Log::info("ConnectToServer", "LAN Server found : %s", + Log::info("ConnectToServer", "Server found : %s", sender.toString().c_str()); #ifndef WIN32 - // just check if the ip is ours : if so, - // then just use localhost (127.0.0.1) - struct ifaddrs *ifap, *ifa; - struct sockaddr_in *sa; - getifaddrs(&ifap); // get the info - for (ifa = ifap; ifa; ifa = ifa->ifa_next) + if (!is_wan) { - if (ifa->ifa_addr->sa_family == AF_INET) + // just check if the ip is ours : if so, + // then just use localhost (127.0.0.1) + struct ifaddrs *ifap, *ifa; + struct sockaddr_in *sa; + getifaddrs(&ifap); // get the info + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { - sa = (struct sockaddr_in *) ifa->ifa_addr; + if (ifa->ifa_addr->sa_family == AF_INET) + { + sa = (struct sockaddr_in *) ifa->ifa_addr; - // This interface is ours - if (ntohl(sa->sin_addr.s_addr) == sender.getIP()) - sender.setIP(0x7f000001); // 127.0.0.1 + // This interface is ours + if (ntohl(sa->sin_addr.s_addr) == sender.getIP()) + sender.setIP(0x7f000001); // 127.0.0.1 + } } - } - freeifaddrs(ifap); + freeifaddrs(ifap); #else - // Query the list of all IP addresses on the local host - // First call to GetIpAddrTable with 0 bytes buffer - // will return insufficient buffer error, and size - // will contain the number of bytes needed for all - // data. Repeat the process of querying the size - // using GetIpAddrTable in a while loop since it - // can happen that an interface comes online between - // the previous call to GetIpAddrTable and the next - // call. - MIB_IPADDRTABLE *table = NULL; - unsigned long size = 0; - int error = GetIpAddrTable(table, &size, 0); - // Also add a count to limit the while loop - in - // case that something strange is going on. - int count = 0; - while (error == ERROR_INSUFFICIENT_BUFFER && count < 10) - { - delete[] table; // deleting NULL is legal - table = (MIB_IPADDRTABLE*)new char[size]; - error = GetIpAddrTable(table, &size, 0); - count++; - } // while insufficient buffer - for (unsigned int i = 0; i < table->dwNumEntries; i++) - { - unsigned int ip = ntohl(table->table[i].dwAddr); - if (sender.getIP() == ip) // this interface is ours + // Query the list of all IP addresses on the local host + // First call to GetIpAddrTable with 0 bytes buffer + // will return insufficient buffer error, and size + // will contain the number of bytes needed for all + // data. Repeat the process of querying the size + // using GetIpAddrTable in a while loop since it + // can happen that an interface comes online between + // the previous call to GetIpAddrTable and the next + // call. + MIB_IPADDRTABLE *table = NULL; + unsigned long size = 0; + int error = GetIpAddrTable(table, &size, 0); + // Also add a count to limit the while loop - in + // case that something strange is going on. + int count = 0; + while (error == ERROR_INSUFFICIENT_BUFFER && count < 10) { - sender.setIP(0x7f000001); // 127.0.0.1 - break; + delete[] table; // deleting NULL is legal + table = (MIB_IPADDRTABLE*)new char[size]; + error = GetIpAddrTable(table, &size, 0); + count++; + } // while insufficient buffer + for (unsigned int i = 0; i < table->dwNumEntries; i++) + { + unsigned int ip = ntohl(table->table[i].dwAddr); + if (sender.getIP() == ip) // this interface is ours + { + sender.setIP(0x7f000001); // 127.0.0.1 + break; + } } - } - delete[] table; - + delete[] table; #endif - m_server_address.copy(sender); + m_server_address.copy(sender); + } m_state = CONNECTING; + // Reset timer for next usage + resetTimer(); + m_tried_connection = 0; } -} // handleSameLAN +} // waitingAloha // ---------------------------------------------------------------------------- @@ -448,7 +417,6 @@ bool ConnectToServer::notifyEventAsynchronous(Event* event) Log::info("ConnectToServer", "The Connect To Server protocol has " "received an event notifying that he's connected to the peer."); m_state = CONNECTED; // we received a message, we are connected - Server *server = ServersManager::get()->getJoinedServer(); } return true; } // notifyEventAsynchronous diff --git a/src/network/protocols/connect_to_server.hpp b/src/network/protocols/connect_to_server.hpp index 42690fef0..86f6bd0e4 100644 --- a/src/network/protocols/connect_to_server.hpp +++ b/src/network/protocols/connect_to_server.hpp @@ -22,27 +22,28 @@ #include "network/protocol.hpp" #include "network/transport_address.hpp" #include "utils/cpp2011.hpp" +#include #include -class ConnectToServer : public Protocol, public CallbackObject +class ConnectToServer : public Protocol { private: + std::chrono::system_clock::time_point m_timer; TransportAddress m_server_address; uint32_t m_server_id; uint32_t m_host_id; + unsigned m_tried_connection = 0; /** Protocol currently being monitored. */ - std::shared_ptr m_current_protocol; + std::weak_ptr m_current_protocol; bool m_quick_join; /** State for finite state machine. */ enum { - NONE, - GETTING_SELF_ADDRESS, + REGISTER_SELF_ADDRESS, GOT_SERVER_ADDRESS, REQUESTING_CONNECTION, - QUICK_JOIN, CONNECTING, CONNECTED, HIDING_ADDRESS, @@ -52,7 +53,8 @@ private: void registerWithSTKServer(); void handleQuickConnect(); - void handleSameLAN(); + void waitingAloha(bool is_wan); + void resetTimer() { m_timer = std::chrono::system_clock::now(); } public: ConnectToServer(); @@ -62,9 +64,7 @@ public: virtual bool notifyEventAsynchronous(Event* event) OVERRIDE; virtual void setup() OVERRIDE; virtual void asynchronousUpdate() OVERRIDE; - virtual void callback(Protocol *protocol) OVERRIDE; virtual void update(float dt) OVERRIDE {} - void setServerAddress(const TransportAddress &address); }; // class ConnectToServer #endif // CONNECT_TO_SERVER_HPP diff --git a/src/network/protocols/get_peer_address.cpp b/src/network/protocols/get_peer_address.cpp index 2ef5a6a96..d1033aea9 100644 --- a/src/network/protocols/get_peer_address.cpp +++ b/src/network/protocols/get_peer_address.cpp @@ -21,13 +21,12 @@ #include "config/player_manager.hpp" #include "config/user_config.hpp" #include "network/network_config.hpp" -#include "network/protocol_manager.hpp" +#include "network/stk_host.hpp" #include "online/request_manager.hpp" #include "utils/log.hpp" -GetPeerAddress::GetPeerAddress(uint32_t peer_id, - CallbackObject* callback_object) - : Protocol(PROTOCOL_SILENT, callback_object) +GetPeerAddress::GetPeerAddress(uint32_t peer_id) + : Protocol(PROTOCOL_SILENT, NULL) { m_peer_id = peer_id; } // GetPeerAddress @@ -41,7 +40,6 @@ GetPeerAddress::~GetPeerAddress() void GetPeerAddress::setup() { m_address.clear(); - m_request = new Online::XMLRequest(); PlayerManager::setUserDetails(m_request, "get", Online::API::SERVER_PATH); @@ -65,7 +63,7 @@ void GetPeerAddress::asynchronousUpdate() m_address.setIP(ip); uint16_t port; - uint32_t my_ip = NetworkConfig::get()->getMyAddress().getIP(); + uint32_t my_ip = STKHost::get()->getPublicAddress().getIP(); if (m_address.getIP() == my_ip && !NetworkConfig::m_disable_lan) result->get("private_port", &port); else @@ -84,9 +82,3 @@ void GetPeerAddress::asynchronousUpdate() m_request = NULL; } } // asynchronousUpdate - -// ---------------------------------------------------------------------------- -void GetPeerAddress::setPeerID(uint32_t peer_id) -{ - m_peer_id = peer_id; -} // setPeerID diff --git a/src/network/protocols/get_peer_address.hpp b/src/network/protocols/get_peer_address.hpp index 094639953..02b5f8d2a 100644 --- a/src/network/protocols/get_peer_address.hpp +++ b/src/network/protocols/get_peer_address.hpp @@ -35,13 +35,12 @@ private: * to get the result. */ TransportAddress m_address; public: - GetPeerAddress(uint32_t peer_id, CallbackObject* callback_object); + GetPeerAddress(uint32_t peer_id); virtual ~GetPeerAddress(); virtual void setup() OVERRIDE; virtual void asynchronousUpdate() OVERRIDE; - void setPeerID(uint32_t m_peer_id); - + void setPeerID(uint32_t peer_id) { m_peer_id = peer_id; } // ------------------------------------------------------------------------ /** Returns the address found. */ const TransportAddress &getAddress() const { return m_address; } diff --git a/src/network/protocols/get_public_address.cpp b/src/network/protocols/get_public_address.cpp index 5bd89c8e6..934fb34a2 100644 --- a/src/network/protocols/get_public_address.cpp +++ b/src/network/protocols/get_public_address.cpp @@ -19,6 +19,7 @@ #include "network/protocols/get_public_address.hpp" #include "config/user_config.hpp" +#include "guiengine/message_queue.hpp" #include "network/network.hpp" #include "network/network_config.hpp" #include "network/network_string.hpp" @@ -28,6 +29,8 @@ #include "utils/string_utils.hpp" #include +#include +#include #include #ifdef __MINGW32__ @@ -48,36 +51,16 @@ const uint32_t GetPublicAddress::m_stun_magic_cookie = 0x2112A442; TransportAddress GetPublicAddress::m_my_address(0, 0); -void GetPublicAddress::setMyIPAddress(const std::string &s) -{ - std::vector l = StringUtils::split(s, ':'); - if (l.size() != 2) - { - Log::fatal("Invalid IP address '%s'.", s.c_str()); - } - std::vector ip = StringUtils::split(l[0], '.'); - if (ip.size() != 4) - { - Log::fatal("Invalid IP address '%s'.", s.c_str()); - } - uint32_t u = 0; - for (unsigned int i = 0; i < 4; i++) - { - int k; - StringUtils::fromString(ip[i], k); - u = (u << 8) + k; - } - m_my_address.setIP(u); - int p; - StringUtils::fromString(l[1], p); - m_my_address.setPort(p); -} // setMyIPAddress // ============================================================================ -GetPublicAddress::GetPublicAddress(CallbackObject *callback) - : Protocol(PROTOCOL_SILENT, callback) +GetPublicAddress::GetPublicAddress() + : Protocol(PROTOCOL_SILENT, NULL) { - m_state = NOTHING_DONE; + m_untried_server = UserConfigParams::m_stun_servers; + // Generate random list of stun servers + std::random_device rd; + std::mt19937 g(rd()); + std::shuffle(m_untried_server.begin(), m_untried_server.end(), g); } // GetPublicAddress // ---------------------------------------------------------------------------- @@ -85,15 +68,22 @@ GetPublicAddress::GetPublicAddress(CallbackObject *callback) * the list stored in the config file. See * https://tools.ietf.org/html/rfc5389#section-6 * for details on the message structure. - * The request is send through m_transaction_host, from which the answer + * The request is send through transaction_host, from which the answer * will be retrieved by parseStunResponse() */ -void GetPublicAddress::createStunRequest() +Network* GetPublicAddress::createStunRequest() { - // Pick a random stun server - std::vector stun_servers = UserConfigParams::m_stun_servers; + if (m_untried_server.empty()) + { + // Notice: MessageQueue is thread safe to add + MessageQueue::add(MessageQueue::MT_ERROR, + _("Failed to get public address from stun server.")); + requestTerminate(); + return NULL; + } - const char* server_name = stun_servers[rand() % stun_servers.size()].c_str(); + // Pick last element in untried servers + const char* server_name = m_untried_server.back().c_str(); Log::debug("GetPublicAddress", "Using STUN server %s", server_name); struct addrinfo hints, *res; @@ -106,10 +96,12 @@ void GetPublicAddress::createStunRequest() int status = getaddrinfo(server_name, NULL, &hints, &res); if (status != 0) { - Log::error("GetPublicAddress", "Error in getaddrinfo: %s", - gai_strerror(status)); - return; + Log::error("GetPublicAddress", "Error in getaddrinfo for stun server" + " %s: %s", server_name, gai_strerror(status)); + m_untried_server.pop_back(); + return NULL; } + m_untried_server.pop_back(); // documentation says it points to "one or more addrinfo structures" assert(res != NULL); struct sockaddr_in* current_interface = (struct sockaddr_in*)(res->ai_addr); @@ -119,7 +111,7 @@ void GetPublicAddress::createStunRequest() ENetAddress addr; addr.host = STKHost::HOST_ANY; addr.port = STKHost::PORT_ANY; - m_transaction_host = new Network(1, 1, 0, 0, &addr); + Network* transaction_host = new Network(1, 1, 0, 0, &addr); // Assemble the message for the stun server BareNetworkString s(20); @@ -138,11 +130,11 @@ void GetPublicAddress::createStunRequest() m_stun_tansaction_id[i] = random_byte; } - m_transaction_host->sendRawPacket(s, + transaction_host->sendRawPacket(s, TransportAddress(m_stun_server_ip, m_stun_server_port) ); freeaddrinfo(res); - m_state = STUN_REQUEST_SENT; + return transaction_host; } // createStunRequest // ---------------------------------------------------------------------------- @@ -151,12 +143,13 @@ void GetPublicAddress::createStunRequest() * then parses the answer into address and port * \return "" if the address could be parsed or an error message */ -std::string GetPublicAddress::parseStunResponse() +std::string GetPublicAddress::parseStunResponse(Network* transaction_host) { TransportAddress sender; const int LEN = 2048; char buffer[LEN]; - int len = m_transaction_host->receiveRawPacket(buffer, LEN, &sender, 2000); + int len = transaction_host->receiveRawPacket(buffer, LEN, &sender, 2000); + delete transaction_host; if(sender.getIP()!=m_stun_server_ip) { @@ -199,7 +192,6 @@ std::string GetPublicAddress::parseStunResponse() // Those are the port and the address to be detected - int pos = 20; while (true) { int type = datas.getUInt16(); @@ -239,35 +231,27 @@ void GetPublicAddress::asynchronousUpdate() if (m_my_address.getIP() != 0 && m_my_address.getPort() != 0) { NetworkConfig::get()->setMyAddress(m_my_address); - m_state = EXITING; requestTerminate(); } //#define LAN_TEST #ifdef LAN_TEST TransportAddress address(0x7f000001, 4); NetworkConfig::get()->setMyAddress(address); - m_state = EXITING; requestTerminate(); return; #endif - if (m_state == NOTHING_DONE) + Network* transaction_host = createStunRequest(); + if (transaction_host) { - createStunRequest(); - } - if (m_state == STUN_REQUEST_SENT) - { - std::string message = parseStunResponse(); - delete m_transaction_host; + std::string message = parseStunResponse(transaction_host); if (message != "") { Log::warn("GetPublicAddress", "%s", message.c_str()); - m_state = NOTHING_DONE; // try again } else { // The address and the port are known, so the connection can be closed - m_state = EXITING; requestTerminate(); } } diff --git a/src/network/protocols/get_public_address.hpp b/src/network/protocols/get_public_address.hpp index d759f29a5..545a7c4df 100644 --- a/src/network/protocols/get_public_address.hpp +++ b/src/network/protocols/get_public_address.hpp @@ -30,8 +30,10 @@ class Network; class GetPublicAddress : public Protocol { private: - void createStunRequest(); - std::string parseStunResponse(); + Network* createStunRequest(); + std::string parseStunResponse(Network* transaction_host); + + std::vector m_untried_server; // Constants static const uint32_t m_stun_magic_cookie; @@ -41,20 +43,13 @@ private: * unnecessary (though that means that the user has to take care of * opening the firewall). */ static TransportAddress m_my_address; - enum State - { - NOTHING_DONE, - STUN_REQUEST_SENT, - EXITING - } m_state; uint8_t m_stun_tansaction_id[12]; uint32_t m_stun_server_ip; Network* m_transaction_host; public: - static void setMyIPAddress(const std::string &s); - GetPublicAddress(CallbackObject *callback = NULL); + GetPublicAddress(); virtual ~GetPublicAddress() {} virtual void asynchronousUpdate() OVERRIDE; @@ -65,7 +60,7 @@ public: // ------------------------------------------------------------------------ virtual bool notifyEventAsynchronous(Event* event) OVERRIDE { return true; } // ------------------------------------------------------------------------ - virtual void setup() { m_state = NOTHING_DONE; } + virtual void setup() { } // ------------------------------------------------------------------------ }; // class GetPublicAddress diff --git a/src/network/protocols/hide_public_address.cpp b/src/network/protocols/hide_public_address.cpp index 766dd95b2..47bc29bcc 100644 --- a/src/network/protocols/hide_public_address.cpp +++ b/src/network/protocols/hide_public_address.cpp @@ -56,7 +56,7 @@ void HidePublicAddress::asynchronousUpdate() { if(rec_success == "yes") { - Log::debug("HidePublicAddress", "Address hidden successfully."); + Log::info("HidePublicAddress", "Address hidden successfully."); } else { diff --git a/src/network/protocols/ping_protocol.cpp b/src/network/protocols/ping_protocol.cpp index 1d0b46666..1398f451f 100644 --- a/src/network/protocols/ping_protocol.cpp +++ b/src/network/protocols/ping_protocol.cpp @@ -26,11 +26,12 @@ * \param delay_between_pings: How often to ping. */ PingProtocol::PingProtocol(const TransportAddress& ping_dst, - double delay_between_pings) + double delay_between_pings, double timeout) : Protocol(PROTOCOL_SILENT) { m_ping_dst.copy(ping_dst); m_delay_between_pings = delay_between_pings; + m_timeout = timeout; } // PingProtocol // ---------------------------------------------------------------------------- @@ -41,7 +42,7 @@ PingProtocol::~PingProtocol() // ---------------------------------------------------------------------------- void PingProtocol::setup() { - m_last_ping_time = 0; + m_last_ping_time = 0.0; } // setup // ---------------------------------------------------------------------------- @@ -49,6 +50,10 @@ void PingProtocol::asynchronousUpdate() { if (StkTime::getRealTime() > m_last_ping_time+m_delay_between_pings) { + if (m_last_ping_time == 0.0) + m_timeout = StkTime::getRealTime() + m_timeout; + else if (StkTime::getRealTime() > m_timeout) + requestTerminate(); m_last_ping_time = StkTime::getRealTime(); BareNetworkString data; data.addUInt8(0); diff --git a/src/network/protocols/ping_protocol.hpp b/src/network/protocols/ping_protocol.hpp index fdb66adf2..1a9f71c09 100644 --- a/src/network/protocols/ping_protocol.hpp +++ b/src/network/protocols/ping_protocol.hpp @@ -16,9 +16,12 @@ private: /** Time of last ping. */ double m_last_ping_time; + + /** If longer than this, terminate this protocol. */ + double m_timeout; public: PingProtocol(const TransportAddress& ping_dst, - double delay_between_pings); + double delay_between_pings, double timeout = 60.0); virtual ~PingProtocol(); virtual void asynchronousUpdate() OVERRIDE; diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index df45a08ff..c19c253b9 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -115,7 +115,6 @@ void ServerLobby::setup() m_state = NetworkConfig::get()->isLAN() ? ACCEPTING_CLIENTS : INIT_WAN; m_selection_enabled = false; - m_current_protocol = nullptr; Log::info("ServerLobby", "Starting the protocol."); // Initialise the data structures to detect if all clients and @@ -201,27 +200,20 @@ void ServerLobby::update(float dt) switch (m_state.load()) { case INIT_WAN: + { // Start the protocol to find the public ip address. - m_current_protocol = std::make_shared(this); - m_current_protocol->requestStart(); m_state = GETTING_PUBLIC_ADDRESS; - // The callback from GetPublicAddress will wake this protocol up - requestPause(); break; + } case GETTING_PUBLIC_ADDRESS: { - Log::debug("ServerLobby", "Public address known."); - // Free GetPublicAddress protocol - m_current_protocol = nullptr; - // Register this server with the STK server. This will block // this thread, but there is no need for the protocol manager // to react to any requests before the server is registered. registerServer(); - Log::info("ServerLobby", "Server registered."); m_state = ACCEPTING_CLIENTS; + break; } - break; case ACCEPTING_CLIENTS: { // Only poll the STK server if this is a WAN server. @@ -306,15 +298,6 @@ void ServerLobby::update(float dt) } } // update -//----------------------------------------------------------------------------- -/** Callback when the GetPublicAddress terminates. It will unpause this - * protocol, which triggers the next state of the finite state machine. - */ -void ServerLobby::callback(Protocol *protocol) -{ - requestUnpause(); -} // callback - //----------------------------------------------------------------------------- /** Register this server (i.e. its public address) with the STK server * so that clients can find it. It blocks till a response from the @@ -325,12 +308,12 @@ void ServerLobby::callback(Protocol *protocol) void ServerLobby::registerServer() { Online::XMLRequest *request = new Online::XMLRequest(); - const TransportAddress& addr = NetworkConfig::get()->getMyAddress(); + const TransportAddress& addr = STKHost::get()->getPublicAddress(); PlayerManager::setUserDetails(request, "create", Online::API::SERVER_PATH); request->addParameter("address", addr.getIP() ); request->addParameter("port", addr.getPort() ); request->addParameter("private_port", - NetworkConfig::get()->getServerPort() ); + STKHost::get()->getPrivatePort() ); request->addParameter("name", NetworkConfig::get()->getServerName() ); request->addParameter("max_players", UserConfigParams::m_server_max_players ); @@ -350,9 +333,11 @@ void ServerLobby::registerServer() { irr::core::stringc error(request->getInfo().c_str()); Log::error("RegisterServer", "%s", error.c_str()); - STKHost::get()->setErrorMessage(_("Failed to register server: %s", error.c_str())); + STKHost::get()->setErrorMessage(_("Failed to register server: %s", + error.c_str())); + STKHost::get()->requestShutdown(); } - + delete request; } // registerServer //----------------------------------------------------------------------------- @@ -444,7 +429,7 @@ void ServerLobby::checkIncomingConnectionRequests() PlayerManager::setUserDetails(request, "poll-connection-requests", Online::API::SERVER_PATH); - const TransportAddress &addr = NetworkConfig::get()->getMyAddress(); + const TransportAddress &addr = STKHost::get()->getPublicAddress(); request->addParameter("address", addr.getIP() ); request->addParameter("port", addr.getPort()); @@ -469,7 +454,7 @@ void ServerLobby::checkIncomingConnectionRequests() Log::debug("ServerLobby", "User with id %d wants to connect.", id); std::make_shared(id)->requestStart(); - } + } delete request; } // checkIncomingConnectionRequests diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 82dbf379c..8e99909ca 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -9,7 +9,6 @@ #include class ServerLobby : public LobbyProtocol - , public CallbackObject { public: /* The state for a small finite state machine. */ @@ -57,7 +56,6 @@ private: * seconds), which is the real time at which the server should start. */ double m_server_delay; - std::shared_ptr m_current_protocol; bool m_selection_enabled; /** Counts how many players are ready to go on. */ @@ -98,7 +96,6 @@ public: void checkRaceFinished(); void finishedLoadingWorld(); ServerState getCurrentState() const { return m_state.load(); } - virtual void callback(Protocol *protocol) OVERRIDE; }; // class ServerLobby diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index a8f889fda..ff8cbd6a4 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -43,7 +43,24 @@ # include # include #endif + +#ifdef __MINGW32__ +# undef _WIN32_WINNT +# define _WIN32_WINNT 0x501 +#endif + +#ifdef WIN32 +# include +# include +#else +# include +#endif +#include + +#include #include +#include +#include STKHost *STKHost::m_stk_host = NULL; bool STKHost::m_enable_console = false; @@ -244,10 +261,10 @@ STKHost::STKHost(uint32_t server_id, uint32_t host_id) // server is made. m_host_id = 0; init(); - TransportAddress a; - a.setIP(0); - a.setPort(NetworkConfig::get()->getClientPort()); - ENetAddress ea = a.toEnetAddress(); + + ENetAddress ea; + ea.host = STKHost::HOST_ANY; + ea.port = STKHost::PORT_ANY; m_network = new Network(/*peer_count*/1, /*channel_limit*/2, /*max_in_bandwidth*/0, /*max_out_bandwidth*/0, &ea); @@ -257,7 +274,16 @@ STKHost::STKHost(uint32_t server_id, uint32_t host_id) "an ENet client host."); } - std::make_shared(server_id, host_id)->requestStart(); + setPrivatePort(); + if (NetworkConfig::get()->isWAN()) + { + setPublicAddress(); + } + // Don't connect to server if no public address in WAN game + if (!m_public_address.isUnset() || NetworkConfig::get()->isLAN()) + { + std::make_shared(server_id, host_id)->requestStart(); + } } // STKHost // ---------------------------------------------------------------------------- @@ -274,7 +300,7 @@ STKHost::STKHost(const irr::core::stringw &server_name) ENetAddress addr; addr.host = STKHost::HOST_ANY; - addr.port = NetworkConfig::get()->getServerPort(); + addr.port = STKHost::PORT_ANY; m_network= new Network(NetworkConfig::get()->getMaxPlayers(), /*channel_limit*/2, @@ -286,8 +312,18 @@ STKHost::STKHost(const irr::core::stringw &server_name) "ENet server host."); } - startListening(); - ProtocolManager::lock()->requestStart(LobbyProtocol::create()); + setPrivatePort(); + if (NetworkConfig::get()->isWAN()) + { + setPublicAddress(); + } + // Don't construct server if no public address in WAN game + if (!m_public_address.isUnset() || NetworkConfig::get()->isLAN()) + { + startListening(); + ProtocolManager::lock() + ->requestStart(LobbyProtocol::create()); + } } // STKHost(server_name) @@ -364,6 +400,192 @@ void STKHost::shutdown() destroy(); } // shutdown +//----------------------------------------------------------------------------- +/** Set the public address using stun protocol. + */ +void STKHost::setPublicAddress() +{ + std::vector untried_server = UserConfigParams::m_stun_servers; + // Generate random list of stun servers + std::random_device rd; + std::mt19937 g(rd()); + std::shuffle(untried_server.begin(), untried_server.end(), g); + while (!untried_server.empty()) + { + // Pick last element in untried servers + const char* server_name = untried_server.back().c_str(); + Log::debug("STKHost", "Using STUN server %s", server_name); + + struct addrinfo hints, *res; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version + hints.ai_socktype = SOCK_STREAM; + + // Resolve the stun server name so we can send it a STUN request + int status = getaddrinfo(server_name, NULL, &hints, &res); + if (status != 0) + { + Log::error("STKHost", "Error in getaddrinfo for stun server" + " %s: %s", server_name, gai_strerror(status)); + untried_server.pop_back(); + continue; + } + untried_server.pop_back(); + // documentation says it points to "one or more addrinfo structures" + assert(res != NULL); + struct sockaddr_in* current_interface = (struct sockaddr_in*)(res->ai_addr); + uint32_t stun_server_ip = ntohl(current_interface->sin_addr.s_addr); + + // Assemble the message for the stun server + BareNetworkString s(20); + + // bytes 0-1: the type of the message + // bytes 2-3: message length added to header (attributes) + uint16_t message_type = 0x0001; // binding request + uint16_t message_length = 0x0000; + s.addUInt16(message_type).addUInt16(message_length) + .addUInt32(0x2112A442); + uint8_t stun_tansaction_id[12]; + // bytes 8-19: the transaction id + for (int i = 0; i < 12; i++) + { + uint8_t random_byte = rand() % 256; + s.addUInt8(random_byte); + stun_tansaction_id[i] = random_byte; + } + + m_network->sendRawPacket(s, TransportAddress(stun_server_ip, 3478)); + freeaddrinfo(res); + + // Recieve now + TransportAddress sender; + const int LEN = 2048; + char buffer[LEN]; + int len = m_network->receiveRawPacket(buffer, LEN, &sender, 2000); + + if (sender.getIP() != stun_server_ip) + { + TransportAddress stun(stun_server_ip, 3478); + Log::warn("STKHost", + "Received stun response from %s instead of %s.", + sender.toString().c_str(), stun.toString().c_str()); + } + + if (len < 0) + { + Log::error("STKHost", "STUN response contains no data at all"); + continue; + } + + // Convert to network string. + BareNetworkString datas(buffer, len); + + // check that the stun response is a response, contains the magic cookie + // and the transaction ID + if (datas.getUInt16() != 0x0101) + { + Log::error("STKHost", "STUN response doesn't contain the magic " + "cookie"); + continue; + } + int message_size = datas.getUInt16(); + if (datas.getUInt32() != 0x2112A442) + { + Log::error("STKHost", "STUN response doesn't contain the magic " + "cookie"); + continue; + } + + for (int i = 0; i < 12; i++) + { + if (datas.getUInt8() != stun_tansaction_id[i]) + { + Log::error("STKHost", "STUN response doesn't contain the " + "transaction ID"); + continue; + } + } + + Log::debug("GetPublicAddress", + "The STUN server responded with a valid answer"); + + // The stun message is valid, so we parse it now: + if (message_size == 0) + { + Log::error("STKHost", "STUN response does not contain any " + "information."); + continue; + } + // Cannot even read the size + if (message_size < 4) + { + Log::error("STKHost", "STUN response is too short."); + continue; + } + // Those are the port and the address to be detected + bool found = false; + while (true) + { + int type = datas.getUInt16(); + int size = datas.getUInt16(); + if (type == 0 || type == 1) + { + assert(size == 8); + datas.getUInt8(); // skip 1 byte +#ifdef DEBUG + uint8_t skip = datas.getUInt8(); + // Family IPv4 only + assert(skip == 0x01); +#else + datas.getUInt8(); +#endif + m_public_address.setPort(datas.getUInt16()); + m_public_address.setIP(datas.getUInt32()); + // finished parsing, we know our public transport address + Log::debug("STKHost", "The public address has been found: %s", + m_public_address.toString().c_str()); + found = true; + break; + } // type = 0 or 1 + datas.skip(4 + size); + message_size -= 4 + size; + if (message_size == 0) + { + Log::error("STKHost", "STUN response is invalid."); + break; + } + // Cannot even read the size + if (message_size < 4) + { + Log::error("STKHost", "STUN response is invalid."); + break; + } + } // while true + // Found public address and port + if (found) + untried_server.clear(); + } + // We shutdown next frame if no public address + if (m_public_address.isUnset()) + requestShutdown(); +} // setPublicAddress + +//----------------------------------------------------------------------------- +void STKHost::setPrivatePort() +{ + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + ENetHost *host = m_network->getENetHost(); + if (getsockname(host->socket, (struct sockaddr *)&sin, &len) == -1) + { + Log::error("STKHost", "Error while using getsockname()."); + m_private_port = 0; + } + else + m_private_port = ntohs(sin.sin_port); +} // setPrivatePort + //----------------------------------------------------------------------------- /** A previous GameSetup is deletea and a new one is created. * \return Newly create GameSetup object. @@ -495,9 +717,7 @@ void STKHost::mainLoop() // A separate network connection (socket) to handle LAN requests. Network* lan_network = NULL; - - if (NetworkConfig::get()->isServer() && - (NetworkConfig::get()->isLAN() || NetworkConfig::get()->isPublicServer()) ) + if (NetworkConfig::get()->isLAN()) { TransportAddress address(0, NetworkConfig::get()->getServerDiscoveryPort()); ENetAddress eaddr = address.toEnetAddress(); @@ -610,6 +830,13 @@ void STKHost::handleDirectSocketRequest(Network* lan_network) { // In case of a LAN connection, we only allow connections from // a LAN address (192.168*, ..., and 127.*). + if (!sender.isLAN()) + { + Log::error("STKHost", "Client trying to connect from '%s'", + sender.toString().c_str()); + Log::error("STKHost", "which is outside of LAN - rejected."); + return; + } std::make_shared(sender)->requestStart(); } else @@ -742,20 +969,6 @@ void STKHost::removePeer(const STKPeer* peer) m_peers.size()); } // removePeer -//----------------------------------------------------------------------------- - -uint16_t STKHost::getPort() const -{ - struct sockaddr_in sin; - socklen_t len = sizeof(sin); - ENetHost *host = m_network->getENetHost(); - if (getsockname(host->socket, (struct sockaddr *)&sin, &len) == -1) - Log::error("STKHost", "Error while using getsockname()."); - else - return ntohs(sin.sin_port); - return 0; -} // getPort - //----------------------------------------------------------------------------- /** Sends data to all peers except the specified one. * \param peer Peer which will not receive the message. diff --git a/src/network/stk_host.hpp b/src/network/stk_host.hpp index ac78aa3a2..5092df90e 100644 --- a/src/network/stk_host.hpp +++ b/src/network/stk_host.hpp @@ -103,12 +103,22 @@ private: * in the GUI. */ irr::core::stringw m_error_message; + /** The public address found by stun (if WAN is used). */ + TransportAddress m_public_address; + + /** The private port enet socket is bound. */ + uint16_t m_private_port; + + /** An error message, which is set by a protocol to be displayed + * in the GUI. */ + STKHost(uint32_t server_id, uint32_t host_id); STKHost(const irr::core::stringw &server_name); virtual ~STKHost(); void init(); void handleDirectSocketRequest(Network* lan_network); - + // ------------------------------------------------------------------------ + void setPublicAddress(); // ------------------------------------------------------------------------ void mainLoop(); @@ -140,7 +150,15 @@ public: // ------------------------------------------------------------------------ /** Checks if the STKHost has been created. */ static bool existHost() { return m_stk_host != NULL; } - + // ------------------------------------------------------------------------ + const TransportAddress& getPublicAddress() const + { return m_public_address; } + // ------------------------------------------------------------------------ + uint16_t getPrivatePort() const + { return m_private_port; } + // ------------------------------------------------------------------------ + void setPrivatePort(); + // ------------------------------------------------------------------------ virtual GameSetup* setupNewGame(); void abort(); void deleteAllPeers(); @@ -175,7 +193,6 @@ public: STKPeer *getPeer(ENetPeer *enet_peer); STKPeer *getServerPeerForClient() const; std::vector getMyPlayerProfiles(); - uint16_t getPort() const; void setErrorMessage(const irr::core::stringw &message); bool isAuthorisedToControl() const; const irr::core::stringw& diff --git a/src/network/transport_address.hpp b/src/network/transport_address.hpp index 2e33fb01f..3a5cab8bd 100644 --- a/src/network/transport_address.hpp +++ b/src/network/transport_address.hpp @@ -90,6 +90,8 @@ private: public: bool isLAN() const; // ------------------------------------------------------------------------ + bool isUnset() const { return m_ip == 0 || m_port == 0; } + // ------------------------------------------------------------------------ /** A copy function (to replace the copy constructor which is disabled * using NoCopy): it copies the data from the argument into this object.*/ void copy(const TransportAddress &other) From 4559fd6a2c00fd3a3613a87a36e3e645d07c21bc Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 22 Feb 2018 15:14:15 +0800 Subject: [PATCH 135/413] Remove unused protocol --- src/main.cpp | 1 - src/network/protocols/connect_to_peer.cpp | 2 - src/network/protocols/get_public_address.cpp | 258 ------------------- src/network/protocols/get_public_address.hpp | 68 ----- src/network/protocols/ping_protocol.cpp | 63 ----- src/network/protocols/ping_protocol.hpp | 38 --- src/network/protocols/server_lobby.cpp | 1 - 7 files changed, 431 deletions(-) delete mode 100644 src/network/protocols/get_public_address.cpp delete mode 100644 src/network/protocols/get_public_address.hpp delete mode 100644 src/network/protocols/ping_protocol.cpp delete mode 100644 src/network/protocols/ping_protocol.hpp diff --git a/src/main.cpp b/src/main.cpp index 03e5dbd64..03725aee1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -215,7 +215,6 @@ #include "network/rewind_queue.hpp" #include "network/servers_manager.hpp" #include "network/stk_host.hpp" -#include "network/protocols/get_public_address.hpp" #include "online/profile_manager.hpp" #include "online/request_manager.hpp" #include "race/grand_prix_manager.hpp" diff --git a/src/network/protocols/connect_to_peer.cpp b/src/network/protocols/connect_to_peer.cpp index 1241f30fb..a73a4efa8 100644 --- a/src/network/protocols/connect_to_peer.cpp +++ b/src/network/protocols/connect_to_peer.cpp @@ -20,11 +20,9 @@ #include "network/event.hpp" #include "network/network_config.hpp" -#include "network/protocols/get_public_address.hpp" #include "network/protocols/get_peer_address.hpp" #include "network/protocols/hide_public_address.hpp" #include "network/protocols/request_connection.hpp" -#include "network/protocols/ping_protocol.hpp" #include "network/protocol_manager.hpp" #include "network/stk_host.hpp" #include "utils/time.hpp" diff --git a/src/network/protocols/get_public_address.cpp b/src/network/protocols/get_public_address.cpp deleted file mode 100644 index 934fb34a2..000000000 --- a/src/network/protocols/get_public_address.cpp +++ /dev/null @@ -1,258 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2013-2015 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/protocols/get_public_address.hpp" - -#include "config/user_config.hpp" -#include "guiengine/message_queue.hpp" -#include "network/network.hpp" -#include "network/network_config.hpp" -#include "network/network_string.hpp" -#include "network/protocols/connect_to_server.hpp" -#include "network/stk_host.hpp" -#include "utils/log.hpp" -#include "utils/string_utils.hpp" - -#include -#include -#include -#include - -#ifdef __MINGW32__ -# undef _WIN32_WINNT -# define _WIN32_WINNT 0x501 -#endif - -#ifdef WIN32 -# include -# include -#else -# include -# include -#endif -#include - -// make the linker happy -const uint32_t GetPublicAddress::m_stun_magic_cookie = 0x2112A442; -TransportAddress GetPublicAddress::m_my_address(0, 0); - - -// ============================================================================ -GetPublicAddress::GetPublicAddress() - : Protocol(PROTOCOL_SILENT, NULL) -{ - m_untried_server = UserConfigParams::m_stun_servers; - // Generate random list of stun servers - std::random_device rd; - std::mt19937 g(rd()); - std::shuffle(m_untried_server.begin(), m_untried_server.end(), g); -} // GetPublicAddress - -// ---------------------------------------------------------------------------- -/** Creates a STUN request and sends it to a random STUN server selected from - * the list stored in the config file. See - * https://tools.ietf.org/html/rfc5389#section-6 - * for details on the message structure. - * The request is send through transaction_host, from which the answer - * will be retrieved by parseStunResponse() - */ -Network* GetPublicAddress::createStunRequest() -{ - if (m_untried_server.empty()) - { - // Notice: MessageQueue is thread safe to add - MessageQueue::add(MessageQueue::MT_ERROR, - _("Failed to get public address from stun server.")); - requestTerminate(); - return NULL; - } - - // Pick last element in untried servers - const char* server_name = m_untried_server.back().c_str(); - Log::debug("GetPublicAddress", "Using STUN server %s", server_name); - - struct addrinfo hints, *res; - - memset(&hints, 0, sizeof hints); - hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version - hints.ai_socktype = SOCK_STREAM; - - // Resolve the stun server name so we can send it a STUN request - int status = getaddrinfo(server_name, NULL, &hints, &res); - if (status != 0) - { - Log::error("GetPublicAddress", "Error in getaddrinfo for stun server" - " %s: %s", server_name, gai_strerror(status)); - m_untried_server.pop_back(); - return NULL; - } - m_untried_server.pop_back(); - // documentation says it points to "one or more addrinfo structures" - assert(res != NULL); - struct sockaddr_in* current_interface = (struct sockaddr_in*)(res->ai_addr); - m_stun_server_ip = ntohl(current_interface->sin_addr.s_addr); - - // Create a new socket for the stun server. - ENetAddress addr; - addr.host = STKHost::HOST_ANY; - addr.port = STKHost::PORT_ANY; - Network* transaction_host = new Network(1, 1, 0, 0, &addr); - - // Assemble the message for the stun server - BareNetworkString s(20); - - // bytes 0-1: the type of the message - // bytes 2-3: message length added to header (attributes) - uint16_t message_type = 0x0001; // binding request - uint16_t message_length = 0x0000; - s.addUInt16(message_type).addUInt16(message_length) - .addUInt32(0x2112A442); - // bytes 8-19: the transaction id - for (int i = 0; i < 12; i++) - { - uint8_t random_byte = rand() % 256; - s.addUInt8(random_byte); - m_stun_tansaction_id[i] = random_byte; - } - - transaction_host->sendRawPacket(s, - TransportAddress(m_stun_server_ip, - m_stun_server_port) ); - freeaddrinfo(res); - return transaction_host; -} // createStunRequest - -// ---------------------------------------------------------------------------- -/** - * Gets the response from the STUN server, checks it for its validity and - * then parses the answer into address and port - * \return "" if the address could be parsed or an error message -*/ -std::string GetPublicAddress::parseStunResponse(Network* transaction_host) -{ - TransportAddress sender; - const int LEN = 2048; - char buffer[LEN]; - int len = transaction_host->receiveRawPacket(buffer, LEN, &sender, 2000); - delete transaction_host; - - if(sender.getIP()!=m_stun_server_ip) - { - TransportAddress stun(m_stun_server_ip, m_stun_server_port); - Log::warn("GetPublicAddress", - "Received stun response from %s instead of %s.", - sender.toString().c_str(), stun.toString().c_str()); - } - - if (len<0) - return "STUN response contains no data at all"; - - // Convert to network string. - BareNetworkString datas(buffer, len); - - // check that the stun response is a response, contains the magic cookie - // and the transaction ID - if (datas.getUInt16() != 0x0101) - return "STUN response doesn't contain the magic cookie"; - int message_size = datas.getUInt16(); - if (datas.getUInt32() != m_stun_magic_cookie) - { - return "STUN response doesn't contain the magic cookie"; - } - - for (int i = 0; i < 12; i++) - { - if (datas.getUInt8() != m_stun_tansaction_id[i]) - return "STUN response doesn't contain the transaction ID"; - } - - Log::debug("GetPublicAddress", - "The STUN server responded with a valid answer"); - - // The stun message is valid, so we parse it now: - if (message_size == 0) - return "STUN response does not contain any information."; - if (message_size < 4) // cannot even read the size - return "STUN response is too short."; - - // Those are the port and the address to be detected - - while (true) - { - int type = datas.getUInt16(); - int size = datas.getUInt16(); - if (type == 0 || type == 1) - { - assert(size == 8); - datas.getUInt8(); // skip 1 byte - assert(datas.getUInt8() == 0x01); // Family IPv4 only - uint16_t port = datas.getUInt16(); - uint32_t ip = datas.getUInt32(); - TransportAddress address(ip, port); - // finished parsing, we know our public transport address - Log::debug("GetPublicAddress", - "The public address has been found: %s", - address.toString().c_str()); - NetworkConfig::get()->setMyAddress(address); - break; - } // type = 0 or 1 - datas.skip(4 + size); - message_size -= 4 + size; - if (message_size == 0) - return "STUN response is invalid."; - if (message_size < 4) // cannot even read the size - return "STUN response is invalid."; - } // while true - - return ""; -} // parseStunResponse - -// ---------------------------------------------------------------------------- -/** Detects public IP-address and port by first sending a request to a randomly - * selected STUN server and then parsing and validating the response */ -void GetPublicAddress::asynchronousUpdate() -{ - // If the user has specified an address, use it instead of the stun protocol. - if (m_my_address.getIP() != 0 && m_my_address.getPort() != 0) - { - NetworkConfig::get()->setMyAddress(m_my_address); - requestTerminate(); - } -//#define LAN_TEST -#ifdef LAN_TEST - TransportAddress address(0x7f000001, 4); - NetworkConfig::get()->setMyAddress(address); - requestTerminate(); - return; -#endif - - Network* transaction_host = createStunRequest(); - if (transaction_host) - { - std::string message = parseStunResponse(transaction_host); - if (message != "") - { - Log::warn("GetPublicAddress", "%s", message.c_str()); - } - else - { - // The address and the port are known, so the connection can be closed - requestTerminate(); - } - } -} // asynchronousUpdate diff --git a/src/network/protocols/get_public_address.hpp b/src/network/protocols/get_public_address.hpp deleted file mode 100644 index 545a7c4df..000000000 --- a/src/network/protocols/get_public_address.hpp +++ /dev/null @@ -1,68 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2013-2015 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef GET_PUBLIC_ADDRESS_HPP -#define GET_PUBLIC_ADDRESS_HPP - -#include "network/protocol.hpp" -#include "network/transport_address.hpp" -#include "utils/cpp2011.hpp" - -#include - -class Network; - -class GetPublicAddress : public Protocol -{ -private: - Network* createStunRequest(); - std::string parseStunResponse(Network* transaction_host); - - std::vector m_untried_server; - - // Constants - static const uint32_t m_stun_magic_cookie; - static const int m_stun_server_port = 3478; - - /** The user can specify its own IP address to make the use of stun - * unnecessary (though that means that the user has to take care of - * opening the firewall). */ - static TransportAddress m_my_address; - - uint8_t m_stun_tansaction_id[12]; - uint32_t m_stun_server_ip; - Network* m_transaction_host; - -public: - GetPublicAddress(); - virtual ~GetPublicAddress() {} - - virtual void asynchronousUpdate() OVERRIDE; - // ------------------------------------------------------------------------ - virtual void update(float dt) OVERRIDE {} - // ------------------------------------------------------------------------ - virtual bool notifyEvent(Event* event) OVERRIDE { return true; } - // ------------------------------------------------------------------------ - virtual bool notifyEventAsynchronous(Event* event) OVERRIDE { return true; } - // ------------------------------------------------------------------------ - virtual void setup() { } - // ------------------------------------------------------------------------ - -}; // class GetPublicAddress - -#endif // GET_PUBLIC_ADDRESS_HPP diff --git a/src/network/protocols/ping_protocol.cpp b/src/network/protocols/ping_protocol.cpp deleted file mode 100644 index 1398f451f..000000000 --- a/src/network/protocols/ping_protocol.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2013-2015 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/protocols/ping_protocol.hpp" - -#include "network/stk_host.hpp" -#include "utils/time.hpp" - -/** Constructor. Stores the destination address and how often to ping. - * \param ping_dest: Destination of ping request. - * \param delay_between_pings: How often to ping. - */ -PingProtocol::PingProtocol(const TransportAddress& ping_dst, - double delay_between_pings, double timeout) - : Protocol(PROTOCOL_SILENT) -{ - m_ping_dst.copy(ping_dst); - m_delay_between_pings = delay_between_pings; - m_timeout = timeout; -} // PingProtocol - -// ---------------------------------------------------------------------------- -PingProtocol::~PingProtocol() -{ -} // ~PingProtocol - -// ---------------------------------------------------------------------------- -void PingProtocol::setup() -{ - m_last_ping_time = 0.0; -} // setup - -// ---------------------------------------------------------------------------- -void PingProtocol::asynchronousUpdate() -{ - if (StkTime::getRealTime() > m_last_ping_time+m_delay_between_pings) - { - if (m_last_ping_time == 0.0) - m_timeout = StkTime::getRealTime() + m_timeout; - else if (StkTime::getRealTime() > m_timeout) - requestTerminate(); - m_last_ping_time = StkTime::getRealTime(); - BareNetworkString data; - data.addUInt8(0); - STKHost::get()->sendRawPacket(data, m_ping_dst); - Log::info("PingProtocol", "Ping message sent"); - } -} // asynchronousUpdate diff --git a/src/network/protocols/ping_protocol.hpp b/src/network/protocols/ping_protocol.hpp deleted file mode 100644 index 1a9f71c09..000000000 --- a/src/network/protocols/ping_protocol.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef PING_PROTOCOL_HPP -#define PING_PROTOCOL_HPP - -#include "network/protocol.hpp" -#include "network/transport_address.hpp" -#include "utils/cpp2011.hpp" - -class PingProtocol : public Protocol -{ -private: - /** The destination for the ping request. */ - TransportAddress m_ping_dst; - - /** How frequently to ping. */ - double m_delay_between_pings; - - /** Time of last ping. */ - double m_last_ping_time; - - /** If longer than this, terminate this protocol. */ - double m_timeout; -public: - PingProtocol(const TransportAddress& ping_dst, - double delay_between_pings, double timeout = 60.0); - virtual ~PingProtocol(); - - virtual void asynchronousUpdate() OVERRIDE; - - virtual void setup() OVERRIDE; - // ------------------------------------------------------------------------ - virtual bool notifyEvent(Event* event) OVERRIDE { return true; } - // ------------------------------------------------------------------------ - virtual bool notifyEventAsynchronous(Event* event) OVERRIDE { return true; } - // ------------------------------------------------------------------------ - virtual void update(float dt) OVERRIDE {} -}; - -#endif // PING_PROTOCOL_HPP diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index c19c253b9..9cefbce78 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -25,7 +25,6 @@ #include "network/event.hpp" #include "network/network_config.hpp" #include "network/network_player_profile.hpp" -#include "network/protocols/get_public_address.hpp" #include "network/protocols/connect_to_peer.hpp" #include "network/protocols/latency_protocol.hpp" #include "network/protocol_manager.hpp" From 33435de0265b021d889171579cbf3e8206a2479a Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 22 Feb 2018 16:03:54 +0800 Subject: [PATCH 136/413] Fix lan game in localhost, also improve timer behaviour --- src/network/protocols/connect_to_peer.cpp | 12 +++++------- src/network/protocols/connect_to_peer.hpp | 4 +--- src/network/protocols/connect_to_server.cpp | 13 +++++++------ src/network/protocols/connect_to_server.hpp | 4 +--- src/network/stk_host.cpp | 14 +++++++------- 5 files changed, 21 insertions(+), 26 deletions(-) diff --git a/src/network/protocols/connect_to_peer.cpp b/src/network/protocols/connect_to_peer.cpp index a73a4efa8..879824377 100644 --- a/src/network/protocols/connect_to_peer.cpp +++ b/src/network/protocols/connect_to_peer.cpp @@ -40,7 +40,6 @@ ConnectToPeer::ConnectToPeer(uint32_t peer_id) : Protocol(PROTOCOL_CONNECTION) m_state = NONE; m_is_lan = false; setHandleConnections(true); - resetTimer(); } // ConnectToPeer(peer_id) // ---------------------------------------------------------------------------- @@ -54,7 +53,6 @@ ConnectToPeer::ConnectToPeer(const TransportAddress &address) // We don't need to find the peer address, so we can start // with the state when we found the peer address. m_state = WAIT_FOR_CONNECTION; - resetTimer(); m_is_lan = true; setHandleConnections(true); } // ConnectToPeers(TransportAddress) @@ -113,15 +111,15 @@ void ConnectToPeer::asynchronousUpdate() } m_state = WAIT_FOR_CONNECTION; - resetTimer(); + m_timer = 0.0; break; } case WAIT_FOR_CONNECTION: { // Each 2 second for a ping or broadcast - if (m_timer > m_timer + std::chrono::seconds(2)) + if (StkTime::getRealTime() > m_timer + 2.0) { - resetTimer(); + m_timer = StkTime::getRealTime(); // Now we know the peer address. If it's a non-local host, start // the Ping protocol to keep the port available. We can't rely // on STKHost::isLAN(), since we might get a LAN connection even @@ -153,8 +151,8 @@ void ConnectToPeer::asynchronousUpdate() STKHost::get()->sendRawPacket(aloha, broadcast_address); Log::info("ConnectToPeer", "Broadcast aloha to self."); - // 30 seconds timeout - if (m_tried_connection++ > 15) + // 10 seconds timeout + if (m_tried_connection++ > 5) { // Not much we can do about if we don't receive the client // connection - it could have stopped, lost network, ... diff --git a/src/network/protocols/connect_to_peer.hpp b/src/network/protocols/connect_to_peer.hpp index 483b36cf5..d26c5ad86 100644 --- a/src/network/protocols/connect_to_peer.hpp +++ b/src/network/protocols/connect_to_peer.hpp @@ -45,7 +45,7 @@ protected: bool m_is_lan; /** Timer use for tracking broadcast. */ - std::chrono::system_clock::time_point m_timer; + double m_timer = 0.0; unsigned m_tried_connection = 0; @@ -60,8 +60,6 @@ protected: EXITING } m_state; - void resetTimer() { m_timer = std::chrono::system_clock::now(); } - public: ConnectToPeer(uint32_t peer_id); ConnectToPeer(const TransportAddress &address); diff --git a/src/network/protocols/connect_to_server.cpp b/src/network/protocols/connect_to_server.cpp index 0b4c47c37..5eebdc9b2 100644 --- a/src/network/protocols/connect_to_server.cpp +++ b/src/network/protocols/connect_to_server.cpp @@ -120,7 +120,7 @@ void ConnectToServer::asynchronousUpdate() request_connection->requestStart(); m_current_protocol = request_connection; // Reset timer for next usage - resetTimer(); + m_timer = 0.0; break; } case REQUESTING_CONNECTION: @@ -161,9 +161,9 @@ void ConnectToServer::asynchronousUpdate() { // Send a 1-byte datagram, the remote host can simply ignore // this datagram, to keep the port open (2 second each) - if (m_timer > m_timer + std::chrono::seconds(2)) + if (StkTime::getRealTime() > m_timer + 2.0) { - resetTimer(); + m_timer = StkTime::getRealTime(); BareNetworkString data; data.addUInt8(0); STKHost::get()->sendRawPacket(data, m_server_address); @@ -173,10 +173,11 @@ void ConnectToServer::asynchronousUpdate() break; case CONNECTING: // waiting the server to answer our connection { - if (m_timer > m_timer + std::chrono::seconds(5)) // every 5 seconds + // Every 5 seconds + if (StkTime::getRealTime() > m_timer + 5.0) { + m_timer = StkTime::getRealTime(); STKHost::get()->connect(m_server_address); - resetTimer(); Log::info("ConnectToServer", "Trying to connect to %s", m_server_address.toString().c_str()); if (m_tried_connection++ > 3) @@ -403,7 +404,7 @@ void ConnectToServer::waitingAloha(bool is_wan) } m_state = CONNECTING; // Reset timer for next usage - resetTimer(); + m_timer = 0.0; m_tried_connection = 0; } } // waitingAloha diff --git a/src/network/protocols/connect_to_server.hpp b/src/network/protocols/connect_to_server.hpp index 86f6bd0e4..1c83af53c 100644 --- a/src/network/protocols/connect_to_server.hpp +++ b/src/network/protocols/connect_to_server.hpp @@ -22,13 +22,12 @@ #include "network/protocol.hpp" #include "network/transport_address.hpp" #include "utils/cpp2011.hpp" -#include #include class ConnectToServer : public Protocol { private: - std::chrono::system_clock::time_point m_timer; + double m_timer = 0.0; TransportAddress m_server_address; uint32_t m_server_id; uint32_t m_host_id; @@ -54,7 +53,6 @@ private: void registerWithSTKServer(); void handleQuickConnect(); void waitingAloha(bool is_wan); - void resetTimer() { m_timer = std::chrono::system_clock::now(); } public: ConnectToServer(); diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index ff8cbd6a4..3a5c8c024 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -313,10 +313,9 @@ STKHost::STKHost(const irr::core::stringw &server_name) } setPrivatePort(); - if (NetworkConfig::get()->isWAN()) - { - setPublicAddress(); - } + // We need the public address for server no matter what to determine + // local lan connection + setPublicAddress(); // Don't construct server if no public address in WAN game if (!m_public_address.isUnset() || NetworkConfig::get()->isLAN()) { @@ -717,9 +716,10 @@ void STKHost::mainLoop() // A separate network connection (socket) to handle LAN requests. Network* lan_network = NULL; - if (NetworkConfig::get()->isLAN()) + if (NetworkConfig::get()->isLAN() && NetworkConfig::get()->isServer()) { - TransportAddress address(0, NetworkConfig::get()->getServerDiscoveryPort()); + TransportAddress address(0, + NetworkConfig::get()->getServerDiscoveryPort()); ENetAddress eaddr = address.toEnetAddress(); lan_network = new Network(1, 1, 0, 0, &eaddr); } @@ -830,7 +830,7 @@ void STKHost::handleDirectSocketRequest(Network* lan_network) { // In case of a LAN connection, we only allow connections from // a LAN address (192.168*, ..., and 127.*). - if (!sender.isLAN()) + if (!sender.isLAN() && sender.getIP() != m_public_address.getIP()) { Log::error("STKHost", "Client trying to connect from '%s'", sender.toString().c_str()); From 005454ba7bd9aa794cf9171a196cd05e8c41e98c Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 22 Feb 2018 16:38:53 +0800 Subject: [PATCH 137/413] Use the recieved port to connect, also fix a possible crash --- src/network/protocols/connect_to_peer.cpp | 14 -------------- src/network/protocols/connect_to_peer.hpp | 1 + src/network/protocols/connect_to_server.cpp | 5 +++-- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/src/network/protocols/connect_to_peer.cpp b/src/network/protocols/connect_to_peer.cpp index 879824377..0b97eab8f 100644 --- a/src/network/protocols/connect_to_peer.cpp +++ b/src/network/protocols/connect_to_peer.cpp @@ -120,20 +120,6 @@ void ConnectToPeer::asynchronousUpdate() if (StkTime::getRealTime() > m_timer + 2.0) { m_timer = StkTime::getRealTime(); - // Now we know the peer address. If it's a non-local host, start - // the Ping protocol to keep the port available. We can't rely - // on STKHost::isLAN(), since we might get a LAN connection even - // if the server itself accepts connections from anywhere. - if ((!m_is_lan && - m_peer_address.getIP() != - STKHost::get()->getPublicAddress().getIP()) || - NetworkConfig::m_disable_lan) - { - BareNetworkString data; - data.addUInt8(0); - STKHost::get()->sendRawPacket(data, m_peer_address); - } - // Send a broadcast packet with the string aloha_stk inside, // the client will know our ip address and will connect // The wan remote should already start its ping message to us now diff --git a/src/network/protocols/connect_to_peer.hpp b/src/network/protocols/connect_to_peer.hpp index d26c5ad86..88e7c53e5 100644 --- a/src/network/protocols/connect_to_peer.hpp +++ b/src/network/protocols/connect_to_peer.hpp @@ -47,6 +47,7 @@ protected: /** Timer use for tracking broadcast. */ double m_timer = 0.0; + /** If greater than a certain value, terminate this protocol. */ unsigned m_tried_connection = 0; enum STATE diff --git a/src/network/protocols/connect_to_server.cpp b/src/network/protocols/connect_to_server.cpp index 5eebdc9b2..859883992 100644 --- a/src/network/protocols/connect_to_server.cpp +++ b/src/network/protocols/connect_to_server.cpp @@ -211,7 +211,8 @@ void ConnectToServer::asynchronousUpdate() } m_state = DONE; // lobby room protocol if we're connected only - if (STKHost::get()->getPeers()[0]->isConnected() && + if (STKHost::get()->getPeerCount() > 0 && + STKHost::get()->getPeers()[0]->isConnected() && !m_server_address.isUnset()) { auto cl = LobbyProtocol::create(); @@ -400,8 +401,8 @@ void ConnectToServer::waitingAloha(bool is_wan) } delete[] table; #endif - m_server_address.copy(sender); } + m_server_address.copy(sender); m_state = CONNECTING; // Reset timer for next usage m_timer = 0.0; From 1023e6580ef6493c7691d2a88cbd3476b2bf8049 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 23 Feb 2018 14:01:20 +0800 Subject: [PATCH 138/413] Unregister STK server when exiting --- src/main.cpp | 3 - src/network/network_config.cpp | 13 --- src/network/network_config.hpp | 35 -------- src/network/network_console.cpp | 9 --- src/network/protocols/connect_to_peer.cpp | 1 - src/network/protocols/connect_to_server.cpp | 2 +- src/network/protocols/server_lobby.cpp | 58 +++++++++++++- src/network/protocols/server_lobby.hpp | 4 + src/network/protocols/stop_server.cpp | 89 --------------------- src/network/protocols/stop_server.hpp | 36 --------- src/network/servers_manager.cpp | 3 - src/network/stk_host.cpp | 1 - src/network/stk_host.hpp | 16 ---- src/states_screens/create_server_screen.cpp | 3 +- 14 files changed, 61 insertions(+), 212 deletions(-) delete mode 100644 src/network/protocols/stop_server.cpp delete mode 100644 src/network/protocols/stop_server.hpp diff --git a/src/main.cpp b/src/main.cpp index 03725aee1..8fd337154 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1003,11 +1003,8 @@ int handleCmdLine() if (CommandLine::has("--connect-now", &s)) { TransportAddress ip(s); - TransportAddress me(2130706433/*127.0.0.1*/, - NetworkConfig::get()->getServerDiscoveryPort() ); NetworkConfig::get()->setIsLAN(); NetworkConfig::get()->setIsServer(false); - NetworkConfig::get()->setMyAddress(me); Log::info("main", "Try to connect to server '%s'.", ip.toString().c_str() ); irr::core::stringw name = StringUtils::utf8ToWide(ip.toString()); diff --git a/src/network/network_config.cpp b/src/network/network_config.cpp index fb2b2f8e1..87fb75a91 100644 --- a/src/network/network_config.cpp +++ b/src/network/network_config.cpp @@ -46,21 +46,8 @@ NetworkConfig::NetworkConfig() m_server_discovery_port = 2757; m_server_port = 2758; m_client_port = 2759; - m_my_address.lock(); - m_my_address.getData().clear(); - m_my_address.unlock(); } // NetworkConfig -//----------------------------------------------------------------------------- -/** Stores the public address of this host. - */ -void NetworkConfig::setMyAddress(const TransportAddress& addr) -{ - m_my_address.lock(); - m_my_address.getData().copy(addr); - m_my_address.unlock(); -} // setPublicAddress - // -------------------------------------------------------------------- /** Sets if this instance is a server or client. It also assigns the * private port depending if this is a server or client. diff --git a/src/network/network_config.hpp b/src/network/network_config.hpp index 44b406a1e..80e2d83a7 100644 --- a/src/network/network_config.hpp +++ b/src/network/network_config.hpp @@ -52,11 +52,6 @@ private: /** The password for a server (or to authenticate to a server). */ std::string m_password; - /** This is either this computer's public IP address, or the LAN - * address in case of a LAN game. With lock since it can - * be updated from a separate thread. */ - Synchronised m_my_address; - /** The port number to which the server listens to detect LAN requests. */ uint16_t m_server_discovery_port; @@ -103,7 +98,6 @@ public: } // destroy // ------------------------------------------------------------------------ - void setMyAddress(const TransportAddress& addr); void setIsServer(bool b); // ------------------------------------------------------------------------ /** Sets the port for server discovery. */ @@ -183,35 +177,6 @@ public: return m_server_name; } // getServerName // -------------------------------------------------------------------- - /** Sets if this server is registered with the stk server. */ - void setRegistered(bool registered) - { - assert(isServer()); - m_is_registered = registered; - } // setRegistered - // -------------------------------------------------------------------- - /** Returns if this server is registered with the stk server. */ - bool isRegistered() const - { - assert(isServer()); - return m_is_registered; - } // isRegistered - - // -------------------------------------------------------------------- - /** Returns the IP address of this host. We need to return a copy - * to make sure the address is thread safe (otherwise it could happen - * that e.g. data is taken when the IP address was written, but not - return a; - * yet the port). */ - const TransportAddress getMyAddress() const - { - TransportAddress a; - m_my_address.lock(); - a.copy(m_my_address.getData()); - m_my_address.unlock(); - return a; - } // getMyAddress - // -------------------------------------------------------------------- /** Sets if a client should immediately connect to the first server. */ void setAutoConnect(bool b) { m_auto_connect = b; } // -------------------------------------------------------------------- diff --git a/src/network/network_console.cpp b/src/network/network_console.cpp index 3b419766a..66bf830b1 100644 --- a/src/network/network_console.cpp +++ b/src/network/network_console.cpp @@ -24,7 +24,6 @@ #include "network/stk_host.hpp" #include "network/protocols/client_lobby.hpp" #include "network/protocols/server_lobby.hpp" -#include "network/protocols/stop_server.hpp" #include "network/stk_peer.hpp" #include "utils/log.hpp" #include "utils/time.hpp" @@ -157,14 +156,6 @@ void* NetworkConsole::mainLoop(void* data) Log::info("Console", "Unknown command '%s'.", str.c_str()); } } // while !stop - - auto p = std::make_shared(); - p->requestStart(); - while(p->getState() != PROTOCOL_STATE_TERMINATED) - { - StkTime::sleep(1); - } - main_loop->abort(); return NULL; diff --git a/src/network/protocols/connect_to_peer.cpp b/src/network/protocols/connect_to_peer.cpp index 0b97eab8f..33adbc294 100644 --- a/src/network/protocols/connect_to_peer.cpp +++ b/src/network/protocols/connect_to_peer.cpp @@ -21,7 +21,6 @@ #include "network/event.hpp" #include "network/network_config.hpp" #include "network/protocols/get_peer_address.hpp" -#include "network/protocols/hide_public_address.hpp" #include "network/protocols/request_connection.hpp" #include "network/protocol_manager.hpp" #include "network/stk_host.hpp" diff --git a/src/network/protocols/connect_to_server.cpp b/src/network/protocols/connect_to_server.cpp index 859883992..742178e8f 100644 --- a/src/network/protocols/connect_to_server.cpp +++ b/src/network/protocols/connect_to_server.cpp @@ -347,9 +347,9 @@ void ConnectToServer::waitingAloha(bool is_wan) { Log::info("ConnectToServer", "Server found : %s", sender.toString().c_str()); -#ifndef WIN32 if (!is_wan) { +#ifndef WIN32 // just check if the ip is ours : if so, // then just use localhost (127.0.0.1) struct ifaddrs *ifap, *ifa; diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 9cefbce78..78d538171 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -99,12 +99,17 @@ ServerLobby::ServerLobby() : LobbyProtocol(NULL) */ ServerLobby::~ServerLobby() { + if (m_server_registered) + { + unregisterServer(); + } } // ~ServerLobby //----------------------------------------------------------------------------- void ServerLobby::setup() { + m_server_registered = false; m_game_setup = STKHost::get()->setupNewGame(); m_game_setup->setNumLocalPlayers(0); // no local players on a server m_next_player_id.setAtomic(0); @@ -316,7 +321,7 @@ void ServerLobby::registerServer() request->addParameter("name", NetworkConfig::get()->getServerName() ); request->addParameter("max_players", UserConfigParams::m_server_max_players ); - Log::info("RegisterServer", "Showing addr %s", addr.toString().c_str()); + Log::info("ServerLobby", "Public server addr %s", addr.toString().c_str()); request->executeNow(); @@ -325,13 +330,13 @@ void ServerLobby::registerServer() if (result->get("success", &rec_success) && rec_success == "yes") { - Log::info("RegisterServer", "Server is now online."); - STKHost::get()->setRegistered(true); + Log::info("ServerLobby", "Server is now online."); + m_server_registered = true; } else { irr::core::stringc error(request->getInfo().c_str()); - Log::error("RegisterServer", "%s", error.c_str()); + Log::error("ServerLobby", "%s", error.c_str()); STKHost::get()->setErrorMessage(_("Failed to register server: %s", error.c_str())); STKHost::get()->requestShutdown(); @@ -339,6 +344,45 @@ void ServerLobby::registerServer() delete request; } // registerServer + +//----------------------------------------------------------------------------- +/** Unregister this server (i.e. its public address) with the STK server, + * currently when karts enter kart selection screen it will be done. + */ +void ServerLobby::unregisterServer() +{ + const TransportAddress &addr = STKHost::get()->getPublicAddress(); + Online::XMLRequest* request = new Online::XMLRequest(); + PlayerManager::setUserDetails(request, "stop", Online::API::SERVER_PATH); + + request->addParameter("address", addr.getIP()); + request->addParameter("port", addr.getPort()); + + Log::info("ServerLobby", "address %s", addr.toString().c_str()); + request->executeNow(); + + const XMLNode * result = request->getXMLData(); + std::string rec_success; + + if (result->get("success", &rec_success)) + { + if (rec_success == "yes") + { + Log::info("ServerLobby", "Server is now unregister."); + } + else + { + Log::error("ServerLobby", "Fail to unregister server."); + } + } + else + { + Log::error("ServerLobby", "Fail to stop server."); + } + delete request; + +} // unregisterServer + //----------------------------------------------------------------------------- /** This function is called when all clients have loaded the world and * are therefore ready to start the race. It signals to all clients @@ -362,6 +406,12 @@ void ServerLobby::signalRaceStartToClients() */ void ServerLobby::startSelection(const Event *event) { + assert(m_server_registered); + if (m_server_registered) + { + unregisterServer(); + m_server_registered = false; + } if (m_state != ACCEPTING_CLIENTS) { diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 8e99909ca..6448a3cf9 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -58,6 +58,9 @@ private: bool m_selection_enabled; + /** It indicates if this server is registered with the stk server. */ + std::atomic_bool m_server_registered; + /** Counts how many players are ready to go on. */ int m_player_ready_counter; @@ -80,6 +83,7 @@ private: void registerServer(); void finishedLoadingWorldClient(Event *event); void startedRaceOnClient(Event *event); + void unregisterServer(); public: ServerLobby(); virtual ~ServerLobby(); diff --git a/src/network/protocols/stop_server.cpp b/src/network/protocols/stop_server.cpp deleted file mode 100644 index 8699bb3f7..000000000 --- a/src/network/protocols/stop_server.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2013-2015 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/protocols/stop_server.hpp" - -#include "config/player_manager.hpp" -#include "config/user_config.hpp" -#include "network/network_config.hpp" -#include "online/request_manager.hpp" - -StopServer::StopServer() : Protocol(PROTOCOL_SILENT) -{ -} - -StopServer::~StopServer() -{ -} - -bool StopServer::notifyEventAsynchronous(Event* event) -{ - return true; -} - -void StopServer::setup() -{ - m_state = NONE; -} - -void StopServer::asynchronousUpdate() -{ - if (m_state == NONE) - { - const TransportAddress& addr = NetworkConfig::get()->getMyAddress(); - m_request = new Online::XMLRequest(); - PlayerManager::setUserDetails(m_request, "stop", Online::API::SERVER_PATH); - - m_request->addParameter("address", addr.getIP()); - m_request->addParameter("port", addr.getPort()); - - Log::info("StopServer", "address %s", addr.toString().c_str()); - - Online::RequestManager::get()->addRequest(m_request); - m_state = REQUEST_PENDING; - } - else if (m_state == REQUEST_PENDING && m_request->isDone()) - { - const XMLNode * result = m_request->getXMLData(); - std::string rec_success; - - if(result->get("success", &rec_success)) - { - if(rec_success == "yes") - { - Log::info("StopServer", "Server is now offline."); - } - else - { - Log::error("StopServer", "Fail to stop server."); - } - } - else - { - Log::error("StopServer", "Fail to stop server."); - } - m_state = DONE; - } - else if (m_state == DONE) - { - m_state = EXITING; - delete m_request; - m_request = NULL; - requestTerminate(); - } -} // asynchronousUpdate diff --git a/src/network/protocols/stop_server.hpp b/src/network/protocols/stop_server.hpp deleted file mode 100644 index 6ec1c814e..000000000 --- a/src/network/protocols/stop_server.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef STOP_SERVER_HPP -#define STOP_SERVER_HPP - -#include "network/protocol.hpp" -#include "utils/cpp2011.hpp" - -namespace Online { class XMLRequest; } - -/*! \brief Removes the server info from the database - */ - -class StopServer : public Protocol -{ -private: - Online::XMLRequest* m_request; - enum STATE - { - NONE, - REQUEST_PENDING, - DONE, - EXITING - }; - STATE m_state; -public: - StopServer(); - virtual ~StopServer(); - - virtual bool notifyEventAsynchronous(Event* event) OVERRIDE; - virtual void setup() OVERRIDE; - virtual void asynchronousUpdate() OVERRIDE; - // -------------------------------------------------------------------- - virtual void update(float dt) OVERRIDE {} - -}; - -#endif // STOP_SERVER_HPP diff --git a/src/network/servers_manager.cpp b/src/network/servers_manager.cpp index 074904bb6..b8fef7a2e 100644 --- a/src/network/servers_manager.cpp +++ b/src/network/servers_manager.cpp @@ -190,9 +190,6 @@ Online::XMLRequest* ServersManager::getLANRefreshRequest() const server->setDifficulty((RaceManager::Difficulty)difficulty); server->setRaceMinorMode((RaceManager::MinorRaceModeType)mode); ServersManager::get()->addServer(server); - - TransportAddress me(my_ip, my_port); - NetworkConfig::get()->setMyAddress(me); m_success = true; } // if received_data } // while still waiting diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index 3a5c8c024..acc6d4206 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -335,7 +335,6 @@ void STKHost::init() m_shutdown = false; m_network = NULL; m_game_setup = NULL; - m_is_registered = false; m_error_message = ""; m_exit_flag.clear(); diff --git a/src/network/stk_host.hpp b/src/network/stk_host.hpp index 5092df90e..16db2686e 100644 --- a/src/network/stk_host.hpp +++ b/src/network/stk_host.hpp @@ -95,10 +95,6 @@ private: /** Atomic flag used to stop this thread. */ std::atomic_flag m_exit_flag = ATOMIC_FLAG_INIT; - /** If this is a server, it indicates if this server is registered - * with the stk server. */ - bool m_is_registered; - /** An error message, which is set by a protocol to be displayed * in the GUI. */ irr::core::stringw m_error_message; @@ -242,18 +238,6 @@ public: /** Returns the number of currently connected peers. */ unsigned int getPeerCount() { return (int)m_peers.size(); } // -------------------------------------------------------------------- - /** Sets if this server is registered with the stk server. */ - void setRegistered(bool registered) - { - m_is_registered = registered; - } // setRegistered - // -------------------------------------------------------------------- - /** Returns if this server is registered with the stk server. */ - bool isRegistered() const - { - return m_is_registered; - } // isRegistered - // -------------------------------------------------------------------- /** Sets the global host id of this host. */ void setMyHostId(uint8_t my_host_id) { m_host_id = my_host_id; } // -------------------------------------------------------------------- diff --git a/src/states_screens/create_server_screen.cpp b/src/states_screens/create_server_screen.cpp index ef85f887b..bd8157aac 100644 --- a/src/states_screens/create_server_screen.cpp +++ b/src/states_screens/create_server_screen.cpp @@ -24,6 +24,7 @@ #include "config/player_manager.hpp" #include "config/user_config.hpp" #include "modes/demo_world.hpp" +#include "network/protocols/lobby_protocol.hpp" #include "network/network_config.hpp" #include "network/servers_manager.hpp" #include "network/stk_host.hpp" @@ -148,7 +149,7 @@ void CreateServerScreen::onUpdate(float delta) // Otherwise wait till we get an answer from the server: // ----------------------------------------------------- - if(!STKHost::get()->isRegistered() && !NetworkConfig::get()->isLAN()) + if (!LobbyProtocol::get()) { m_info_widget->setDefaultColor(); m_info_widget->setText(StringUtils::loadingDots(_("Creating server")), From 7d14954012a912d7c075d37fe5a81e24fefdc083 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 23 Feb 2018 14:57:59 +0800 Subject: [PATCH 139/413] Simpify network console --- src/main.cpp | 8 ++--- src/main_loop.cpp | 17 +++++---- src/main_loop.hpp | 4 +-- src/network/network_console.cpp | 64 ++++++++++----------------------- src/network/network_console.hpp | 31 ++-------------- src/network/stk_host.cpp | 12 ++++--- src/network/stk_host.hpp | 5 ++- 7 files changed, 46 insertions(+), 95 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 8fd337154..bd495f47b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -987,6 +987,9 @@ int handleCmdLine() } // Networking command lines + if(CommandLine::has("--start-console")) + STKHost::m_enable_console = true; + NetworkConfig::get()-> setMaxPlayers(UserConfigParams::m_server_max_players); if (CommandLine::has("--port", &n)) @@ -1040,7 +1043,7 @@ int handleCmdLine() Log::warn("main", "No saved online player session to create a wan server"); } } - if (CommandLine::has("--lan-server", &s)) + else if (CommandLine::has("--lan-server", &s)) { NetworkConfig::get()->setServerName(core::stringw(s.c_str())); NetworkConfig::get()->setIsServer(true); @@ -1059,9 +1062,6 @@ int handleCmdLine() if(CommandLine::has("--max-players", &n)) UserConfigParams::m_server_max_players=n; - if(CommandLine::has("--start-console")) - STKHost::m_enable_console = true; - if(CommandLine::has("--login", &s) ) { login = s.c_str(); diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 352325e3d..4b6206743 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -277,7 +277,7 @@ void MainLoop::run() float dt = 1.0f / stk_config->m_physics_fps; left_over_time -= num_steps * dt ; - if (!ProfileWorld::isNoGraphics() && STKHost::existHost() && + if (STKHost::existHost() && STKHost::get()->requestedShutdown()) { STKHost::get()->shutdown(); @@ -285,13 +285,18 @@ void MainLoop::run() { race_manager->exitRace(); } - GUIEngine::Screen* new_stack[] = + if (!ProfileWorld::isNoGraphics()) { - MainMenuScreen::getInstance(), OnlineScreen::getInstance(), NULL - }; - StateManager::get()->resetAndSetStack(new_stack); + GUIEngine::Screen* new_stack[] = + { + MainMenuScreen::getInstance(), + OnlineScreen::getInstance(), NULL + }; + StateManager::get()->resetAndSetStack(new_stack); + MessageQueue::add(MessageQueue::MT_ERROR, + _("Connection to server is lost.")); + } NetworkConfig::get()->unsetNetworking(); - MessageQueue::add(MessageQueue::MT_ERROR, _("Connection to server is lost.")); } // Add a Time step entry to the rewind list, which can store all diff --git a/src/main_loop.hpp b/src/main_loop.hpp index 0aae68201..766a47c7b 100644 --- a/src/main_loop.hpp +++ b/src/main_loop.hpp @@ -21,7 +21,7 @@ #define HEADER_MAIN_LOOP_HPP typedef unsigned long Uint32; - +#include /** Management class for the whole gameflow, this is where the main-loop is */ @@ -29,7 +29,7 @@ class MainLoop { private: /** True if the main loop should exit. */ - bool m_abort; + std::atomic_bool m_abort; /** True if the frame rate should be throttled. */ bool m_throttle_fps; diff --git a/src/network/network_console.cpp b/src/network/network_console.cpp index 66bf830b1..455d78c81 100644 --- a/src/network/network_console.cpp +++ b/src/network/network_console.cpp @@ -16,9 +16,6 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "network/network_console.hpp" - -#include "main_loop.hpp" #include "network/network_config.hpp" #include "network/network_player_profile.hpp" #include "network/stk_host.hpp" @@ -28,52 +25,37 @@ #include "utils/log.hpp" #include "utils/time.hpp" #include "utils/vs.hpp" +#include "main_loop.hpp" #include - -NetworkConsole::NetworkConsole() +namespace NetworkConsole { - m_localhost = NULL; - m_thread_keyboard = NULL; -} +// ---------------------------------------------------------------------------- +void kickAllPlayers(STKHost* host) +{ + const std::vector &peers = host->getPeers(); + for (unsigned int i = 0; i < peers.size(); i++) + { + peers[i]->disconnect(); + } +} // kickAllPlayers // ---------------------------------------------------------------------------- -NetworkConsole::~NetworkConsole() -{ -#ifndef ANDROID - if (m_thread_keyboard) - pthread_cancel(*m_thread_keyboard);//, SIGKILL); -#endif -} - -// ---------------------------------------------------------------------------- -void NetworkConsole::run() -{ - // listen keyboard console input - m_thread_keyboard = new pthread_t; - pthread_create(m_thread_keyboard, NULL, mainLoop, this); - - Log::info("NetworkConsole", "Ready."); -} // run - -// ---------------------------------------------------------------------------- -void* NetworkConsole::mainLoop(void* data) +void mainLoop(STKHost* host) { VS::setThreadName("NetworkConsole"); - NetworkConsole *me = static_cast(data); std::string str = ""; - bool stop = false; - while (!stop) + while (!host->requestedShutdown()) { getline(std::cin, str); if (str == "quit") { - stop = true; + host->requestShutdown(); } else if (str == "kickall" && NetworkConfig::get()->isServer()) { - me->kickAllPlayers(); + kickAllPlayers(host); } else if (str == "start" && NetworkConfig::get()->isServer()) { @@ -91,7 +73,7 @@ void* NetworkConsole::mainLoop(void* data) getline(std::cin, str2); auto clrp = LobbyProtocol::get(); std::vector players = - STKHost::get()->getMyPlayerProfiles(); + host->getMyPlayerProfiles(); // For now send a vote for each local player for(unsigned int i=0; i(); std::vector players = - STKHost::get()->getMyPlayerProfiles(); + host->getMyPlayerProfiles(); if (str2 == "track") { std::cin >> str2; @@ -157,16 +139,6 @@ void* NetworkConsole::mainLoop(void* data) } } // while !stop main_loop->abort(); - - return NULL; } // mainLoop -// ---------------------------------------------------------------------------- -void NetworkConsole::kickAllPlayers() -{ - const std::vector &peers = STKHost::get()->getPeers(); - for (unsigned int i = 0; i < peers.size(); i++) - { - peers[i]->disconnect(); - } -} // kickAllPlayers +} diff --git a/src/network/network_console.hpp b/src/network/network_console.hpp index 4a429d1fd..299c04a07 100644 --- a/src/network/network_console.hpp +++ b/src/network/network_console.hpp @@ -19,38 +19,11 @@ #ifndef HEADER_NETWORK_CONSOLE_HPP #define HEADER_NETWORK_CONSOLE_HPP -#include "utils/types.hpp" - -#include "pthread.h" - -class NetworkString; class STKHost; -class NetworkConsole +namespace NetworkConsole { -protected: - - STKHost *m_localhost; - - pthread_t* m_thread_keyboard; - - uint8_t m_max_players; - - static void* mainLoop(void* data); - -public: - NetworkConsole(); - virtual ~NetworkConsole(); - - virtual void run(); - void kickAllPlayers(); - // ------------------------------------------------------------------------ - void setMaxPlayers(uint8_t count) { m_max_players = count; } - // ------------------------------------------------------------------------ - uint8_t getMaxPlayers() { return m_max_players; } - // ------------------------------------------------------------------------ - virtual bool isServer() { return true; } - + void mainLoop(STKHost* host); }; // class NetworkConsole #endif // SERVER_CONSOLE_HPP diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index acc6d4206..d3cd29101 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -353,12 +353,11 @@ void STKHost::init() ProtocolManager::createInstance(); // Optional: start the network console - m_network_console = NULL; - if(m_enable_console) + if (m_enable_console) { - m_network_console = new NetworkConsole(); - m_network_console->run(); - } + m_network_console = std::thread(std::bind(&NetworkConsole::mainLoop, + this)); + } } // STKHost // ---------------------------------------------------------------------------- @@ -367,6 +366,9 @@ void STKHost::init() */ STKHost::~STKHost() { + requestShutdown(); + if (m_network_console.joinable()) + m_network_console.join(); // delete the game setup if (m_game_setup) delete m_game_setup; diff --git a/src/network/stk_host.hpp b/src/network/stk_host.hpp index 16db2686e..fbaa118bc 100644 --- a/src/network/stk_host.hpp +++ b/src/network/stk_host.hpp @@ -42,7 +42,6 @@ #include class GameSetup; -class NetworkConsole; class STKHost { @@ -67,8 +66,8 @@ private: /** ENet host interfacing sockets. */ Network* m_network; - /** Network console */ - NetworkConsole *m_network_console; + /** Network console thread */ + std::thread m_network_console; /** The list of peers connected to this instance. */ std::vector m_peers; From a93182740e4237c3da51ff6d41021e39a287ce5b Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 23 Feb 2018 15:46:03 +0800 Subject: [PATCH 140/413] Reset NetworkConfig when exiting WAN game screen --- src/states_screens/online_profile_servers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/states_screens/online_profile_servers.cpp b/src/states_screens/online_profile_servers.cpp index 1e9a9c355..24966cae1 100644 --- a/src/states_screens/online_profile_servers.cpp +++ b/src/states_screens/online_profile_servers.cpp @@ -80,7 +80,7 @@ void OnlineProfileServers::eventCallback(Widget* widget, const std::string& name { if (name == "back") { - StateManager::get()->popMenu(); + StateManager::get()->escapePressed(); return; } if (name == "wan") From d586ab9011eef26a36022ef579b789d64b75741e Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 23 Feb 2018 16:16:43 +0800 Subject: [PATCH 141/413] Allow auto-fallback to another unused port if needed --- src/network/network.cpp | 20 +++++++++++++++----- src/network/network.hpp | 3 ++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index d8ec5f4af..b8dd1027f 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -45,19 +45,29 @@ SynchronisedNetwork::m_log_file = NULL; * \param channel_limit : The maximum number of channels per peer. * \param max_incoming_bandwidth : The maximum incoming bandwidth. * \param max_outgoing_bandwidth : The maximum outgoing bandwidth. + * \param change_port_if_bound : Use another port if the prefered port is + * already bound to a socket. */ Network::Network(int peer_count, int channel_limit, uint32_t max_incoming_bandwidth, uint32_t max_outgoing_bandwidth, - ENetAddress* address) + ENetAddress* address, bool change_port_if_bound) { m_host = enet_host_create(address, peer_count, channel_limit, 0, 0); - if (!m_host) + if (m_host) + return; + if (change_port_if_bound) { - Log::fatal("Network", "An error occurred while trying to create an " - "ENet client host, maybe you started multiple instance " - "of STK server?"); + ENetAddress new_addr; + new_addr.host = address->host; + // Any port + new_addr.port = 0; + m_host = enet_host_create(&new_addr, peer_count, channel_limit, 0, 0); + if (m_host) + return; } + Log::fatal("Network", "An error occurred while trying to create an ENet " + "client host, maybe you started multiple instance of STK server?"); } // Network // ---------------------------------------------------------------------------- diff --git a/src/network/network.hpp b/src/network/network.hpp index d2f4d09b0..ac7a54aaa 100644 --- a/src/network/network.hpp +++ b/src/network/network.hpp @@ -54,7 +54,8 @@ public: Network(int peer_count, int channel_limit, uint32_t max_incoming_bandwidth, uint32_t max_outgoing_bandwidth, - ENetAddress* address); + ENetAddress* address, + bool change_port_if_bound = false); virtual ~Network(); static void openLog(); From b26b784b6ae417b43e1a471ca763198f609e9065 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 24 Feb 2018 15:48:30 +0800 Subject: [PATCH 142/413] Various improvements to WAN and LAN connection --- src/main_loop.cpp | 9 +- src/network/network.cpp | 2 + src/network/protocols/connect_to_server.cpp | 80 ++------- src/network/protocols/connect_to_server.hpp | 1 + src/network/protocols/server_lobby.cpp | 89 ++++++--- src/network/protocols/server_lobby.hpp | 9 +- src/network/stk_host.cpp | 190 ++++++++++---------- src/network/stk_host.hpp | 25 ++- src/network/transport_address.cpp | 99 ++++++++-- src/network/transport_address.hpp | 3 + src/states_screens/create_server_screen.cpp | 21 --- 11 files changed, 286 insertions(+), 242 deletions(-) diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 4b6206743..9c78f11d2 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -280,6 +280,12 @@ void MainLoop::run() if (STKHost::existHost() && STKHost::get()->requestedShutdown()) { + SFXManager::get()->quickSound("anvil"); + core::stringw msg = _("Connection to server is lost."); + if (!STKHost::get()->getErrorMessage().empty()) + { + msg = STKHost::get()->getErrorMessage(); + } STKHost::get()->shutdown(); if (World::getWorld()) { @@ -293,8 +299,7 @@ void MainLoop::run() OnlineScreen::getInstance(), NULL }; StateManager::get()->resetAndSetStack(new_stack); - MessageQueue::add(MessageQueue::MT_ERROR, - _("Connection to server is lost.")); + MessageQueue::add(MessageQueue::MT_ERROR, msg); } NetworkConfig::get()->unsetNetworking(); } diff --git a/src/network/network.cpp b/src/network/network.cpp index b8dd1027f..a6cbb061e 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -58,6 +58,8 @@ Network::Network(int peer_count, int channel_limit, return; if (change_port_if_bound) { + Log::warn("Network", "%d port is in used, use another port", + address->port); ENetAddress new_addr; new_addr.host = address->host; // Any port diff --git a/src/network/protocols/connect_to_server.cpp b/src/network/protocols/connect_to_server.cpp index 742178e8f..65a0af5ea 100644 --- a/src/network/protocols/connect_to_server.cpp +++ b/src/network/protocols/connect_to_server.cpp @@ -32,12 +32,6 @@ #include "utils/time.hpp" #include "utils/log.hpp" -#ifdef WIN32 -# include -#else -#include -#endif - // ---------------------------------------------------------------------------- /** Connects to a server. This is the quick connect constructor, which * will pick a server randomly. @@ -84,8 +78,7 @@ void ConnectToServer::setup() // In case of LAN we already have the server's and our ip address, // so we can immediately start requesting a connection. m_state = NetworkConfig::get()->isLAN() ? GOT_SERVER_ADDRESS : - REGISTER_SELF_ADDRESS; - + SET_PUBLIC_ADDRESS; } // setup // ---------------------------------------------------------------------------- @@ -93,10 +86,17 @@ void ConnectToServer::asynchronousUpdate() { switch(m_state) { + case SET_PUBLIC_ADDRESS: + { + STKHost::get()->setPublicAddress(); + // Set to DONE will stop STKHost is not connected + m_state = STKHost::get()->getPublicAddress().isUnset() ? + DONE : REGISTER_SELF_ADDRESS; + } + break; case REGISTER_SELF_ADDRESS: { registerWithSTKServer(); // Register us with STK server - if (m_quick_join) { handleQuickConnect(); @@ -226,6 +226,9 @@ void ConnectToServer::asynchronousUpdate() if (STKHost::get()->getPeerCount() == 0) { // Shutdown STKHost (go back to online menu too) + STKHost::get()->setErrorMessage( + _("Cannot connect to server with address: %s.", + m_server_address.toString().c_str())); STKHost::get()->requestShutdown(); } break; @@ -266,7 +269,10 @@ void ConnectToServer::registerWithSTKServer() } else { - Log::error("ConnectToServer", "Failed to register address."); + irr::core::stringc error(request->getInfo().c_str()); + Log::error("ConnectToServer", "Failed to register client address: %s", + error.c_str()); + m_state = DONE; } delete request; @@ -349,58 +355,8 @@ void ConnectToServer::waitingAloha(bool is_wan) sender.toString().c_str()); if (!is_wan) { -#ifndef WIN32 - // just check if the ip is ours : if so, - // then just use localhost (127.0.0.1) - struct ifaddrs *ifap, *ifa; - struct sockaddr_in *sa; - getifaddrs(&ifap); // get the info - for (ifa = ifap; ifa; ifa = ifa->ifa_next) - { - if (ifa->ifa_addr->sa_family == AF_INET) - { - sa = (struct sockaddr_in *) ifa->ifa_addr; - - // This interface is ours - if (ntohl(sa->sin_addr.s_addr) == sender.getIP()) - sender.setIP(0x7f000001); // 127.0.0.1 - } - } - freeifaddrs(ifap); -#else - // Query the list of all IP addresses on the local host - // First call to GetIpAddrTable with 0 bytes buffer - // will return insufficient buffer error, and size - // will contain the number of bytes needed for all - // data. Repeat the process of querying the size - // using GetIpAddrTable in a while loop since it - // can happen that an interface comes online between - // the previous call to GetIpAddrTable and the next - // call. - MIB_IPADDRTABLE *table = NULL; - unsigned long size = 0; - int error = GetIpAddrTable(table, &size, 0); - // Also add a count to limit the while loop - in - // case that something strange is going on. - int count = 0; - while (error == ERROR_INSUFFICIENT_BUFFER && count < 10) - { - delete[] table; // deleting NULL is legal - table = (MIB_IPADDRTABLE*)new char[size]; - error = GetIpAddrTable(table, &size, 0); - count++; - } // while insufficient buffer - for (unsigned int i = 0; i < table->dwNumEntries; i++) - { - unsigned int ip = ntohl(table->table[i].dwAddr); - if (sender.getIP() == ip) // this interface is ours - { - sender.setIP(0x7f000001); // 127.0.0.1 - break; - } - } - delete[] table; -#endif + if (sender.isPublicAddressLAN()) + sender.setIP(0x7f000001); // 127.0.0.1 } m_server_address.copy(sender); m_state = CONNECTING; diff --git a/src/network/protocols/connect_to_server.hpp b/src/network/protocols/connect_to_server.hpp index 1c83af53c..927a570c2 100644 --- a/src/network/protocols/connect_to_server.hpp +++ b/src/network/protocols/connect_to_server.hpp @@ -40,6 +40,7 @@ private: /** State for finite state machine. */ enum { + SET_PUBLIC_ADDRESS, REGISTER_SELF_ADDRESS, GOT_SERVER_ADDRESS, REQUESTING_CONNECTION, diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 78d538171..6db382669 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -83,6 +83,8 @@ ServerLobby::ServerLobby() : LobbyProtocol(NULL) { setHandleDisconnections(true); + m_state = SET_PUBLIC_ADDRESS; + // We use maximum 16bit unsigned limit auto all_k = kart_properties_manager->getAllAvailableKarts(); auto all_t = track_manager->getAllTrackIdentifiers(); @@ -113,11 +115,6 @@ void ServerLobby::setup() m_game_setup = STKHost::get()->setupNewGame(); m_game_setup->setNumLocalPlayers(0); // no local players on a server m_next_player_id.setAtomic(0); - - // In case of LAN we don't need our public address or register with the - // STK server, so we can directly go to the accepting clients state. - m_state = NetworkConfig::get()->isLAN() ? ACCEPTING_CLIENTS - : INIT_WAN; m_selection_enabled = false; Log::info("ServerLobby", "Starting the protocol."); @@ -185,7 +182,6 @@ bool ServerLobby::notifyEventAsynchronous(Event* event) case LE_VOTE_LAPS: playerLapsVote(event); break; case LE_RACE_FINISHED_ACK: playerFinishedResult(event); break; } // switch - } // if (event->getType() == EVENT_TYPE_MESSAGE) else if (event->getType() == EVENT_TYPE_DISCONNECTED) { @@ -195,21 +191,34 @@ bool ServerLobby::notifyEventAsynchronous(Event* event) } // notifyEventAsynchronous //----------------------------------------------------------------------------- -/** Simple finite state machine. First get the public ip address. Once this - * is known, register the server and its address with the stk server so that - * client can find it. - */ -void ServerLobby::update(float dt) +/** Find out the public IP server or poll STK server asynchronously. */ +void ServerLobby::asynchronousUpdate() { switch (m_state.load()) { - case INIT_WAN: + case SET_PUBLIC_ADDRESS: { - // Start the protocol to find the public ip address. - m_state = GETTING_PUBLIC_ADDRESS; + // In case of LAN we don't need our public address or register with the + // STK server, so we can directly go to the accepting clients state. + if (NetworkConfig::get()->isLAN()) + { + m_state = ACCEPTING_CLIENTS; + STKHost::get()->startListening(); + return; + } + STKHost::get()->setPublicAddress(); + if (STKHost::get()->getPublicAddress().isUnset()) + { + m_state = ERROR_LEAVE; + } + else + { + STKHost::get()->startListening(); + m_state = REGISTER_SELF_ADDRESS; + } break; } - case GETTING_PUBLIC_ADDRESS: + case REGISTER_SELF_ADDRESS: { // Register this server with the STK server. This will block // this thread, but there is no need for the protocol manager @@ -221,10 +230,39 @@ void ServerLobby::update(float dt) case ACCEPTING_CLIENTS: { // Only poll the STK server if this is a WAN server. - if(NetworkConfig::get()->isWAN()) + if (NetworkConfig::get()->isWAN()) checkIncomingConnectionRequests(); break; } + case ERROR_LEAVE: + { + requestTerminate(); + m_state = EXITING; + STKHost::get()->setErrorMessage(_("Failed to setup server.")); + STKHost::get()->requestShutdown(); + break; + } + default: + break; + } +} // asynchronousUpdate + +//----------------------------------------------------------------------------- +/** Simple finite state machine. Once this + * is known, register the server and its address with the stk server so that + * client can find it. + */ +void ServerLobby::update(float dt) +{ + switch (m_state.load()) + { + case SET_PUBLIC_ADDRESS: + case REGISTER_SELF_ADDRESS: + case ACCEPTING_CLIENTS: + { + // Waiting for asynchronousUpdate + break; + } case SELECTING: // The function playerTrackVote will trigger the next state // once all track votes have been received. @@ -280,7 +318,8 @@ void ServerLobby::update(float dt) sendMessageToPeersChangingToken(exit_result_screen, /*reliable*/true); delete exit_result_screen; - m_state = ACCEPTING_CLIENTS; + m_state = NetworkConfig::get()->isLAN() ? + ACCEPTING_CLIENTS : REGISTER_SELF_ADDRESS; RaceResultGUI::getInstance()->backToLobby(); // notify the network world that it is stopped RaceEventManager::getInstance()->stop(); @@ -293,10 +332,7 @@ void ServerLobby::update(float dt) setup(); } break; - case DONE: - m_state = EXITING; - requestTerminate(); - break; + case ERROR_LEAVE: case EXITING: break; } @@ -337,14 +373,11 @@ void ServerLobby::registerServer() { irr::core::stringc error(request->getInfo().c_str()); Log::error("ServerLobby", "%s", error.c_str()); - STKHost::get()->setErrorMessage(_("Failed to register server: %s", - error.c_str())); - STKHost::get()->requestShutdown(); + m_state = ERROR_LEAVE; } delete request; } // registerServer - //----------------------------------------------------------------------------- /** Unregister this server (i.e. its public address) with the STK server, * currently when karts enter kart selection screen it will be done. @@ -472,6 +505,12 @@ void ServerLobby::checkIncomingConnectionRequests() if (StkTime::getRealTime() < last_poll_time + POLL_INTERVAL) return; + // Keep the port open, it can be sent to anywhere as we will send to the + // correct peer later in ConnectToPeer. + BareNetworkString data; + data.addUInt8(0); + STKHost::get()->sendRawPacket(data, STKHost::get()->getStunAddress()); + // Now poll the stk server last_poll_time = StkTime::getRealTime(); Online::XMLRequest* request = new Online::XMLRequest(); diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 6448a3cf9..b2ec2e03a 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -14,18 +14,17 @@ public: /* The state for a small finite state machine. */ enum ServerState : unsigned int { - INIT_WAN, // Start state for WAN game - GETTING_PUBLIC_ADDRESS, // Waiting to receive its public ip address + SET_PUBLIC_ADDRESS, // Waiting to receive its public ip address + REGISTER_SELF_ADDRESS, // Register with STK online server ACCEPTING_CLIENTS, // In lobby, accepting clients SELECTING, // kart, track, ... selection started LOAD_WORLD, // Server starts loading world WAIT_FOR_WORLD_LOADED, // Wait for clients and server to load world WAIT_FOR_RACE_STARTED, // Wait for all clients to have started the race - START_RACE, // Inform clients to start race DELAY_SERVER, // Additional server delay RACING, // racing RESULT_DISPLAY, // Show result screen - DONE, // shutting down server + ERROR_LEAVE, // shutting down server EXITING }; private: @@ -92,7 +91,7 @@ public: virtual bool notifyEvent(Event* event) OVERRIDE; virtual void setup() OVERRIDE; virtual void update(float dt) OVERRIDE; - virtual void asynchronousUpdate() OVERRIDE {}; + virtual void asynchronousUpdate() OVERRIDE; void signalRaceStartToClients(); void startSelection(const Event *event=NULL); diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index d3cd29101..c97e82b30 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -261,13 +261,14 @@ STKHost::STKHost(uint32_t server_id, uint32_t host_id) // server is made. m_host_id = 0; init(); - + ENetAddress ea; ea.host = STKHost::HOST_ANY; - ea.port = STKHost::PORT_ANY; + ea.port = NetworkConfig::get()->getClientPort(); m_network = new Network(/*peer_count*/1, /*channel_limit*/2, - /*max_in_bandwidth*/0, /*max_out_bandwidth*/0, &ea); + /*max_in_bandwidth*/0, /*max_out_bandwidth*/0, + &ea, true/*change_port_if_bound*/); if (!m_network) { Log::fatal ("STKHost", "An error occurred while trying to create " @@ -275,15 +276,7 @@ STKHost::STKHost(uint32_t server_id, uint32_t host_id) } setPrivatePort(); - if (NetworkConfig::get()->isWAN()) - { - setPublicAddress(); - } - // Don't connect to server if no public address in WAN game - if (!m_public_address.isUnset() || NetworkConfig::get()->isLAN()) - { - std::make_shared(server_id, host_id)->requestStart(); - } + std::make_shared(server_id, host_id)->requestStart(); } // STKHost // ---------------------------------------------------------------------------- @@ -300,12 +293,13 @@ STKHost::STKHost(const irr::core::stringw &server_name) ENetAddress addr; addr.host = STKHost::HOST_ANY; - addr.port = STKHost::PORT_ANY; + addr.port = NetworkConfig::get()->getServerPort(); m_network= new Network(NetworkConfig::get()->getMaxPlayers(), /*channel_limit*/2, /*max_in_bandwidth*/0, - /*max_out_bandwidth*/ 0, &addr); + /*max_out_bandwidth*/ 0, &addr, + true/*change_port_if_bound*/); if (!m_network) { Log::fatal("STKHost", "An error occurred while trying to create an " @@ -313,16 +307,8 @@ STKHost::STKHost(const irr::core::stringw &server_name) } setPrivatePort(); - // We need the public address for server no matter what to determine - // local lan connection - setPublicAddress(); - // Don't construct server if no public address in WAN game - if (!m_public_address.isUnset() || NetworkConfig::get()->isLAN()) - { - startListening(); - ProtocolManager::lock() - ->requestStart(LobbyProtocol::create()); - } + ProtocolManager::lock() + ->requestStart(LobbyProtocol::create()); } // STKHost(server_name) @@ -335,7 +321,6 @@ void STKHost::init() m_shutdown = false; m_network = NULL; m_game_setup = NULL; - m_error_message = ""; m_exit_flag.clear(); m_exit_flag.test_and_set(); @@ -435,17 +420,19 @@ void STKHost::setPublicAddress() // documentation says it points to "one or more addrinfo structures" assert(res != NULL); struct sockaddr_in* current_interface = (struct sockaddr_in*)(res->ai_addr); - uint32_t stun_server_ip = ntohl(current_interface->sin_addr.s_addr); + m_stun_address.setIP(ntohl(current_interface->sin_addr.s_addr)); + m_stun_address.setPort(3478); // Assemble the message for the stun server BareNetworkString s(20); + constexpr uint32_t magic_cookie = 0x2112A442; // bytes 0-1: the type of the message // bytes 2-3: message length added to header (attributes) uint16_t message_type = 0x0001; // binding request uint16_t message_length = 0x0000; s.addUInt16(message_type).addUInt16(message_length) - .addUInt32(0x2112A442); + .addUInt32(magic_cookie); uint8_t stun_tansaction_id[12]; // bytes 8-19: the transaction id for (int i = 0; i < 12; i++) @@ -455,7 +442,7 @@ void STKHost::setPublicAddress() stun_tansaction_id[i] = random_byte; } - m_network->sendRawPacket(s, TransportAddress(stun_server_ip, 3478)); + m_network->sendRawPacket(s, m_stun_address); freeaddrinfo(res); // Recieve now @@ -464,33 +451,37 @@ void STKHost::setPublicAddress() char buffer[LEN]; int len = m_network->receiveRawPacket(buffer, LEN, &sender, 2000); - if (sender.getIP() != stun_server_ip) + if (sender.getIP() != m_stun_address.getIP()) { - TransportAddress stun(stun_server_ip, 3478); Log::warn("STKHost", - "Received stun response from %s instead of %s.", - sender.toString().c_str(), stun.toString().c_str()); + "Received stun response from %s instead of %s.", + sender.toString().c_str(), m_stun_address.toString().c_str()); } - if (len < 0) + if (len <= 0) { Log::error("STKHost", "STUN response contains no data at all"); continue; } // Convert to network string. - BareNetworkString datas(buffer, len); - - // check that the stun response is a response, contains the magic cookie - // and the transaction ID - if (datas.getUInt16() != 0x0101) + BareNetworkString response(buffer, len); + if (response.size() < 20) { - Log::error("STKHost", "STUN response doesn't contain the magic " - "cookie"); + Log::error("STKHost", "STUN response should be at least 20 bytes."); continue; } - int message_size = datas.getUInt16(); - if (datas.getUInt32() != 0x2112A442) + + if (response.getUInt16() != 0x0101) + { + Log::error("STKHost", "STUN has no binding success response."); + continue; + } + + // Skip message size + response.getUInt16(); + + if (response.getUInt32() != magic_cookie) { Log::error("STKHost", "STUN response doesn't contain the magic " "cookie"); @@ -499,7 +490,7 @@ void STKHost::setPublicAddress() for (int i = 0; i < 12; i++) { - if (datas.getUInt8() != stun_tansaction_id[i]) + if (response.getUInt8() != stun_tansaction_id[i]) { Log::error("STKHost", "STUN response doesn't contain the " "transaction ID"); @@ -511,64 +502,77 @@ void STKHost::setPublicAddress() "The STUN server responded with a valid answer"); // The stun message is valid, so we parse it now: - if (message_size == 0) - { - Log::error("STKHost", "STUN response does not contain any " - "information."); - continue; - } - // Cannot even read the size - if (message_size < 4) - { - Log::error("STKHost", "STUN response is too short."); - continue; - } // Those are the port and the address to be detected bool found = false; while (true) { - int type = datas.getUInt16(); - int size = datas.getUInt16(); - if (type == 0 || type == 1) - { - assert(size == 8); - datas.getUInt8(); // skip 1 byte -#ifdef DEBUG - uint8_t skip = datas.getUInt8(); - // Family IPv4 only - assert(skip == 0x01); -#else - datas.getUInt8(); -#endif - m_public_address.setPort(datas.getUInt16()); - m_public_address.setIP(datas.getUInt32()); - // finished parsing, we know our public transport address - Log::debug("STKHost", "The public address has been found: %s", - m_public_address.toString().c_str()); - found = true; - break; - } // type = 0 or 1 - datas.skip(4 + size); - message_size -= 4 + size; - if (message_size == 0) + if (response.size() < 4) { Log::error("STKHost", "STUN response is invalid."); break; } - // Cannot even read the size - if (message_size < 4) + unsigned type = response.getUInt16(); + unsigned size = response.getUInt16(); + + // Bit determining whether comprehension of an attribute is optional. + // Described in section 15 of RFC 5389. + constexpr uint16_t comprehension_optional = 0x1 << 15; + + // Bit determining whether the bit was assigned by IETF Review. + // Described in section 18.1. of RFC 5389. + constexpr uint16_t IETF_review = 0x1 << 14; + + // Defined in section 15.1 of RFC 5389 + constexpr uint8_t ipv4 = 0x01; + + // Defined in section 18.2 of RFC 5389 + constexpr uint16_t mapped_address = 0x001; + constexpr uint16_t xor_mapped_address = 0x0020; + // The first two bits are irrelevant to the type + type &= ~(comprehension_optional | IETF_review); + if (type == mapped_address || type == xor_mapped_address) { - Log::error("STKHost", "STUN response is invalid."); + if (size != 8 || response.size() < 8) + { + Log::error("STKHost", "Invalid STUN mapped address " + "length"); + break; + } + // Ignore the first byte as mentioned in Section 15.1 of RFC + // 5389. + uint8_t ip_type = response.getUInt8(); + ip_type = response.getUInt8(); + if (ip_type != ipv4) + { + Log::error("STKHost", "Only IPv4 is supported"); + break; + } + + uint16_t port = response.getUInt16(); + uint32_t ip = response.getUInt32(); + if (type == xor_mapped_address) + { + // Obfuscation is described in Section 15.2 of RFC 5389. + port ^= magic_cookie >> 16; + ip ^= magic_cookie; + } + m_public_address.setPort(port); + m_public_address.setIP(ip); + found = true; break; + } // type == mapped_address || type == xor_mapped_address + else + { + response.skip(size); + int padding = size % 4; + if (padding != 0) + response.skip(4 - padding); } } // while true // Found public address and port if (found) untried_server.clear(); } - // We shutdown next frame if no public address - if (m_public_address.isUnset()) - requestShutdown(); } // setPublicAddress //----------------------------------------------------------------------------- @@ -629,18 +633,14 @@ void STKHost::abort() */ void STKHost::setErrorMessage(const irr::core::stringw &message) { - irr::core::stringc s(message.c_str()); - Log::error("STKHost", "%s", s.c_str()); + if (!message.empty()) + { + irr::core::stringc s(message.c_str()); + Log::error("STKHost", "%s", s.c_str()); + } m_error_message = message; } // setErrorMessage -// -------------------------------------------------------------------- -/** Returns the last error (or "" if no error has happened). */ -const irr::core::stringw& STKHost::getErrorMessage() const -{ - return m_error_message; -} // getErrorMessage - // -------------------------------------------------------------------- /** \brief Try to establish a connection to a given transport address. * \param peer : The transport address which you want to connect to. @@ -831,7 +831,7 @@ void STKHost::handleDirectSocketRequest(Network* lan_network) { // In case of a LAN connection, we only allow connections from // a LAN address (192.168*, ..., and 127.*). - if (!sender.isLAN() && sender.getIP() != m_public_address.getIP()) + if (!sender.isLAN() && !sender.isPublicAddressLAN()) { Log::error("STKHost", "Client trying to connect from '%s'", sender.toString().c_str()); diff --git a/src/network/stk_host.hpp b/src/network/stk_host.hpp index fbaa118bc..421afe97a 100644 --- a/src/network/stk_host.hpp +++ b/src/network/stk_host.hpp @@ -101,6 +101,9 @@ private: /** The public address found by stun (if WAN is used). */ TransportAddress m_public_address; + /** The public address stun server used. */ + TransportAddress m_stun_address; + /** The private port enet socket is bound. */ uint16_t m_private_port; @@ -113,8 +116,6 @@ private: void init(); void handleDirectSocketRequest(Network* lan_network); // ------------------------------------------------------------------------ - void setPublicAddress(); - // ------------------------------------------------------------------------ void mainLoop(); public: @@ -149,11 +150,16 @@ public: const TransportAddress& getPublicAddress() const { return m_public_address; } // ------------------------------------------------------------------------ + const TransportAddress& getStunAddress() const + { return m_stun_address; } + // ------------------------------------------------------------------------ uint16_t getPrivatePort() const { return m_private_port; } // ------------------------------------------------------------------------ void setPrivatePort(); // ------------------------------------------------------------------------ + void setPublicAddress(); + // ------------------------------------------------------------------------ virtual GameSetup* setupNewGame(); void abort(); void deleteAllPeers(); @@ -190,9 +196,11 @@ public: std::vector getMyPlayerProfiles(); void setErrorMessage(const irr::core::stringw &message); bool isAuthorisedToControl() const; - const irr::core::stringw& - getErrorMessage() const; + // -------------------------------------------------------------------- + /** Returns the last error (or "" if no error has happened). */ + const irr::core::stringw& getErrorMessage() const + { return m_error_message; } // -------------------------------------------------------------------- /** Returns true if a shutdown of the network infrastructure was * requested. */ @@ -214,15 +222,6 @@ public: { m_network->sendRawPacket(buffer, dst); } // sendRawPacket - - // -------------------------------------------------------------------- - /** Returns the IP address of this host. */ - uint32_t getAddress() const - { - return m_network->getENetHost()->address.host; - } // getAddress - - // -------------------------------------------------------------------- /** Returns a const reference to the list of peers. */ const std::vector &getPeers() { return m_peers; } diff --git a/src/network/transport_address.cpp b/src/network/transport_address.cpp index 1a9c3cdd0..51909dccf 100644 --- a/src/network/transport_address.cpp +++ b/src/network/transport_address.cpp @@ -18,8 +18,15 @@ #include "network/transport_address.hpp" +#ifdef WIN32 +# include +#else +#include +#endif + +// ---------------------------------------------------------------------------- /** Returns if this IP address belongs to a LAN, i.e. is in 192.168* or - * 10*, 172.16-31.*, or is on the same host, i.e. 127*. + * 10*, 172.16-31.*, or is on the same host, i.e. 127* or same public address. */ bool TransportAddress::isLAN() const { @@ -33,6 +40,60 @@ bool TransportAddress::isLAN() const else if (ip >> 24 == 0x7f ) // 127.* localhost return true; return false; +} + +// ---------------------------------------------------------------------------- +/** Returns this IP address is localhost (127.0.0.1). + */ +bool TransportAddress::isPublicAddressLAN() const +{ +#ifndef WIN32 + struct ifaddrs *ifap, *ifa; + struct sockaddr_in *sa; + getifaddrs(&ifap); // get the info + for (ifa = ifap; ifa; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr->sa_family == AF_INET) + { + sa = (struct sockaddr_in *) ifa->ifa_addr; + // This interface is ours + if (ntohl(sa->sin_addr.s_addr) == getIP()) + return true; + } + } + freeifaddrs(ifap); +#else + // Query the list of all IP addresses on the local host. First call to + // GetIpAddrTable with 0 bytes buffer will return insufficient buffer + // error, and size will contain the number of bytes needed for all data. + // Repeat the process of querying the size using GetIpAddrTable in a while + // loop since it can happen that an interface comes online between the + // previous call to GetIpAddrTable and the next call. + MIB_IPADDRTABLE *table = NULL; + unsigned long size = 0; + int error = GetIpAddrTable(table, &size, 0); + // Also add a count to limit the while loop - in case that something + // strange is going on. + int count = 0; + while (error == ERROR_INSUFFICIENT_BUFFER && count < 10) + { + delete[] table; // deleting NULL is legal + table = (MIB_IPADDRTABLE*)new char[size]; + error = GetIpAddrTable(table, &size, 0); + count++; + } // while insufficient buffer + for (unsigned int i = 0; i < table->dwNumEntries; i++) + { + unsigned int ip = ntohl(table->table[i].dwAddr); + if (getIP() == ip) // this interface is ours + { + delete[] table; + return true; + } + } + delete[] table; +#endif + return false; } // isLAN // ---------------------------------------------------------------------------- @@ -42,71 +103,71 @@ bool TransportAddress::isLAN() const void TransportAddress::unitTesting() { TransportAddress t1("192.168.0.0"); - assert(t1.getIP() == (192 << 24) + (168 << 16)); + assert(t1.getIP() == (192u << 24) + (168u << 16)); assert(t1.isLAN()); TransportAddress t2("192.168.255.255"); - assert(t2.getIP() == (192 << 24) + (168 << 16) + (255 << 8) + 255); + assert(t2.getIP() == (192u << 24) + (168u << 16) + (255u << 8) + 255u); assert(t2.isLAN()); TransportAddress t3("193.168.0.1"); - assert(t3.getIP() == (193 << 24) + (168 << 16) + 1); + assert(t3.getIP() == (193u << 24) + (168u << 16) + 1); assert(!t3.isLAN()); TransportAddress t4("192.167.255.255"); - assert(t4.getIP() == (192 << 24) + (167 << 16) + (255 << 8) + 255); + assert(t4.getIP() == (192u << 24) + (167u << 16) + (255u << 8) + 255u); assert(!t4.isLAN()); TransportAddress t5("192.169.0.0"); - assert(t5.getIP() == (192 << 24) + (169 << 16)); + assert(t5.getIP() == (192u << 24) + (169u << 16)); assert(!t5.isLAN()); TransportAddress t6("172.16.0.0"); - assert(t6.getIP() == (172 << 24) + (16 << 16)); + assert(t6.getIP() == (172u << 24) + (16u << 16)); assert(t6.isLAN()); TransportAddress t7("172.31.255.255"); - assert(t7.getIP() == (172 << 24) + (31 << 16) + (255 << 8) + 255); + assert(t7.getIP() == (172u << 24) + (31u << 16) + (255u << 8) + 255u); assert(t7.isLAN()); TransportAddress t8("172.15.255.255"); - assert(t8.getIP() == (172 << 24) + (15 << 16) + (255 << 8) + 255); + assert(t8.getIP() == (172u << 24) + (15u << 16) + (255u << 8) + 255u); assert(!t8.isLAN()); TransportAddress t9("172.32.0.0"); - assert(t9.getIP() == (172 << 24) + (32 << 16)); + assert(t9.getIP() == (172u << 24) + (32u << 16)); assert(!t9.isLAN()); TransportAddress t10("10.0.0.0"); - assert(t10.getIP() == (10 << 24)); + assert(t10.getIP() == (10u << 24)); assert(t10.isLAN()); TransportAddress t11("10.255.255.255"); - assert(t11.getIP() == (10 << 24) + (255 << 16) + (255 << 8) + 255); + assert(t11.getIP() == (10u << 24) + (255u << 16) + (255u << 8) + 255u); assert(t11.isLAN()); TransportAddress t12("9.255.255.255"); - assert(t12.getIP() == (9 << 24) + (255 << 16) + (255 << 8) + 255); + assert(t12.getIP() == (9u << 24) + (255u << 16) + (255u << 8) + 255u); assert(!t12.isLAN()); TransportAddress t13("11.0.0.0"); - assert(t13.getIP() == (11 << 24) ); + assert(t13.getIP() == (11u << 24)); assert(!t13.isLAN()); TransportAddress t14("127.0.0.0"); - assert(t14.getIP() == (127 << 24)); + assert(t14.getIP() == (127u << 24)); assert(t14.isLAN()); TransportAddress t15("127.255.255.255"); - assert(t15.getIP() == (127 << 24) + (255 << 16) + (255 << 8) + 255); + assert(t15.getIP() == (127u << 24) + (255u << 16) + (255u << 8) + 255u); assert(t15.isLAN()); TransportAddress t16("126.255.255.255"); - assert(t16.getIP() == (126 << 24) + (255 << 16) + (255 << 8) + 255); + assert(t16.getIP() == (126u << 24) + (255u << 16) + (255u << 8) + 255u); assert(!t16.isLAN()); TransportAddress t17("128.0.0.0"); - assert(t17.getIP() == (128 << 24)); + assert(t17.getIP() == (128u << 24)); assert(!t17.isLAN()); -} // unitTesting \ No newline at end of file +} // unitTesting diff --git a/src/network/transport_address.hpp b/src/network/transport_address.hpp index 3a5cab8bd..3ca3c0379 100644 --- a/src/network/transport_address.hpp +++ b/src/network/transport_address.hpp @@ -88,6 +88,9 @@ private: copy(other); } // TransportAddress(const TransportAddress&) public: + // ------------------------------------------------------------------------ + bool isPublicAddressLAN() const; + // ------------------------------------------------------------------------ bool isLAN() const; // ------------------------------------------------------------------------ bool isUnset() const { return m_ip == 0 || m_port == 0; } diff --git a/src/states_screens/create_server_screen.cpp b/src/states_screens/create_server_screen.cpp index bd8157aac..2d268a14d 100644 --- a/src/states_screens/create_server_screen.cpp +++ b/src/states_screens/create_server_screen.cpp @@ -136,27 +136,6 @@ void CreateServerScreen::onUpdate(float delta) if(!STKHost::existHost()) return; - // First check if an error happened while registering the server: - // -------------------------------------------------------------- - const irr::core::stringw &error = STKHost::get()->getErrorMessage(); - if(error!="") - { - SFXManager::get()->quickSound("anvil"); - m_info_widget->setErrorColor(); - m_info_widget->setText(error, false); - return; - } - - // Otherwise wait till we get an answer from the server: - // ----------------------------------------------------- - if (!LobbyProtocol::get()) - { - m_info_widget->setDefaultColor(); - m_info_widget->setText(StringUtils::loadingDots(_("Creating server")), - false); - return; - } - //FIXME If we really want a gui, we need to decide what else to do here // For now start the (wrong i.e. client) lobby, to prevent to create // a server more than once. From 2a117d8e44747e4dbbaa10729678b55013e5933a Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 25 Feb 2018 02:07:24 +0800 Subject: [PATCH 143/413] Don't lock in async update in protocol manager It allows GUI interacts with protocol more actively Also don't handle lan connection request if we are not waiting for players --- src/network/protocol_manager.cpp | 7 +++-- src/network/protocols/client_lobby.hpp | 2 ++ src/network/protocols/connect_to_server.cpp | 29 ++++++++++++++++----- src/network/protocols/connect_to_server.hpp | 8 +++--- src/network/protocols/lobby_protocol.hpp | 1 + src/network/protocols/server_lobby.hpp | 2 ++ src/network/stk_host.cpp | 3 ++- 7 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/network/protocol_manager.cpp b/src/network/protocol_manager.cpp index af7736e33..f6aeb9601 100644 --- a/src/network/protocol_manager.cpp +++ b/src/network/protocol_manager.cpp @@ -505,9 +505,12 @@ void ProtocolManager::asynchronousUpdate() // The lock is likely not necessary, since this function is only // called from the ProtocolManager thread, and this thread is also // the only one who changes the number of protocols. - opt.lock(); + // Edit: remove this lock can avoid hanging the GUI when connecting + // to or creating server, but you need to make sure async and non-async + // update in each protocol will have atomic or mutex write + //opt.lock(); opt.update(0, /*async*/true); // dt does not matter, so set it to 0 - opt.unlock(); + //opt.unlock(); } PROFILER_POP_CPU_MARKER(); diff --git a/src/network/protocols/client_lobby.hpp b/src/network/protocols/client_lobby.hpp index bf2ab7366..bd7b5525a 100644 --- a/src/network/protocols/client_lobby.hpp +++ b/src/network/protocols/client_lobby.hpp @@ -81,6 +81,8 @@ public: virtual void finishedLoadingWorld() OVERRIDE; virtual void setup() OVERRIDE; virtual void update(float dt) OVERRIDE; + virtual bool waitingForPlayers() const OVERRIDE + { return m_state == LINKED; } virtual void asynchronousUpdate() OVERRIDE {} }; diff --git a/src/network/protocols/connect_to_server.cpp b/src/network/protocols/connect_to_server.cpp index 65a0af5ea..c06e766da 100644 --- a/src/network/protocols/connect_to_server.cpp +++ b/src/network/protocols/connect_to_server.cpp @@ -84,7 +84,7 @@ void ConnectToServer::setup() // ---------------------------------------------------------------------------- void ConnectToServer::asynchronousUpdate() { - switch(m_state) + switch(m_state.load()) { case SET_PUBLIC_ADDRESS: { @@ -210,19 +210,31 @@ void ConnectToServer::asynchronousUpdate() return; } m_state = DONE; + break; + case DONE: + case EXITING: + break; + } +} // asynchronousUpdate + +// ---------------------------------------------------------------------------- +void ConnectToServer::update(float dt) +{ + switch(m_state.load()) + { + case DONE: + { // lobby room protocol if we're connected only if (STKHost::get()->getPeerCount() > 0 && STKHost::get()->getPeers()[0]->isConnected() && !m_server_address.isUnset()) { + // Let main thread create ClientLobby for better + // synchronization with GUI auto cl = LobbyProtocol::create(); cl->setAddress(m_server_address); cl->requestStart(); } - break; - case DONE: - requestTerminate(); - m_state = EXITING; if (STKHost::get()->getPeerCount() == 0) { // Shutdown STKHost (go back to online menu too) @@ -231,11 +243,14 @@ void ConnectToServer::asynchronousUpdate() m_server_address.toString().c_str())); STKHost::get()->requestShutdown(); } + requestTerminate(); + m_state = EXITING; break; - case EXITING: + } + default: break; } -} // asynchronousUpdate +} // update // ---------------------------------------------------------------------------- /** Register this client with the STK server. diff --git a/src/network/protocols/connect_to_server.hpp b/src/network/protocols/connect_to_server.hpp index 927a570c2..0a8c41dcd 100644 --- a/src/network/protocols/connect_to_server.hpp +++ b/src/network/protocols/connect_to_server.hpp @@ -22,6 +22,7 @@ #include "network/protocol.hpp" #include "network/transport_address.hpp" #include "utils/cpp2011.hpp" +#include #include class ConnectToServer : public Protocol @@ -38,7 +39,7 @@ private: bool m_quick_join; /** State for finite state machine. */ - enum + enum ConnectState : unsigned int { SET_PUBLIC_ADDRESS, REGISTER_SELF_ADDRESS, @@ -49,7 +50,8 @@ private: HIDING_ADDRESS, DONE, EXITING - } m_state; + }; + std::atomic m_state; void registerWithSTKServer(); void handleQuickConnect(); @@ -63,7 +65,7 @@ public: virtual bool notifyEventAsynchronous(Event* event) OVERRIDE; virtual void setup() OVERRIDE; virtual void asynchronousUpdate() OVERRIDE; - virtual void update(float dt) OVERRIDE {} + virtual void update(float dt) OVERRIDE; }; // class ConnectToServer #endif // CONNECT_TO_SERVER_HPP diff --git a/src/network/protocols/lobby_protocol.hpp b/src/network/protocols/lobby_protocol.hpp index 52f31bf27..ac5020e04 100644 --- a/src/network/protocols/lobby_protocol.hpp +++ b/src/network/protocols/lobby_protocol.hpp @@ -100,6 +100,7 @@ public: virtual void update(float dt) = 0; virtual void finishedLoadingWorld() = 0; virtual void loadWorld(); + virtual bool waitingForPlayers() const = 0; void terminateLatencyProtocol(); virtual void requestKartSelection(uint8_t player_id, const std::string &kart_name) diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index b2ec2e03a..a25e0a3f0 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -99,6 +99,8 @@ public: void checkRaceFinished(); void finishedLoadingWorld(); ServerState getCurrentState() const { return m_state.load(); } + virtual bool waitingForPlayers() const OVERRIDE + { return m_state.load() == ACCEPTING_CLIENTS; } }; // class ServerLobby diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index c97e82b30..8c4123620 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -727,7 +727,8 @@ void STKHost::mainLoop() while (m_exit_flag.test_and_set()) { - if (lan_network) + auto sl = LobbyProtocol::get(); + if (lan_network && sl && sl->waitingForPlayers()) { handleDirectSocketRequest(lan_network); } // if discovery host From 34caab24fefbd769d68b958043734fe3f2bd6cb1 Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 25 Feb 2018 02:09:07 +0800 Subject: [PATCH 144/413] Update networking lobbdy for better display info --- data/gui/online/networking_lobby.stkgui | 60 ++++++--------------- src/states_screens/networking_lobby.cpp | 71 +++++++++++++++---------- src/states_screens/networking_lobby.hpp | 21 ++++---- 3 files changed, 71 insertions(+), 81 deletions(-) diff --git a/data/gui/online/networking_lobby.stkgui b/data/gui/online/networking_lobby.stkgui index 19cb03f5d..5bacbe40d 100644 --- a/data/gui/online/networking_lobby.stkgui +++ b/data/gui/online/networking_lobby.stkgui @@ -3,58 +3,30 @@
-
+
- -
-
-
-
-
-
-
+
- - - -
- - - - - - - - - -
- - - - -
- diff --git a/src/states_screens/networking_lobby.cpp b/src/states_screens/networking_lobby.cpp index 62d751d9f..6559f43a3 100644 --- a/src/states_screens/networking_lobby.cpp +++ b/src/states_screens/networking_lobby.cpp @@ -24,12 +24,11 @@ #include "challenges/unlock_manager.hpp" #include "config/player_manager.hpp" -#include "graphics/irr_driver.hpp" -#include "guiengine/scalable_font.hpp" +#include "guiengine/widgets/bubble_widget.hpp" #include "guiengine/widgets/icon_button_widget.hpp" -#include "guiengine/widgets/label_widget.hpp" #include "guiengine/widgets/list_widget.hpp" #include "guiengine/widgets/ribbon_widget.hpp" +#include "guiengine/widgets/text_box_widget.hpp" #include "input/device_manager.hpp" #include "input/input_manager.hpp" #include "io/file_manager.hpp" @@ -61,7 +60,6 @@ DEFINE_SCREEN_SINGLETON( NetworkingLobby ); // ---------------------------------------------------------------------------- NetworkingLobby::NetworkingLobby() : Screen("online/networking_lobby.stkgui") { - m_server = NULL; m_player_list = NULL; } // NetworkingLobby @@ -75,26 +73,16 @@ void NetworkingLobby::loadedFromFile() m_start_button= getWidget("start"); assert(m_start_button!= NULL); - m_server_name_widget = getWidget("server_name"); - assert(m_server_name_widget != NULL); + m_text_bubble = getWidget("text"); + assert(m_text_bubble != NULL); - m_server_difficulty = getWidget("server_difficulty"); - assert(m_server_difficulty != NULL); - - m_server_game_mode = getWidget("server_game_mode"); - assert(m_server_game_mode != NULL); - - m_online_status_widget = getWidget("online_status"); - assert(m_online_status_widget != NULL); - - m_bottom_menu_widget = getWidget("menu_bottomrow"); - assert(m_bottom_menu_widget != NULL); + m_chat_box = getWidget("chat"); + assert(m_chat_box != NULL); m_player_list = getWidget("players"); assert(m_player_list!= NULL); - m_exit_widget = (IconButtonWidget *) m_bottom_menu_widget - ->findWidgetNamed("exit"); + m_exit_widget = getWidget("exit");; assert(m_exit_widget != NULL); } // loadedFromFile @@ -112,18 +100,25 @@ void NetworkingLobby::beforeAddingWidget() */ void NetworkingLobby::init() { + m_server_info.clear(); Screen::init(); setInitialFocus(); - m_server = ServersManager::get()->getJoinedServer(); - if (m_server) + Server* server = ServersManager::get()->getJoinedServer(); + if (server) { - m_server_name_widget->setText(m_server->getName(), false); + m_server_name = server->getName(); + core::stringw each_line; + each_line = _("Server name: %s", m_server_name); + m_server_info.push_back(each_line); - core::stringw difficulty = race_manager->getDifficultyName(m_server->getDifficulty()); - m_server_difficulty->setText(difficulty, false); + const core::stringw& difficulty_name = + race_manager->getDifficultyName(race_manager->getDifficulty()); + each_line = _("Difficulty: %s", difficulty_name); + m_server_info.push_back(each_line); - core::stringw mode = RaceManager::getNameOf(m_server->getRaceMinorMode()); - m_server_game_mode->setText(mode, false); + core::stringw mode = RaceManager::getNameOf(server->getRaceMinorMode()); + each_line = _("Game mode: %s", mode); + m_server_info.push_back(each_line); } if(!NetworkConfig::get()->isServer()) @@ -136,15 +131,37 @@ void NetworkingLobby::init() StateManager::get()->createActivePlayer(profile, device); } // init +// ---------------------------------------------------------------------------- +void NetworkingLobby::addMoreServerInfo(const core::stringw& info) +{ +} // addMoreServerInfo + // ---------------------------------------------------------------------------- void NetworkingLobby::onUpdate(float delta) { - // FIXME Network looby should be closed when stkhost is shut down + auto lp = LobbyProtocol::get(); + if (!lp) + { + const core::stringw connect_msg = StringUtils::loadingDots( + _("Connecting to server %s", m_server_name)); + m_text_bubble->setText(connect_msg); + } + else + { + core::stringw total_msg; + for (auto& string : m_server_info) + { + total_msg += string; + total_msg += L"\n"; + } + m_text_bubble->setText(total_msg); + } if(NetworkConfig::get()->isClient()) { m_start_button->setVisible(STKHost::existHost() && STKHost::get()->isAuthorisedToControl()); } + } // onUpdate // ---------------------------------------------------------------------------- diff --git a/src/states_screens/networking_lobby.hpp b/src/states_screens/networking_lobby.hpp index d9801f3ac..c95daae32 100644 --- a/src/states_screens/networking_lobby.hpp +++ b/src/states_screens/networking_lobby.hpp @@ -22,12 +22,13 @@ class Server; -namespace GUIEngine { +namespace GUIEngine +{ class Widget; class ListWidget; class IconButtonWidget; - class LabelWidget; - class RibbonWidget; + class BubbleWidget; + class TextBoxWidget; } class NetworkPlayerProfile; @@ -42,19 +43,17 @@ class NetworkingLobby : public GUIEngine::Screen, private: friend class GUIEngine::ScreenSingleton; - Server * m_server; - NetworkingLobby(); + core::stringw m_server_name; + std::vector m_server_info; + GUIEngine::IconButtonWidget * m_back_widget; - GUIEngine::LabelWidget * m_server_name_widget; - GUIEngine::LabelWidget * m_online_status_widget; - GUIEngine::RibbonWidget * m_bottom_menu_widget; + GUIEngine::BubbleWidget * m_text_bubble; GUIEngine::IconButtonWidget * m_exit_widget; GUIEngine::IconButtonWidget *m_start_button; GUIEngine::ListWidget *m_player_list; - GUIEngine::LabelWidget* m_server_difficulty; - GUIEngine::LabelWidget* m_server_game_mode; + GUIEngine::TextBoxWidget* m_chat_box; /** \brief Sets which widget has to be focused. Depends on the user state. */ void setInitialFocus(); @@ -88,6 +87,8 @@ public: /** \brief Implements the callback when a dialog gets closed. */ virtual void onDialogClose() OVERRIDE; + /** Used to insert each client chat message (reserved). */ + void addMoreServerInfo(const core::stringw& info); void addPlayer(NetworkPlayerProfile *profile); void removePlayer(NetworkPlayerProfile *profile); }; // class NetworkingLobby From 8d565fba110ea93bec50c3a00cf4ed5b4972dd07 Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Sat, 24 Feb 2018 21:38:31 -0500 Subject: [PATCH 145/413] Unicode fix --- src/online/http_request.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/online/http_request.hpp b/src/online/http_request.hpp index 70a5b5d60..bba850c51 100644 --- a/src/online/http_request.hpp +++ b/src/online/http_request.hpp @@ -133,7 +133,7 @@ namespace Online void addParameter(const std::string &name, const irr::core::stringw &value) { - core::stringc s = core::stringc(value.c_str()); + std::string s = StringUtils::wideToUtf8(value); // Call the template to escape strings properly addParameter(name, s.c_str()); From 7b903a09ba3bfc9061661869be056671d353aa2c Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Sat, 24 Feb 2018 21:49:49 -0500 Subject: [PATCH 146/413] Add text_valign="top" support to GUI engine --- src/guiengine/engine.cpp | 7 ++++--- src/guiengine/screen_loader.cpp | 1 + src/guiengine/widget.hpp | 1 + src/guiengine/widgets/bubble_widget.cpp | 4 +++- src/guiengine/widgets/label_widget.cpp | 5 ++++- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/guiengine/engine.cpp b/src/guiengine/engine.cpp index 87e5c0d06..6c996f1a5 100644 --- a/src/guiengine/engine.cpp +++ b/src/guiengine/engine.cpp @@ -309,11 +309,12 @@ namespace GUIEngine For icon buttons. A different icon to show when the item is focused. \n - \subsection prop4 PROP_TEXT_ALIGN - Name in XML files: \c "text_align" + \subsection prop4 PROP_TEXT_ALIGN, PROP_TEXT_VALIGN + Name in XML files: \c "text_align", "text_valign" used exclusively by label components. Value can be "right" or "center" (left - used if not specified). + used if not specified) for "text_align", or "top"/"center"/"bottom" for + valign. \n \subsection prop5 PROP_WORD_WRAP diff --git a/src/guiengine/screen_loader.cpp b/src/guiengine/screen_loader.cpp index 373cbed45..9139f0e76 100644 --- a/src/guiengine/screen_loader.cpp +++ b/src/guiengine/screen_loader.cpp @@ -210,6 +210,7 @@ if(prop_name != NULL) widget.m_properties[prop_flag] = core::stringc(prop_name). READ_PROPERTY(icon, PROP_ICON); READ_PROPERTY(focus_icon, PROP_FOCUS_ICON); READ_PROPERTY(text_align, PROP_TEXT_ALIGN); + READ_PROPERTY(text_valign, PROP_TEXT_VALIGN); READ_PROPERTY(min_value, PROP_MIN_VALUE); READ_PROPERTY(max_value, PROP_MAX_VALUE); READ_PROPERTY(square_items, PROP_SQUARE); diff --git a/src/guiengine/widget.hpp b/src/guiengine/widget.hpp index 16fab6c04..3eefcb724 100644 --- a/src/guiengine/widget.hpp +++ b/src/guiengine/widget.hpp @@ -98,6 +98,7 @@ namespace GUIEngine PROP_ICON, PROP_FOCUS_ICON, PROP_TEXT_ALIGN, + PROP_TEXT_VALIGN, PROP_MIN_VALUE, PROP_MAX_VALUE, PROP_MAX_WIDTH, diff --git a/src/guiengine/widgets/bubble_widget.cpp b/src/guiengine/widgets/bubble_widget.cpp index 0e51478d4..79ac9865b 100644 --- a/src/guiengine/widgets/bubble_widget.cpp +++ b/src/guiengine/widgets/bubble_widget.cpp @@ -76,7 +76,9 @@ void BubbleWidget::replaceText() else if (m_properties[PROP_TEXT_ALIGN] == "right") align = EGUIA_LOWERRIGHT; else if (translations->isRTLText(message)) align = EGUIA_LOWERRIGHT; - EGUI_ALIGNMENT valign = EGUIA_CENTER ; //TODO: make label v-align configurable through XML file? + EGUI_ALIGNMENT valign = EGUIA_CENTER; + if (m_properties[PROP_TEXT_VALIGN] == "top") valign = EGUIA_UPPERLEFT; + if (m_properties[PROP_TEXT_VALIGN] == "bottom") valign = EGUIA_LOWERRIGHT; // find expanded bubble size int text_height = irrwidget->getTextHeight(); diff --git a/src/guiengine/widgets/label_widget.cpp b/src/guiengine/widgets/label_widget.cpp index 3f8e25ae1..a48961408 100644 --- a/src/guiengine/widgets/label_widget.cpp +++ b/src/guiengine/widgets/label_widget.cpp @@ -66,7 +66,10 @@ void LabelWidget::add() EGUI_ALIGNMENT align = EGUIA_UPPERLEFT; if (m_properties[PROP_TEXT_ALIGN] == "center") align = EGUIA_CENTER; else if (m_properties[PROP_TEXT_ALIGN] == "right") align = EGUIA_LOWERRIGHT; - EGUI_ALIGNMENT valign = EGUIA_CENTER ; //TODO: make label v-align configurable through XML file? + + EGUI_ALIGNMENT valign = EGUIA_CENTER ; + if (m_properties[PROP_TEXT_VALIGN] == "top") valign = EGUIA_UPPERLEFT; + if (m_properties[PROP_TEXT_VALIGN] == "bottom") valign = EGUIA_LOWERRIGHT; IGUIStaticText* irrwidget; if (m_scroll_speed != 0) From 571a524f59e1b3992efed58d8a80361e7d406424 Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 25 Feb 2018 13:31:45 +0800 Subject: [PATCH 147/413] Update lobby for new gui engine code --- data/gui/online/networking_lobby.stkgui | 16 ++++--- src/network/protocols/connect_to_server.cpp | 7 ++- src/network/protocols/connect_to_server.hpp | 3 ++ src/states_screens/networking_lobby.cpp | 47 ++++++++++----------- src/states_screens/networking_lobby.hpp | 6 +-- 5 files changed, 42 insertions(+), 37 deletions(-) diff --git a/data/gui/online/networking_lobby.stkgui b/data/gui/online/networking_lobby.stkgui index 5bacbe40d..c2660c79c 100644 --- a/data/gui/online/networking_lobby.stkgui +++ b/data/gui/online/networking_lobby.stkgui @@ -3,9 +3,11 @@
-
+
- + + @@ -15,13 +17,15 @@
- - + + + +
+ + +
+
+ + +
+
+ From b72cf4f40678ad11bbd0c58d590ec679beb465e9 Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 13 Mar 2018 01:03:02 +0800 Subject: [PATCH 214/413] Don't assert peer size for client as the listening thread is delayed-start --- src/network/stk_host.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index c43f5f4a2..a738e48e5 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -1012,7 +1012,8 @@ void STKHost::sendPacketExcept(STKPeer* peer, NetworkString *data, void STKHost::sendToServer(NetworkString *data, bool reliable) { std::lock_guard lock(m_peers_mutex); - assert(m_peers.size() == 1); + if (m_peers.empty()) + return; assert(NetworkConfig::get()->isClient()); m_peers.begin()->second->sendPacket(data, reliable); } // sendToServer From a33a9a040bfbaa02d521a41d9588bb9e4f4ba5db Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 13 Mar 2018 09:00:44 +0800 Subject: [PATCH 215/413] Allow text box widget to listen enter event --- src/guiengine/widgets/text_box_widget.cpp | 9 ++++- src/guiengine/widgets/text_box_widget.hpp | 1 + src/states_screens/networking_lobby.cpp | 46 +++++++++++++---------- src/states_screens/networking_lobby.hpp | 13 ++++++- 4 files changed, 47 insertions(+), 22 deletions(-) diff --git a/src/guiengine/widgets/text_box_widget.cpp b/src/guiengine/widgets/text_box_widget.cpp index 9b5684488..e04819027 100644 --- a/src/guiengine/widgets/text_box_widget.cpp +++ b/src/guiengine/widgets/text_box_widget.cpp @@ -63,7 +63,14 @@ public: m_listeners[n].onTextUpdated(); } } - + if (event.EventType == EET_KEY_INPUT_EVENT && event.KeyInput.Key == IRR_KEY_RETURN) + { + for (unsigned int n=0; ngetDeviceManager()->getLatestUsedDevice(); PlayerProfile* profile = PlayerManager::getCurrentPlayer(); StateManager::get()->createActivePlayer(profile, device); + m_chat_box->addListener(this); } // init // ---------------------------------------------------------------------------- @@ -240,6 +241,29 @@ void NetworkingLobby::onUpdate(float delta) } // onUpdate +// ---------------------------------------------------------------------------- +void NetworkingLobby::sendChat(irr::core::stringw text) +{ + // Max 80 words + text = text.subString(0, 80).trim(); + if (text.size() > 0) + { + NetworkString chat(PROTOCOL_LOBBY_ROOM); + chat.addUInt8(LobbyProtocol::LE_CHAT); + + core::stringw name; + PlayerProfile* player = PlayerManager::getCurrentPlayer(); + if (PlayerManager::getCurrentOnlineState() == + PlayerProfile::OS_SIGNED_IN) + name = PlayerManager::getCurrentOnlineUserName(); + else + name = player->getName(); + chat.encodeString(name + L": " + text); + + STKHost::get()->sendToServer(&chat, true); + } +} // sendChat + // ---------------------------------------------------------------------------- void NetworkingLobby::eventCallback(Widget* widget, const std::string& name, const int playerID) @@ -263,25 +287,7 @@ void NetworkingLobby::eventCallback(Widget* widget, const std::string& name, } // click on a user else if (name == m_send_button->m_properties[PROP_ID]) { - core::stringw text = m_chat_box->getText(); - // Max 80 words - text = text.subString(0, 80).trim(); - if (text.size() > 0) - { - NetworkString chat(PROTOCOL_LOBBY_ROOM); - chat.addUInt8(LobbyProtocol::LE_CHAT); - - core::stringw name; - PlayerProfile* player = PlayerManager::getCurrentPlayer(); - if (PlayerManager::getCurrentOnlineState() == - PlayerProfile::OS_SIGNED_IN) - name = PlayerManager::getCurrentOnlineUserName(); - else - name = player->getName(); - chat.encodeString(name + L": " + text); - - STKHost::get()->sendToServer(&chat, true); - } + sendChat(m_chat_box->getText()); m_chat_box->setText(""); } // send chat message diff --git a/src/states_screens/networking_lobby.hpp b/src/states_screens/networking_lobby.hpp index f28f69a78..29b341c03 100644 --- a/src/states_screens/networking_lobby.hpp +++ b/src/states_screens/networking_lobby.hpp @@ -19,6 +19,7 @@ #define HEADER_NETWORKING_LOBBY_HPP #include "guiengine/screen.hpp" +#include "guiengine/widgets/text_box_widget.hpp" #include #include @@ -46,7 +47,8 @@ namespace irr * \ingroup states_screens */ class NetworkingLobby : public GUIEngine::Screen, - public GUIEngine::ScreenSingleton + public GUIEngine::ScreenSingleton, + public GUIEngine::ITextBoxWidgetListener { private: friend class GUIEngine::ScreenSingleton; @@ -70,6 +72,15 @@ private: /** \brief implement optional callback from parent class GUIEngine::Screen */ virtual void unloaded(); + virtual void onTextUpdated() {} + virtual bool onEnterPressed(const irr::core::stringw& text) OVERRIDE + { + sendChat(text); + return true; + } + + void sendChat(irr::core::stringw text); + public: virtual void onUpdate(float delta) OVERRIDE; From fe0adadd162cd84a8c948e0e23339815d56fa220 Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 13 Mar 2018 12:52:04 +0800 Subject: [PATCH 216/413] Simplify linebreak --- src/states_screens/networking_lobby.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/states_screens/networking_lobby.cpp b/src/states_screens/networking_lobby.cpp index 1000a3404..837a4a8fe 100644 --- a/src/states_screens/networking_lobby.cpp +++ b/src/states_screens/networking_lobby.cpp @@ -60,7 +60,6 @@ DEFINE_SCREEN_SINGLETON( NetworkingLobby ); // ---------------------------------------------------------------------------- NetworkingLobby::NetworkingLobby() : Screen("online/networking_lobby.stkgui") { - m_server_info_height = GUIEngine::getFont()->getDimension(L"X").Height; m_player_list = NULL; } // NetworkingLobby @@ -128,6 +127,7 @@ void NetworkingLobby::beforeAddingWidget() void NetworkingLobby::init() { Screen::init(); + m_server_info_height = GUIEngine::getFont()->getDimension(L"X").Height; m_start_button->setVisible(false); // For now create the active player and bind it to the right @@ -180,13 +180,7 @@ void NetworkingLobby::setJoinedServer(std::shared_ptr server) // ---------------------------------------------------------------------------- void NetworkingLobby::addMoreServerInfo(core::stringw info) { - assert(m_text_bubble->getDimension().Height > 10 && - m_text_bubble->getDimension().Width > 10); - while ((int)m_server_info.size() * m_server_info_height > - m_text_bubble->getDimension().Height - 10) - { - m_server_info.erase(m_server_info.begin()); - } + assert(m_text_bubble->getDimension().Width > 10); while ((int)GUIEngine::getFont()->getDimension(info.c_str()).Width > m_text_bubble->getDimension().Width - 10) { @@ -201,6 +195,11 @@ void NetworkingLobby::addMoreServerInfo(core::stringw info) { m_server_info.push_back(info); } + while ((int)m_server_info.size() * m_server_info_height > + m_text_bubble->getDimension().Height) + { + m_server_info.erase(m_server_info.begin()); + } } // addMoreServerInfo // ---------------------------------------------------------------------------- @@ -244,8 +243,7 @@ void NetworkingLobby::onUpdate(float delta) // ---------------------------------------------------------------------------- void NetworkingLobby::sendChat(irr::core::stringw text) { - // Max 80 words - text = text.subString(0, 80).trim(); + text = text.trim().removeChars(L"\n\r"); if (text.size() > 0) { NetworkString chat(PROTOCOL_LOBBY_ROOM); @@ -258,7 +256,8 @@ void NetworkingLobby::sendChat(irr::core::stringw text) name = PlayerManager::getCurrentOnlineUserName(); else name = player->getName(); - chat.encodeString(name + L": " + text); + // Max 80 words + chat.encodeString((name + L": " + text).subString(0, 80)); STKHost::get()->sendToServer(&chat, true); } From c73536263f590b8d58be69887e58c617981102b9 Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 13 Mar 2018 14:39:20 +0800 Subject: [PATCH 217/413] Add password protected and version info to server --- src/main.cpp | 7 ++++--- src/network/network_config.cpp | 1 + src/network/network_config.hpp | 3 +++ src/network/protocols/server_lobby.cpp | 5 +++++ src/network/server.cpp | 5 ++++- src/network/server.hpp | 8 ++++++-- src/network/servers_manager.cpp | 17 ++++++++++++++++- src/network/stk_host.cpp | 6 +++--- src/states_screens/create_server_screen.cpp | 2 +- 9 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 7ed89dc78..1c3f17533 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1045,10 +1045,11 @@ int handleCmdLine() if(CommandLine::has("--network-console")) STKHost::m_enable_console = true; + core::stringw server_password; if (CommandLine::has("--server-password", &s)) { - core::stringw pw = StringUtils::xmlDecode(s); - NetworkConfig::get()->setPassword(StringUtils::wideToUtf8(pw)); + server_password = StringUtils::xmlDecode(s); + NetworkConfig::get()->setPassword(StringUtils::wideToUtf8(server_password)); } if (CommandLine::has("--server-id-file", &s)) @@ -1084,7 +1085,7 @@ int handleCmdLine() NetworkConfig::get()->getMaxPlayers(), 0, race_manager->getDifficulty(), NetworkConfig::get()->getServerGameMode(race_manager->getMinorMode(), - race_manager->getMajorMode()), ip); + race_manager->getMajorMode()), ip, !server_password.empty()); NetworkingLobby::getInstance()->setJoinedServer(server); STKHost::create(server); } diff --git a/src/network/network_config.cpp b/src/network/network_config.cpp index 400b54d59..34c55b218 100644 --- a/src/network/network_config.cpp +++ b/src/network/network_config.cpp @@ -23,6 +23,7 @@ NetworkConfig *NetworkConfig::m_network_config = NULL; bool NetworkConfig::m_disable_lan = false; +const uint8_t NetworkConfig::m_server_version = 1; /** \class NetworkConfig * This class is the interface between STK and the online code, particularly diff --git a/src/network/network_config.hpp b/src/network/network_config.hpp index c97c5f7d8..b71c0be00 100644 --- a/src/network/network_config.hpp +++ b/src/network/network_config.hpp @@ -93,6 +93,9 @@ public: * WAN code to be used when connection client and server). */ static bool m_disable_lan; + /** Server version, will be advanced if there are protocol changes. */ + static const uint8_t m_server_version; + /** Singleton get, which creates this object if necessary. */ static NetworkConfig *get() { diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index faf30fd5f..dab4f346c 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -425,6 +425,11 @@ void ServerLobby::registerServer() request->addParameter("game_mode", NetworkConfig::get()->getServerGameMode(race_manager->getMinorMode(), race_manager->getMajorMode())); + request->addParameter("password", + (unsigned)(!NetworkConfig::get()->getPassword().empty())); + request->addParameter("version", + (unsigned)NetworkConfig::m_server_version); + Log::info("ServerLobby", "Public server addr %s", addr.toString().c_str()); request->executeNow(); diff --git a/src/network/server.cpp b/src/network/server.cpp index cb205c025..ef698e35e 100644 --- a/src/network/server.cpp +++ b/src/network/server.cpp @@ -56,6 +56,7 @@ Server::Server(const XMLNode& xml) xml.get("port", &port); m_address.setPort(port); xml.get("private_port", &m_private_port); + xml.get("password", &m_password_protected); } // Server(const XML&) // ---------------------------------------------------------------------------- @@ -69,10 +70,11 @@ Server::Server(const XMLNode& xml) * \param difficulty The difficulty of server. * \param server_mode The game modes of server (including minor and major). * \param address IP and port of the server. + * \param password_protected True if can only be joined with a password. */ Server::Server(unsigned server_id, const core::stringw &name, int max_players, int current_players, unsigned difficulty, unsigned server_mode, - const TransportAddress &address) + const TransportAddress &address, bool password_protected) { m_name = name; m_lower_case_name = StringUtils::toLowerCase(StringUtils::wideToUtf8(name)); @@ -87,6 +89,7 @@ Server::Server(unsigned server_id, const core::stringw &name, int max_players, m_difficulty = (RaceManager::Difficulty)difficulty; m_minor_mode = NetworkConfig::get()->getLocalGameMode(server_mode).first; m_major_mode = NetworkConfig::get()->getLocalGameMode(server_mode).second; + m_password_protected = password_protected; } // server(server_id, ...) // ---------------------------------------------------------------------------- diff --git a/src/network/server.hpp b/src/network/server.hpp index 058a31864..0ee2339dd 100644 --- a/src/network/server.hpp +++ b/src/network/server.hpp @@ -74,13 +74,16 @@ protected: RaceManager::Difficulty m_difficulty; + bool m_password_protected; + public: /** Initialises the object from an XML node. */ Server(const XMLNode &xml); Server(unsigned server_id, const irr::core::stringw &name, int max_players, int current_players, unsigned difficulty, - unsigned server_mode, const TransportAddress &address); + unsigned server_mode, const TransportAddress &address, + bool password_protected); bool filterByWords(const irr::core::stringw words) const; // ------------------------------------------------------------------------ /** Returns ip address and port of this server. */ @@ -114,7 +117,8 @@ public: { return m_major_mode; } // ------------------------------------------------------------------------ RaceManager::Difficulty getDifficulty() const { return m_difficulty; } - + // ------------------------------------------------------------------------ + bool isPasswordProtected() const { return m_password_protected; } }; // Server #endif // HEADER_SERVER_HPP diff --git a/src/network/servers_manager.cpp b/src/network/servers_manager.cpp index 01bb43ae3..37a1d4eaa 100644 --- a/src/network/servers_manager.cpp +++ b/src/network/servers_manager.cpp @@ -160,6 +160,12 @@ Online::XMLRequest* ServersManager::getLANRefreshRequest() const if (len > 0) { BareNetworkString s(buffer, len); + uint8_t version = s.getUInt8(); + if (version != NetworkConfig::m_server_version) + { + Log::verbose("ServersManager", "Skipping a server"); + continue; + } irr::core::stringw name; // bytes_read is the number of bytes read s.decodeStringW(&name); @@ -169,9 +175,10 @@ Online::XMLRequest* ServersManager::getLANRefreshRequest() const uint8_t difficulty = s.getUInt8(); uint8_t mode = s.getUInt8(); sender.setPort(port); + uint8_t password = s.getUInt8(); servers_now.emplace_back(std::make_shared (cur_server_id++, name, max_players, players, - difficulty, mode, sender)); + difficulty, mode, sender, password)); } // if received_data } // while still waiting m_success = true; @@ -229,6 +236,14 @@ void ServersManager::setWanServers(bool success, const XMLNode* input) const XMLNode *servers_xml = input->getNode("servers"); for (unsigned int i = 0; i < servers_xml->getNumNodes(); i++) { + unsigned version = 0; + servers_xml->getNode(i)->get("version", &version); + assert(version != 0); + if (version != NetworkConfig::m_server_version) + { + Log::verbose("ServersManager", "Skipping a server"); + continue; + } m_servers.emplace_back( std::make_shared(*servers_xml->getNode(i))); } diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index a738e48e5..9d2c33075 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -872,10 +872,9 @@ void STKHost::handleDirectSocketRequest(Network* direct_socket) name = name.substr(0, 255); // Send the answer, consisting of server name, max players, - // current players, and the client's ip address and port - // number (which solves the problem which network interface - // might be the right one if there is more than one). + // current players BareNetworkString s((int)name.size()+1+11); + s.addUInt8(NetworkConfig::m_server_version); s.encodeString(name); s.addUInt8(NetworkConfig::get()->getMaxPlayers()); s.addUInt8(0); // FIXME: current number of connected players @@ -884,6 +883,7 @@ void STKHost::handleDirectSocketRequest(Network* direct_socket) s.addUInt8((uint8_t) NetworkConfig::get()->getServerGameMode(race_manager->getMinorMode(), race_manager->getMajorMode())); + s.addUInt8(!NetworkConfig::get()->getPassword().empty()); direct_socket->sendRawPacket(s, sender); } // if message is server-requested else if (command == connection_cmd) diff --git a/src/states_screens/create_server_screen.cpp b/src/states_screens/create_server_screen.cpp index 7587371bc..a824902a8 100644 --- a/src/states_screens/create_server_screen.cpp +++ b/src/states_screens/create_server_screen.cpp @@ -186,7 +186,7 @@ void CreateServerScreen::createServer() max_players, /*current_player*/0, (RaceManager::Difficulty) difficulty_widget->getSelection(PLAYER_ID_GAME_MASTER), NetworkConfig::get()->getServerGameMode(race_manager->getMinorMode(), - race_manager->getMajorMode()), server_address); + race_manager->getMajorMode()), server_address, !password_w.empty()); #undef USE_GRAPHICS_SERVER #ifdef USE_GRAPHICS_SERVER From 901c5eabecddedd607509ffc0ec8d8dfe73ffea8 Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 13 Mar 2018 16:04:59 +0800 Subject: [PATCH 218/413] Allow specifying server password in dialog --- data/gui/online/server_info_dialog.stkgui | 4 +++ src/main.cpp | 6 ++-- src/network/network_config.hpp | 6 +++- src/network/protocols/connect_to_server.cpp | 18 ++++++++--- src/states_screens/create_server_screen.cpp | 25 ++++++++++++---- .../dialogs/server_info_dialog.cpp | 30 +++++++++++++++++-- .../dialogs/server_info_dialog.hpp | 13 ++++++-- src/states_screens/online_profile_servers.cpp | 1 + 8 files changed, 84 insertions(+), 19 deletions(-) diff --git a/data/gui/online/server_info_dialog.stkgui b/data/gui/online/server_info_dialog.stkgui index aa98b72e0..eb53f5fc6 100644 --- a/data/gui/online/server_info_dialog.stkgui +++ b/data/gui/online/server_info_dialog.stkgui @@ -26,6 +26,10 @@
+
+
diff --git a/src/main.cpp b/src/main.cpp index 1c3f17533..ba6767533 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1045,11 +1045,11 @@ int handleCmdLine() if(CommandLine::has("--network-console")) STKHost::m_enable_console = true; - core::stringw server_password; + std::string server_password; if (CommandLine::has("--server-password", &s)) { - server_password = StringUtils::xmlDecode(s); - NetworkConfig::get()->setPassword(StringUtils::wideToUtf8(server_password)); + server_password = s; + NetworkConfig::get()->setPassword(server_password); } if (CommandLine::has("--server-id-file", &s)) diff --git a/src/network/network_config.hpp b/src/network/network_config.hpp index b71c0be00..710375aae 100644 --- a/src/network/network_config.hpp +++ b/src/network/network_config.hpp @@ -164,7 +164,11 @@ public: void setIsWAN() { m_network_type = NETWORK_WAN; } // ------------------------------------------------------------------------ /** Set that this is not a networked game. */ - void unsetNetworking() { m_network_type = NETWORK_NONE; } + void unsetNetworking() + { + m_network_type = NETWORK_NONE; + m_password = ""; + } // ------------------------------------------------------------------------ /** Sets the maximum number of players for this server. */ void setMaxPlayers(int n) { m_max_players = n; } diff --git a/src/network/protocols/connect_to_server.cpp b/src/network/protocols/connect_to_server.cpp index d5da2f262..0c0b193fc 100644 --- a/src/network/protocols/connect_to_server.cpp +++ b/src/network/protocols/connect_to_server.cpp @@ -78,17 +78,26 @@ void ConnectToServer::asynchronousUpdate() StkTime::sleep(1); while (!ServersManager::get()->listUpdated()) StkTime::sleep(1); - if (!ServersManager::get()->getServers().empty()) + auto servers = ServersManager::get()->getServers(); + ServersManager::get()->cleanUpServers(); + + // Remove password protected servers + servers.erase(std::remove_if(servers.begin(), servers.end(), [] + (const std::shared_ptr a)->bool + { + return a->isPasswordProtected(); + }), servers.end()); + + if (!servers.empty()) { // For quick play we choose the server with the least player - ServersManager::get()->sortServers([] + std::sort(servers.begin(), servers.end(), [] (const std::shared_ptr a, const std::shared_ptr b)->bool { return a->getCurrentPlayers() < b->getCurrentPlayers(); }); - m_server = ServersManager::get()->getServers()[0]; - ServersManager::get()->cleanUpServers(); + m_server = servers[0]; } else { @@ -99,6 +108,7 @@ void ConnectToServer::asynchronousUpdate() m_state = EXITING; return; } + servers.clear(); } STKHost::get()->setPublicAddress(); // Set to DONE will stop STKHost is not connected diff --git a/src/states_screens/create_server_screen.cpp b/src/states_screens/create_server_screen.cpp index a824902a8..f8119b475 100644 --- a/src/states_screens/create_server_screen.cpp +++ b/src/states_screens/create_server_screen.cpp @@ -164,18 +164,31 @@ void CreateServerScreen::createServer() if (name.size() < 4 || name.size() > 30) { + //I18N: In the create server screen m_info_widget->setText( _("Name has to be between 4 and 30 characters long!"), false); SFXManager::get()->quickSound("anvil"); return; } - assert(max_players > 1 && - max_players <= UserConfigParams::m_server_max_players.getDefaultValue()); + assert(max_players > 1 && max_players <= + UserConfigParams::m_server_max_players.getDefaultValue()); UserConfigParams::m_server_max_players = max_players; - core::stringw password_w = getWidget("password")->getText(); - std::string password = StringUtils::xmlEncode(password_w); - NetworkConfig::get()->setPassword(StringUtils::wideToUtf8(password_w)); + std::string password = StringUtils::wideToUtf8(getWidget + ("password")->getText()); + if ((!password.empty() != 0 && + password.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP" + "QRSTUVWXYZ01234567890_") != std::string::npos) || + password.size() > 255) + { + //I18N: In the create server screen + m_info_widget->setText( + _("Incorrect characters in password!"), false); + SFXManager::get()->quickSound("anvil"); + return; + } + + NetworkConfig::get()->setPassword(password); if (!password.empty()) password = std::string(" --server-password=") + password; @@ -186,7 +199,7 @@ void CreateServerScreen::createServer() max_players, /*current_player*/0, (RaceManager::Difficulty) difficulty_widget->getSelection(PLAYER_ID_GAME_MASTER), NetworkConfig::get()->getServerGameMode(race_manager->getMinorMode(), - race_manager->getMajorMode()), server_address, !password_w.empty()); + race_manager->getMajorMode()), server_address, !password.empty()); #undef USE_GRAPHICS_SERVER #ifdef USE_GRAPHICS_SERVER diff --git a/src/states_screens/dialogs/server_info_dialog.cpp b/src/states_screens/dialogs/server_info_dialog.cpp index 7597c3fbe..779606aee 100644 --- a/src/states_screens/dialogs/server_info_dialog.cpp +++ b/src/states_screens/dialogs/server_info_dialog.cpp @@ -18,8 +18,13 @@ #include "states_screens/dialogs/server_info_dialog.hpp" #include "guiengine/engine.hpp" +#include "guiengine/widgets/icon_button_widget.hpp" +#include "guiengine/widgets/label_widget.hpp" +#include "guiengine/widgets/ribbon_widget.hpp" +#include "guiengine/widgets/text_box_widget.hpp" #include "network/server.hpp" #include "network/stk_host.hpp" +#include "network/network_config.hpp" #include "states_screens/networking_lobby.hpp" #include "states_screens/state_manager.hpp" #include "utils/string_utils.hpp" @@ -39,7 +44,7 @@ using namespace Online; * server (i.e. while it is being created). */ ServerInfoDialog::ServerInfoDialog(std::shared_ptr server) - : ModalDialog(0.8f,0.8f), m_server(server) + : ModalDialog(0.8f,0.8f), m_server(server), m_password(NULL) { Log::info("ServerInfoDialog", "Server id is %d, Host id is %d", server->getServerId(), server->getHostId()); @@ -67,10 +72,21 @@ ServerInfoDialog::ServerInfoDialog(std::shared_ptr server) assert(m_cancel_widget != NULL); m_options_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER); + if (m_server->isPasswordProtected()) + { + m_password = getWidget("password"); + m_password->setPasswordBox(true, L'*'); + assert(m_password != NULL); + } + else + { + getWidget("label_password")->setVisible(false); + getWidget("password")->setVisible(false); + } + } // ServerInfoDialog // ----------------------------------------------------------------------------- - ServerInfoDialog::~ServerInfoDialog() { } // ~ServerInfoDialog @@ -78,6 +94,16 @@ ServerInfoDialog::~ServerInfoDialog() // ----------------------------------------------------------------------------- void ServerInfoDialog::requestJoin() { + if (m_server->isPasswordProtected()) + { + assert(m_password != NULL); + NetworkConfig::get()->setPassword( + StringUtils::wideToUtf8(m_password->getText())); + } + else + { + NetworkConfig::get()->setPassword(""); + } STKHost::create(m_server); NetworkingLobby::getInstance()->setJoinedServer(m_server); ModalDialog::dismiss(); diff --git a/src/states_screens/dialogs/server_info_dialog.hpp b/src/states_screens/dialogs/server_info_dialog.hpp index 5c89ababa..38b08b985 100644 --- a/src/states_screens/dialogs/server_info_dialog.hpp +++ b/src/states_screens/dialogs/server_info_dialog.hpp @@ -20,11 +20,16 @@ #define HEADER_SERVER_INFO_DIALOG_HPP #include "guiengine/modaldialog.hpp" -#include "guiengine/widgets/icon_button_widget.hpp" -#include "guiengine/widgets/ribbon_widget.hpp" -#include "guiengine/widgets/label_widget.hpp" #include "utils/types.hpp" +namespace GUIEngine +{ + class LabelWidget; + class RibbonWidget; + class IconButtonWidget; + class TextBoxWidget; +} + #include #include @@ -49,6 +54,8 @@ private: /** The cancel button. */ GUIEngine::IconButtonWidget *m_cancel_widget; + /** Specify server password if needed. */ + GUIEngine::TextBoxWidget* m_password; public: ServerInfoDialog(std::shared_ptr server); diff --git a/src/states_screens/online_profile_servers.cpp b/src/states_screens/online_profile_servers.cpp index ba055ee4c..5847c9a9e 100644 --- a/src/states_screens/online_profile_servers.cpp +++ b/src/states_screens/online_profile_servers.cpp @@ -110,6 +110,7 @@ void OnlineProfileServers::eventCallback(Widget* widget, const std::string& name // ---------------------------------------------------------------------------- void OnlineProfileServers::doQuickPlay() { + NetworkConfig::get()->setPassword(""); STKHost::create(); NetworkingLobby::getInstance()->setJoinedServer(nullptr); NetworkingLobby::getInstance()->push(); From 5699a86586363d5f6201db0bae0f866e79cfa82c Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 14 Mar 2018 00:50:19 +0800 Subject: [PATCH 219/413] Allow toggling the display of only private server --- data/gui/online/server_selection.stkgui | 8 ++++ src/network/protocols/connect_to_server.cpp | 5 ++- src/network/servers_manager.hpp | 13 +----- src/states_screens/server_selection.cpp | 50 +++++++++++++++------ src/states_screens/server_selection.hpp | 18 +++++++- 5 files changed, 64 insertions(+), 30 deletions(-) diff --git a/data/gui/online/server_selection.stkgui b/data/gui/online/server_selection.stkgui index 8d89bdd96..556e2d95b 100644 --- a/data/gui/online/server_selection.stkgui +++ b/data/gui/online/server_selection.stkgui @@ -11,5 +11,13 @@ +
+
+ + +
+
diff --git a/src/network/protocols/connect_to_server.cpp b/src/network/protocols/connect_to_server.cpp index 0c0b193fc..36eabdd7f 100644 --- a/src/network/protocols/connect_to_server.cpp +++ b/src/network/protocols/connect_to_server.cpp @@ -33,6 +33,8 @@ #include "utils/time.hpp" #include "utils/log.hpp" +#include + // ---------------------------------------------------------------------------- /** Specify server to connect to. * \param server Server to connect to (if nullptr than we use quick play). @@ -78,8 +80,7 @@ void ConnectToServer::asynchronousUpdate() StkTime::sleep(1); while (!ServersManager::get()->listUpdated()) StkTime::sleep(1); - auto servers = ServersManager::get()->getServers(); - ServersManager::get()->cleanUpServers(); + auto servers = std::move(ServersManager::get()->getServers()); // Remove password protected servers servers.erase(std::remove_if(servers.begin(), servers.end(), [] diff --git a/src/network/servers_manager.hpp b/src/network/servers_manager.hpp index 713484693..823ce74b3 100644 --- a/src/network/servers_manager.hpp +++ b/src/network/servers_manager.hpp @@ -20,9 +20,6 @@ #define HEADER_SERVERS_MANAGER_HPP #include -#include -#include -#include #include #include #include @@ -70,17 +67,9 @@ public: // ------------------------------------------------------------------------ void cleanUpServers() { m_servers.clear(); } // ------------------------------------------------------------------------ - void sortServers(std::function a, - const std::shared_ptr b)> sorting_function) - { - assert(m_list_updated); - std::sort(m_servers.begin(), m_servers.end(), sorting_function); - } - // ------------------------------------------------------------------------ bool refresh(); // ------------------------------------------------------------------------ - const std::vector >& getServers() const - { return m_servers; } + std::vector >& getServers() { return m_servers; } // ------------------------------------------------------------------------ bool listUpdated() const { return m_list_updated; } diff --git a/src/states_screens/server_selection.cpp b/src/states_screens/server_selection.cpp index 3576168a3..04bc7268f 100644 --- a/src/states_screens/server_selection.cpp +++ b/src/states_screens/server_selection.cpp @@ -18,6 +18,9 @@ #include "states_screens/server_selection.hpp" #include "audio/sfx_manager.hpp" +#include "guiengine/widgets/check_box_widget.hpp" +#include "guiengine/widgets/icon_button_widget.hpp" +#include "guiengine/widgets/label_widget.hpp" #include "guiengine/modaldialog.hpp" #include "network/network_config.hpp" #include "network/server.hpp" @@ -28,8 +31,8 @@ #include "utils/translation.hpp" #include "utils/string_utils.hpp" -#include -#include +#include +#include using namespace Online; @@ -55,6 +58,7 @@ ServerSelection::~ServerSelection() */ void ServerSelection::tearDown() { + m_servers.clear(); ServersManager::get()->cleanUpServers(); m_server_list_widget->clear(); } // tearDown @@ -85,6 +89,9 @@ void ServerSelection::loadedFromFile() m_server_list_widget = getWidget("server_list"); assert(m_server_list_widget != NULL); m_server_list_widget->setColumnListener(this); + m_private_server = getWidget("private_server"); + assert(m_private_server != NULL); + m_private_server->setState(false); } // loadedFromFile // ---------------------------------------------------------------------------- @@ -118,7 +125,7 @@ void ServerSelection::init() void ServerSelection::loadList(unsigned sort_case) { m_server_list_widget->clear(); - ServersManager::get()->sortServers([sort_case, this] + std::sort(m_servers.begin(), m_servers.end(), [sort_case, this] (const std::shared_ptr a, const std::shared_ptr b)->bool { @@ -142,7 +149,7 @@ void ServerSelection::loadList(unsigned sort_case) assert(false); return false; }); - for (auto server : ServersManager::get()->getServers()) + for (auto server : m_servers) { core::stringw num_players; num_players.append(StringUtils::toWString(server->getCurrentPlayers())); @@ -182,24 +189,25 @@ void ServerSelection::eventCallback(GUIEngine::Widget* widget, { StateManager::get()->escapePressed(); } - else if (name == "reload") { refresh(); } - + else if (name == "private_server") + { + copyFromServersManager(); + } else if (name == m_server_list_widget->m_properties[GUIEngine::PROP_ID]) { int selected_index = m_server_list_widget->getSelectionID(); // This can happen e.g. when the list is empty and the user // clicks somewhere. if (selected_index < 0 || m_refreshing_server || - selected_index >= (int)ServersManager::get()->getServers().size()) + selected_index >= (int)m_servers.size()) { return; } - new ServerInfoDialog( - ServersManager::get()->getServers()[selected_index]); + new ServerInfoDialog(m_servers[selected_index]); } // click on server } // eventCallback @@ -209,14 +217,13 @@ void ServerSelection::eventCallback(GUIEngine::Widget* widget, * if so, update the list of servers. */ void ServerSelection::onUpdate(float dt) - { // In case of auto-connect command line parameter, select the first server asap if (NetworkConfig::get()->isAutoConnect() && m_refreshing_server == false && - !ServersManager::get()->getServers().empty()) + !m_servers.empty()) { - ServerInfoDialog *sid = new ServerInfoDialog(ServersManager::get()->getServers()[0]); + ServerInfoDialog *sid = new ServerInfoDialog(m_servers[0]); sid->requestJoin(); } @@ -228,8 +235,9 @@ void ServerSelection::onUpdate(float dt) if (!ServersManager::get()->getServers().empty()) { int selection = m_server_list_widget->getSelectionID(); - std::string selection_str = m_server_list_widget->getSelectionInternalName(); - loadList(0); + std::string selection_str = m_server_list_widget + ->getSelectionInternalName(); + copyFromServersManager(); // restore previous selection if (selection != -1 && selection_str != "loading") m_server_list_widget->setSelectionID(selection); @@ -250,3 +258,17 @@ void ServerSelection::onUpdate(float dt) } } // onUpdate + +// ---------------------------------------------------------------------------- +void ServerSelection::copyFromServersManager() +{ + m_servers = ServersManager::get()->getServers(); + if (m_servers.empty()) + return; + m_servers.erase(std::remove_if(m_servers.begin(), m_servers.end(), + [this](const std::shared_ptr a)->bool + { + return a->isPasswordProtected() != m_private_server->getState(); + }), m_servers.end()); + loadList(0); +} // copyFromServersManager diff --git a/src/states_screens/server_selection.hpp b/src/states_screens/server_selection.hpp index 8c2c1f1f8..b2c0d2131 100644 --- a/src/states_screens/server_selection.hpp +++ b/src/states_screens/server_selection.hpp @@ -19,11 +19,20 @@ #define HEADER_SERVER_SELECTION_HPP #include "guiengine/screen.hpp" -#include "guiengine/widgets.hpp" +#include "guiengine/widgets/list_widget.hpp" + +#include namespace Online { class XMLRequest; } -namespace GUIEngine { class Widget; } +namespace GUIEngine +{ + class CheckBoxWidget; + class IconButtonWidget; + class LabelWidget; + class ListWidget; +} +class Server; /** * \brief ServerSelection @@ -39,6 +48,9 @@ private: ServerSelection(); ~ServerSelection(); + std::vector > m_servers; + + GUIEngine::CheckBoxWidget* m_private_server; GUIEngine::IconButtonWidget* m_reload_widget; GUIEngine::LabelWidget* m_update_status; GUIEngine::ListWidget* m_server_list_widget; @@ -51,6 +63,8 @@ private: /** Load the servers into the main list.*/ void loadList(unsigned sort_case); + void copyFromServersManager(); + void refresh(); public: From f3d19950331c3be3e15d4cfa133d011b387ddcc6 Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Tue, 13 Mar 2018 21:39:27 -0400 Subject: [PATCH 220/413] Change assert into warning, it's definitely not fatal --- src/guiengine/widget.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/guiengine/widget.cpp b/src/guiengine/widget.cpp index db1956f56..b28ac4d36 100644 --- a/src/guiengine/widget.cpp +++ b/src/guiengine/widget.cpp @@ -309,7 +309,8 @@ bool Widget::isVisible() const { if (m_element != NULL) { - assert(m_element->isVisible() == m_is_visible); + if (m_element->isVisible() != m_is_visible) + Log::warn("Widget", "Widget isVisible mismatch"); } return m_is_visible; } From ea8ba470d6245d1889274227566f0664278032e6 Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 14 Mar 2018 10:07:54 +0800 Subject: [PATCH 221/413] Use (s) for server --- data/gui/online/server_selection.stkgui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/gui/online/server_selection.stkgui b/data/gui/online/server_selection.stkgui index 556e2d95b..5c19d2eb0 100644 --- a/data/gui/online/server_selection.stkgui +++ b/data/gui/online/server_selection.stkgui @@ -16,7 +16,7 @@
From 1b8d4c80dc8876c21ac5b7a4a4031175df40e567 Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 14 Mar 2018 15:48:02 +0800 Subject: [PATCH 222/413] Initial work to port network profile to use weak_ptr --- src/modes/world_status.cpp | 8 +- src/modes/world_status.hpp | 5 +- src/network/game_setup.cpp | 140 ++-------- src/network/game_setup.hpp | 54 ++-- src/network/network_config.hpp | 6 +- src/network/protocols/client_lobby.cpp | 35 +-- .../protocols/game_events_protocol.cpp | 4 +- src/network/protocols/lobby_protocol.cpp | 22 +- src/network/protocols/lobby_protocol.hpp | 9 +- src/network/protocols/server_lobby.cpp | 259 ++++++++---------- src/network/protocols/server_lobby.hpp | 20 +- src/network/stk_host.cpp | 34 +-- src/network/stk_host.hpp | 54 ++-- src/network/stk_peer.hpp | 4 +- src/states_screens/network_kart_selection.cpp | 84 +----- src/states_screens/network_kart_selection.hpp | 2 - src/states_screens/tracks_screen.cpp | 4 +- src/states_screens/waiting_for_others.cpp | 61 +---- 18 files changed, 263 insertions(+), 542 deletions(-) diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index 0659771d1..670479816 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -48,7 +48,7 @@ WorldStatus::WorldStatus() m_play_track_intro_sound = UserConfigParams::m_music; m_play_ready_set_go_sounds = true; m_play_racestart_sounds = true; - m_server_is_ready = false; + m_server_is_ready.store(false); IrrlichtDevice *device = irr_driver->getDevice(); @@ -98,7 +98,7 @@ void WorldStatus::reset() // In case of a networked race the race can only start once // all protocols are up. This flag is used to wait for // a confirmation before starting the actual race. - m_server_is_ready = false; + m_server_is_ready.store(false); } // reset //----------------------------------------------------------------------------- @@ -259,7 +259,7 @@ void WorldStatus::updateTime(const float dt) // loaded the world). The server waits for a confirmation from // each client that they have started (to guarantee that the // server is running with a local time behind all clients). - if (!m_server_is_ready) return; + if (m_server_is_ready.load() == false) return; m_phase = READY_PHASE; auto cl = LobbyProtocol::get(); @@ -474,7 +474,7 @@ float WorldStatus::adjustDT(float dt) */ void WorldStatus::startReadySetGo() { - m_server_is_ready = true; + m_server_is_ready.store(true); } // startReadySetGo //----------------------------------------------------------------------------- diff --git a/src/modes/world_status.hpp b/src/modes/world_status.hpp index ec255f306..061bc60c3 100644 --- a/src/modes/world_status.hpp +++ b/src/modes/world_status.hpp @@ -19,6 +19,7 @@ #define HEADER_WORLD_STATUS_HPP #include "utils/cpp2011.hpp" +#include class SFXBase; @@ -138,7 +139,7 @@ private: * set go' to make sure all client are actually ready to start the game. * A server on the other hand will run behind all clients, so it will * wait for all clients to indicate that they have started the race. */ - bool m_server_is_ready; + std::atomic_bool m_server_is_ready; void startEngines(); @@ -205,7 +206,7 @@ public: /** Get the time since start regardless of which way the clock counts */ float getTimeSinceStart() const { return m_count_up_timer; } // ------------------------------------------------------------------------ - void setReadyToRace() { m_server_is_ready = true; } + void setReadyToRace() { m_server_is_ready.store(true); } // ------------------------------------------------------------------------ /** Sets a time by which the clock should be adjusted. Used by networking * if too many rewinds are detected. */ diff --git a/src/network/game_setup.cpp b/src/network/game_setup.cpp index d05704607..5663dc434 100644 --- a/src/network/game_setup.cpp +++ b/src/network/game_setup.cpp @@ -19,6 +19,7 @@ #include "network/game_setup.hpp" #include "karts/abstract_kart.hpp" +#include "network/race_config.hpp" #include "modes/world.hpp" #include "network/network_player_profile.hpp" #include "online/online_profile.hpp" @@ -39,43 +40,39 @@ GameSetup::GameSetup() GameSetup::~GameSetup() { // remove all players - for (unsigned int i = 0; i < m_players.size(); i++) - { - delete m_players[i]; - }; m_players.clear(); delete m_race_config; } // ~GameSetup //----------------------------------------------------------------------------- - -void GameSetup::addPlayer(NetworkPlayerProfile* profile) +void GameSetup::addPlayer(std::shared_ptr profile) { m_players.push_back(profile); - Log::info("GameSetup", "New player in the game setup. Player id : %d.", - profile->getGlobalPlayerId()); + Log::info("GameSetup", "New player in the game setup. Player name : %s.", + StringUtils::wideToUtf8(profile->getName()).c_str()); } // addPlayer //----------------------------------------------------------------------------- -/** Removed a player give his NetworkPlayerProfile. - * \param profile The NetworkPlayerProfile to remove. - * \return True if the player was found and removed, false otherwise. +/** Update and see if any player disconnects. + * \param remove_disconnected_players remove the disconnected players, + * otherwise replace with AI (when racing), so this function must be called + * in main thread. */ -bool GameSetup::removePlayer(const NetworkPlayerProfile *profile) +void GameSetup::update(bool remove_disconnected_players) { - for (unsigned int i = 0; i < m_players.size(); i++) + std::unique_lock lock(m_players_mutex); + if (remove_disconnected_players) { - if (m_players[i] == profile) - { - delete m_players[i]; - m_players.erase(m_players.begin()+i, m_players.begin()+i+1); - Log::verbose("GameSetup", - "Removed a player from the game setup. Remains %u.", - m_players.size()); - return true; - } + m_players.erase(std::remove_if(m_players.begin(), m_players.end(), [] + (const std::weak_ptr npp)->bool + { + return npp.expired(); + }), m_players.end()); + return; } - return false; + lock.unlock(); + if (!World::getWorld()) + return; } // removePlayer //----------------------------------------------------------------------------- @@ -97,38 +94,13 @@ bool GameSetup::isLocalMaster(uint8_t player_id) return m_local_master == player_id; } // isLocalMaster -//----------------------------------------------------------------------------- -/** Sets the kart the specified player uses. - * \param player_id ID of this player (in this race). - * \param kart_name Name of the kart the player picked. - */ -void GameSetup::setPlayerKart(uint8_t player_id, const std::string &kart_name) -{ - bool found = false; - for (unsigned int i = 0; i < m_players.size(); i++) - { - if (m_players[i]->getGlobalPlayerId() == player_id) - { - m_players[i]->setKartName(kart_name); - Log::info("GameSetup::setPlayerKart", "Player %d took kart %s", - player_id, kart_name.c_str()); - found = true; - } - } - if (!found) - { - Log::info("GameSetup::setPlayerKart", "The player %d was unknown.", - player_id); - } -} // setPlayerKart - //----------------------------------------------------------------------------- void GameSetup::bindKartsToProfiles() { World::KartList karts = World::getWorld()->getKarts(); - for (unsigned int i = 0; i < m_players.size(); i++) + /*for (unsigned int i = 0; i < m_players.size(); i++) { Log::info("GameSetup", "Player %d has id %d and kart %s", i, m_players[i]->getGlobalPlayerId(), @@ -155,73 +127,5 @@ void GameSetup::bindKartsToProfiles() { Log::error("GameSetup", "Error while binding world kart ids to players profiles."); } - } + }*/ } // bindKartsToProfiles - -//----------------------------------------------------------------------------- -/** \brief Get a network player profile with the specified player id. - * \param player_id : Player id in this race. - * \return The profile of the player having the specified player id, or - * NULL if no such player exists. - */ -const NetworkPlayerProfile* GameSetup::getProfile(uint8_t player_id) -{ - for (unsigned int i = 0; i < m_players.size(); i++) - { - if (m_players[i]->getGlobalPlayerId()== player_id) - { - return m_players[i]; - } - } - return NULL; -} // getProfile - -//----------------------------------------------------------------------------- -/** \brief Get a network player profile matching a kart name. - * \param kart_name : Name of the kart used by the player. - * \return The profile of the player having the kart kart_name, or NULL - * if no such network profile exists. - */ - -const NetworkPlayerProfile* GameSetup::getProfile(const std::string &kart_name) -{ - for (unsigned int i = 0; i < m_players.size(); i++) - { - if (m_players[i]->getKartName() == kart_name) - { - return m_players[i]; - } - } - return NULL; -} // getProfile(kart_name) - -//----------------------------------------------------------------------------- -/** Returns the list of all player profiles from a specified host. Note that - * this function is somewhat expensive (it loops over all network profiles - * to find the ones with the specified host id). - * \param host_id The host id which players should be collected. - * \return List of NetworkPlayerProfile pointers/ - */ -std::vector GameSetup::getAllPlayersOnHost(uint8_t host_id) -{ - std::vector result; - - for (unsigned int i = 0; i < m_players.size(); i++) - { - if (m_players[i]->getHostId() == host_id) - result.push_back(m_players[i]); - } - return result; -} // getAllPlayersOnHost - -//----------------------------------------------------------------------------- - -bool GameSetup::isKartAvailable(std::string kart_name) -{ - for (unsigned int i = 0; i < m_players.size(); i++) - { - if (m_players[i]->getKartName() == kart_name) - return false; - } - return true; -} diff --git a/src/network/game_setup.hpp b/src/network/game_setup.hpp index 047192421..fb423ea70 100644 --- a/src/network/game_setup.hpp +++ b/src/network/game_setup.hpp @@ -22,15 +22,16 @@ #ifndef GAME_SETUP_HPP #define GAME_SETUP_HPP -#include "network/race_config.hpp" #include "network/remote_kart_info.hpp" +#include #include #include #include #include class NetworkPlayerProfile; +class RaceConfig; // ============================================================================ /*! \class GameSetup @@ -40,8 +41,10 @@ class NetworkPlayerProfile; class GameSetup { private: + mutable std::mutex m_players_mutex; + /** Information about all connected players. */ - std::vector m_players; + std::vector > m_players; /** The race configuration. */ RaceConfig* m_race_config; @@ -52,26 +55,22 @@ private: /** The player id of the local game master, used in * kart selection screen. */ uint8_t m_local_master; + public: - GameSetup(); - virtual ~GameSetup(); - - void addPlayer(NetworkPlayerProfile* profile); //!< Add a player. - bool removePlayer(const NetworkPlayerProfile *profile); - void setPlayerKart(uint8_t player_id, const std::string &kart_name); - void bindKartsToProfiles(); //!< Sets the right world_kart_id in profiles + // ------------------------------------------------------------------------ + GameSetup(); + // ------------------------------------------------------------------------ + ~GameSetup(); + // ------------------------------------------------------------------------ + void addPlayer(std::shared_ptr profile); + // ------------------------------------------------------------------------ + void update(bool remove_disconnected_players); + // ------------------------------------------------------------------------ + void bindKartsToProfiles(); + // ------------------------------------------------------------------------ void setLocalMaster(uint8_t player_id); - + // ------------------------------------------------------------------------ bool isLocalMaster(uint8_t player_id); - const NetworkPlayerProfile* getProfile(uint8_t id); - const NetworkPlayerProfile* getProfile(const std::string &kart_name); - std::vector getAllPlayersOnHost(uint8_t host_id); - - /*! \brief Used to know if a kart is available. - * \param kart_name : Name of the kart to check. - * \return True if the kart hasn't been selected yet, false elseway. - */ - bool isKartAvailable(std::string kart_name); // ------------------------------------------------------------------------ /** Sets the number of local players. */ void setNumLocalPlayers(int n) { m_num_local_players = n; } @@ -79,26 +78,23 @@ public: /** Returns the nunber of local players. */ int getNumLocalPlayers() const { return m_num_local_players; } // ------------------------------------------------------------------------ - /*! \brief Used to know if a kart is playable. - * \param kart_name : Name of the kart to check. - * \return True if the kart is playable (standard kart). - * Currently this is always true as the kart selection screen shows - * only the standard karts. - */ - bool isKartAllowed(std::string kart_name) { return true; } - // ------------------------------------------------------------------------ /** Returns the configuration for this race. */ RaceConfig* getRaceConfig() { return m_race_config; } // ------------------------------------------------------------------------ /** \brief Get the players that are in the game * \return A vector containing pointers on the players profiles. */ - const std::vector& getPlayers() const + std::vector > getPlayers() const { + std::lock_guard lock(m_players_mutex); return m_players; } // getPlayers // ------------------------------------------------------------------------ /** Returns the number of connected players. */ - int getPlayerCount() { return (int)m_players.size(); } + unsigned getPlayerCount() + { + std::lock_guard lock(m_players_mutex); + return (unsigned)m_players.size(); + } // ------------------------------------------------------------------------ /** Returns the id of the local master. */ int getLocalMasterID() const { return m_local_master; } diff --git a/src/network/network_config.hpp b/src/network/network_config.hpp index 710375aae..c4fabba86 100644 --- a/src/network/network_config.hpp +++ b/src/network/network_config.hpp @@ -70,7 +70,7 @@ private: uint16_t m_client_port; /** Maximum number of players on the server. */ - int m_max_players; + unsigned m_max_players; /** True if a client should connect to the first server it finds and * immediately start a race. */ @@ -171,10 +171,10 @@ public: } // ------------------------------------------------------------------------ /** Sets the maximum number of players for this server. */ - void setMaxPlayers(int n) { m_max_players = n; } + void setMaxPlayers(unsigned n) { m_max_players = n; } // ------------------------------------------------------------------------ /** Returns the maximum number of players for this server. */ - int getMaxPlayers() const { return m_max_players; } + unsigned getMaxPlayers() const { return m_max_players; } // ------------------------------------------------------------------------ /** Returns if this instance is a server. */ bool isServer() const { return m_is_server; } diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index cc96cba48..aaee48d29 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -25,10 +25,12 @@ #include "guiengine/message_queue.hpp" #include "modes/world_with_rank.hpp" #include "network/event.hpp" +#include "network/game_setup.hpp" #include "network/network_config.hpp" #include "network/network_player_profile.hpp" #include "network/protocol_manager.hpp" #include "network/protocols/latency_protocol.hpp" +#include "network/race_config.hpp" #include "network/race_event_manager.hpp" #include "network/stk_host.hpp" #include "network/stk_peer.hpp" @@ -85,7 +87,7 @@ void ClientLobby::setAddress(const TransportAddress &address) void ClientLobby::setup() { - m_game_setup = STKHost::get()->setupNewGame(); // create a new game setup + LobbyProtocol::setup(); m_state = NONE; } // setup @@ -446,6 +448,7 @@ void ClientLobby::connectionAccepted(Event* event) Log::info("ClientLobby", "The server accepted the connection."); STKHost::get()->setMyHostId(data.getUInt32()); + // For now no split screen so only 1 player m_game_setup->setNumLocalPlayers(1); // connection token uint32_t token = data.getToken(); @@ -596,18 +599,7 @@ void ClientLobby::kartSelectionRefused(Event* event) void ClientLobby::kartSelectionUpdate(Event* event) { if(!checkDataSize(event, 3)) return; - const NetworkString &data = event->data(); - uint8_t player_id = data.getUInt8(); - std::string kart_name; - data.decodeString(&kart_name); - if (!m_game_setup->isKartAvailable(kart_name)) - { - Log::error("ClientLobby", - "The updated kart is taken already."); - } - m_game_setup->setPlayerKart(player_id, kart_name); - NetworkKartSelectionScreen::getInstance()->playerSelected(player_id, - kart_name); + } // kartSelectionUpdate //----------------------------------------------------------------------------- @@ -725,7 +717,7 @@ void ClientLobby::exitResultScreen(Event *event) { RaceResultGUI::getInstance()->backToLobby(); // Will be reset to linked if connected to server, see update(float dt) - m_game_setup = STKHost::get()->setupNewGame(); + LobbyProtocol::setup(); STKHost::get()->getServerPeerForClient()->unsetClientServerToken(); // stop race protocols auto pm = ProtocolManager::lock(); @@ -873,19 +865,8 @@ void ClientLobby::playerLapsVote(Event* event) */ void ClientLobby::finishedLoadingWorld() { - assert(STKHost::get()->getPeerCount() == 1); - std::vector players = - STKHost::get()->getMyPlayerProfiles(); - NetworkString *ns = getNetworkString(2); + NetworkString* ns = getNetworkString(1); ns->addUInt8(LE_CLIENT_LOADED_WORLD); - ns->addUInt8( uint8_t(players.size()) ) ; - for (unsigned int i = 0; i < players.size(); i++) - { - ns->addUInt8(players[i]->getGlobalPlayerId()); - Log::info("ClientLobby", - "Player %d ready, notifying server.", - players[i]->getGlobalPlayerId()); - } // for i < players.size() - sendToServer(ns, /*reliable*/true); + sendToServer(ns, true); delete ns; } // finishedLoadingWorld diff --git a/src/network/protocols/game_events_protocol.cpp b/src/network/protocols/game_events_protocol.cpp index 862766cfb..d03f0f3c7 100644 --- a/src/network/protocols/game_events_protocol.cpp +++ b/src/network/protocols/game_events_protocol.cpp @@ -176,7 +176,7 @@ void GameEventsProtocol::clientHasStarted() * ready set go. */ void GameEventsProtocol::receivedClientHasStarted(Event *event) { - assert(NetworkConfig::get()->isServer()); +/* assert(NetworkConfig::get()->isServer()); m_count_ready_clients++; Log::verbose("GameEvent", "Host %d has started ready-set-go: %d out of %d done", @@ -189,5 +189,5 @@ void GameEventsProtocol::receivedClientHasStarted(Event *event) // SIgnal the server to start now - since it is now behind the client // times by the latency of the 'slowest' client. World::getWorld()->startReadySetGo(); - } + }*/ } // receivedClientHasStarted diff --git a/src/network/protocols/lobby_protocol.cpp b/src/network/protocols/lobby_protocol.cpp index e1f7a444c..2c68388db 100644 --- a/src/network/protocols/lobby_protocol.cpp +++ b/src/network/protocols/lobby_protocol.cpp @@ -22,14 +22,15 @@ #include "input/input_manager.hpp" #include "input/device_manager.hpp" #include "modes/world.hpp" +#include "network/game_setup.hpp" #include "network/network_player_profile.hpp" #include "network/protocol_manager.hpp" #include "network/protocols/game_protocol.hpp" #include "network/protocols/game_events_protocol.hpp" #include "network/protocols/latency_protocol.hpp" +#include "network/race_config.hpp" #include "network/race_event_manager.hpp" #include "network/rewind_manager.hpp" -#include "network/stk_host.hpp" #include "race/race_manager.hpp" #include "states_screens/state_manager.hpp" @@ -44,6 +45,8 @@ LobbyProtocol::LobbyProtocol(CallbackObject* callback_object) // ---------------------------------------------------------------------------- LobbyProtocol::~LobbyProtocol() { + if (m_game_setup) + delete m_game_setup; } // ~LobbyProtocol //----------------------------------------------------------------------------- @@ -73,7 +76,7 @@ void LobbyProtocol::loadWorld() // Create the kart information for the race manager: // ------------------------------------------------- - std::vector players = m_game_setup->getPlayers(); + /*std::vector players = m_game_setup->getPlayers(); int local_player_id = 0; for (unsigned int i = 0; i < players.size(); i++) { @@ -95,7 +98,7 @@ void LobbyProtocol::loadWorld() // corresponding device associated with it). RemoteKartInfo rki(is_local ? local_player_id : i - local_player_id - + STKHost::get()->getGameSetup()->getNumLocalPlayers(), + + m_game_setup->getNumLocalPlayers(), profile->getKartName(), profile->getName(), profile->getHostId(), @@ -110,7 +113,7 @@ void LobbyProtocol::loadWorld() // Inform the race manager about the data for this kart. race_manager->setPlayerKart(i, rki); - } // for i in players + } // for i in players*/ // Make sure that if there is only a single local player this player can // use all input devices. @@ -137,3 +140,14 @@ void LobbyProtocol::terminateLatencyProtocol() { ProtocolManager::lock()->findAndTerminate(PROTOCOL_SYNCHRONIZATION); } // stopLatencyProtocol + +//----------------------------------------------------------------------------- +/** A previous GameSetup is deleted and a new one is created. + * \return Newly create GameSetup object. + */ +void LobbyProtocol::setup() +{ + if (m_game_setup) + delete m_game_setup; + m_game_setup = new GameSetup(); +} // setupNewGame diff --git a/src/network/protocols/lobby_protocol.hpp b/src/network/protocols/lobby_protocol.hpp index 8f90036da..8782a5e14 100644 --- a/src/network/protocols/lobby_protocol.hpp +++ b/src/network/protocols/lobby_protocol.hpp @@ -20,10 +20,10 @@ #define LOBBY_PROTOCOL_HPP #include "network/protocol.hpp" - -#include "network/game_setup.hpp" #include "network/network_string.hpp" +class GameSetup; + /*! * \class LobbyProtocol * \brief Base class for both client and server lobby. The lobbies are started @@ -78,7 +78,7 @@ public: protected: static std::weak_ptr m_lobby; - /** The game setup. */ + /** Stores data about the online game to play. */ GameSetup* m_game_setup; public: @@ -109,7 +109,7 @@ public: LobbyProtocol(CallbackObject* callback_object); virtual ~LobbyProtocol(); - virtual void setup() = 0; + virtual void setup(); virtual void update(float dt) = 0; virtual void finishedLoadingWorld() = 0; virtual void loadWorld(); @@ -120,6 +120,7 @@ public: { assert(false); // Only defined in client }; + GameSetup* getGameSetup() const { return m_game_setup; } }; // class LobbyProtocol diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index dab4f346c..dbb061de9 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -22,11 +22,13 @@ #include "karts/kart_properties_manager.hpp" #include "modes/world.hpp" #include "network/event.hpp" +#include "network/game_setup.hpp" #include "network/network_config.hpp" #include "network/network_player_profile.hpp" #include "network/protocols/connect_to_peer.hpp" #include "network/protocols/latency_protocol.hpp" #include "network/protocol_manager.hpp" +#include "network/race_config.hpp" #include "network/race_event_manager.hpp" #include "network/stk_host.hpp" #include "network/stk_peer.hpp" @@ -42,6 +44,7 @@ #include "utils/time.hpp" #include +#include #include /** This is the central game setup protocol running in the server. It is @@ -102,9 +105,9 @@ ServerLobby::~ServerLobby() } // ~ServerLobby //----------------------------------------------------------------------------- - void ServerLobby::setup() { + LobbyProtocol::setup(); // We use maximum 16bit unsigned limit auto all_k = kart_properties_manager->getAllAvailableKarts(); auto all_t = track_manager->getAllTrackIdentifiers(); @@ -116,24 +119,14 @@ void ServerLobby::setup() m_available_kts.second = { all_t.begin(), all_t.end() }; m_server_registered = false; - m_game_setup = STKHost::get()->setupNewGame(); m_game_setup->setNumLocalPlayers(0); // no local players on a server - m_next_player_id.setAtomic(0); - m_selection_enabled = false; - Log::info("ServerLobby", "Starting the protocol."); // Initialise the data structures to detect if all clients and // the server are ready: - m_player_states.clear(); - m_client_ready_count.setAtomic(0); - m_server_has_loaded_world = false; - const std::vector &players = - m_game_setup->getPlayers(); - for (unsigned int i = 0; i < players.size(); i++) - { - m_player_states[players[i]->getGlobalPlayerId()] = false; - } - + m_server_has_loaded_world.store(false); + m_peers_ready.clear(); + m_server_delay = 0.0; + Log::info("ServerLobby", "Reset server to initial state."); } // setup //----------------------------------------------------------------------------- @@ -302,12 +295,55 @@ void ServerLobby::asynchronousUpdate() STKHost::get()->requestShutdown(); break; } + case WAIT_FOR_WORLD_LOADED: + { + // m_server_has_loaded_world is set by main thread with atomic write + if (m_server_has_loaded_world.load() == false) + return; + if (!checkPeersReady()) + return; + m_state = WAIT_FOR_RACE_STARTED; + // Reset for next state usage + for (auto p : m_peers_ready) + { + p.second = false; + } + signalRaceStartToClients(); + break; + } + case WAIT_FOR_RACE_STARTED: + // The function startedRaceOnClient() will trigger the + // next state. + break; + case DELAY_SERVER: + if (m_server_delay < StkTime::getRealTime()) + { + Log::verbose("ServerLobby", "End delay at %lf", + StkTime::getRealTime()); + m_state = RACING; + World::getWorld()->setReadyToRace(); + } default: break; } } // asynchronousUpdate +//----------------------------------------------------------------------------- +bool ServerLobby::checkPeersReady() const +{ + bool all_ready = true; + for (auto p : m_peers_ready) + { + if (p.first.expired()) + continue; + all_ready = all_ready && p.second; + if (!all_ready) + return false; + } + return true; +} // checkPeersReady + //----------------------------------------------------------------------------- /** Simple finite state machine. Once this * is known, register the server and its address with the stk server so that @@ -317,12 +353,20 @@ void ServerLobby::update(float dt) { // Check if server owner has left updateServerOwner(); - + if (m_game_setup) + { + // Remove disconnected players if in these two states + m_game_setup->update(m_state.load() == ACCEPTING_CLIENTS || + m_state.load() == SELECTING); + } switch (m_state.load()) { case SET_PUBLIC_ADDRESS: case REGISTER_SELF_ADDRESS: case ACCEPTING_CLIENTS: + case WAIT_FOR_WORLD_LOADED: + case WAIT_FOR_RACE_STARTED: + case DELAY_SERVER: { // Waiting for asynchronousUpdate break; @@ -337,33 +381,6 @@ void ServerLobby::update(float dt) loadWorld(); m_state = WAIT_FOR_WORLD_LOADED; break; - case WAIT_FOR_WORLD_LOADED: - // Note that m_server_has_loaded_world is called by the main thread - // (same as the thread updating this protocol) - m_client_ready_count.lock(); - if (m_server_has_loaded_world && - m_client_ready_count.getData() == m_game_setup->getPlayerCount()) - { - signalRaceStartToClients(); - m_client_ready_count.getData() = 0; - } - m_client_ready_count.unlock(); - // Initialise counter again, to wait for all clients to indicate that - // they have started the race/ - break; - case WAIT_FOR_RACE_STARTED: - // The function startedRaceOnClient() will trigger the - // next state. - break; - case DELAY_SERVER: - if (m_server_delay < StkTime::getRealTime()) - { - Log::verbose("ServerLobby", "End delay at %lf", - StkTime::getRealTime()); - m_state = RACING; - World::getWorld()->setReadyToRace(); - } - break; case RACING: if (World::getWorld() && RaceEventManager::getInstance()->isRunning()) @@ -374,16 +391,6 @@ void ServerLobby::update(float dt) case RESULT_DISPLAY: if(StkTime::getRealTime() > m_timeout) { - // Send a notification to all clients to exit - // the race result screen - NetworkString *exit_result_screen = getNetworkString(1); - exit_result_screen->setSynchronous(true); - exit_result_screen->addUInt8(LE_EXIT_RESULT); - sendMessageToPeersChangingToken(exit_result_screen, - /*reliable*/true); - delete exit_result_screen; - m_state = NetworkConfig::get()->isLAN() ? - ACCEPTING_CLIENTS : REGISTER_SELF_ADDRESS; RaceResultGUI::getInstance()->backToLobby(); // notify the network world that it is stopped RaceEventManager::getInstance()->stop(); @@ -394,6 +401,16 @@ void ServerLobby::update(float dt) pm->findAndTerminate(PROTOCOL_KART_UPDATE); pm->findAndTerminate(PROTOCOL_GAME_EVENTS); setup(); + m_state = NetworkConfig::get()->isLAN() ? + ACCEPTING_CLIENTS : REGISTER_SELF_ADDRESS; + // Send a notification to all clients to exit + // the race result screen + NetworkString *exit_result_screen = getNetworkString(1); + exit_result_screen->setSynchronous(true); + exit_result_screen->addUInt8(LE_EXIT_RESULT); + sendMessageToPeersChangingToken(exit_result_screen, + /*reliable*/true); + delete exit_result_screen; } break; case ERROR_LEAVE: @@ -502,7 +519,6 @@ void ServerLobby::signalRaceStartToClients() ns->addUInt8(LE_START_RACE); sendMessageToPeersChangingToken(ns, /*reliable*/true); delete ns; - m_state = WAIT_FOR_RACE_STARTED; } // startGame //----------------------------------------------------------------------------- @@ -555,8 +571,6 @@ void ServerLobby::startSelection(const Event *event) sendMessageToPeersChangingToken(ns, /*reliable*/true); delete ns; - m_selection_enabled = true; - m_state = SELECTING; WaitingForOthersScreen::getInstance()->push(); @@ -591,7 +605,7 @@ void ServerLobby::checkIncomingConnectionRequests() const TransportAddress &addr = STKHost::get()->getPublicAddress(); request->addParameter("address", addr.getIP() ); request->addParameter("port", addr.getPort()); - request->addParameter("current_players", STKHost::get()->getPeerCount()); + request->addParameter("current_players", m_game_setup->getPlayerCount()); request->executeNow(); assert(request->isDone()); @@ -684,7 +698,9 @@ void ServerLobby::clientDisconnected(Event* event) msg->addUInt8((uint8_t)players_on_peer.size()); for (auto p : players_on_peer) { - msg->encodeString(p->getName()); + std::string name = StringUtils::wideToUtf8(p->getName()); + msg->encodeString(name); + Log::info("ServerLobby", "%s disconnected", name.c_str()); } sendMessageToPeersChangingToken(msg, /*reliable*/true); updatePlayerList(); @@ -852,6 +868,9 @@ void ServerLobby::connectionRequested(Event* event) peer->sendPacket(message_ack); delete message_ack; + m_peers_ready[peer] = false; + for (std::shared_ptr npp : peer->getPlayerProfiles()) + m_game_setup->addPlayer(npp); updatePlayerList(); Log::verbose("ServerLobby", "New player."); @@ -922,74 +941,41 @@ void ServerLobby::updateServerOwner() } // updateServerOwner //----------------------------------------------------------------------------- -/*! \brief Called when a player asks to select a kart. +/*! \brief Called when a player asks to select karts. * \param event : Event providing the information. - * - * Format of the data : - * Byte 0 1 2 - * ---------------------------------------------- - * Size | 1 | 1 | N | - * Data |player id | N (kart name size) | kart name | - * ---------------------------------------------- */ void ServerLobby::kartSelectionRequested(Event* event) { - if(m_state!=SELECTING) + std::lock_guard lock(m_connection_mutex); + if (m_state != SELECTING) { - Log::warn("Server", "Received kart selection while in state %d.", + Log::warn("ServerLobby", "Received kart selection while in state %d.", m_state.load()); return; } if (!checkDataSize(event, 1)) return; - - const NetworkString &data = event->data(); + const NetworkString& data = event->data(); STKPeer* peer = event->getPeer(); - - uint8_t player_id = data.getUInt8(); - std::string kart_name; - data.decodeString(&kart_name); - // check if selection is possible - if (!m_selection_enabled) + unsigned player_count = data.getUInt8(); + for (unsigned i = 0; i < player_count; i++) { - NetworkString *answer = getNetworkString(2); - // selection still not started - answer->addUInt8(LE_KART_SELECTION_REFUSED).addUInt8(2); - peer->sendPacket(answer); - delete answer; - return; + std::string kart; + data.decodeString(&kart); + if (m_available_kts.first.find(kart) == m_available_kts.first.end()) + { + Log::debug("ServerLobby", "Player %d from peer %d chose unknown " + "kart %s, use a random one", i, peer->getHostId(), + kart.c_str()); + RandomGenerator rg; + std::set::iterator it = m_available_kts.first.begin(); + std::advance(it, rg.get((int)m_available_kts.first.size())); + kart = *it; + } + peer->getPlayerProfiles()[i]->setKartName(kart); + Log::verbose("ServerLobby", "Player %d from peer %d chose %s", i, + peer->getHostId(), kart.c_str()); } - // check if somebody picked that kart - if (!m_game_setup->isKartAvailable(kart_name)) - { - NetworkString *answer = getNetworkString(2); - // kart is already taken - answer->addUInt8(LE_KART_SELECTION_REFUSED).addUInt8(0); - peer->sendPacket(answer); - delete answer; - return; - } - // check if this kart is authorized - if (!m_game_setup->isKartAllowed(kart_name)) - { - NetworkString *answer = getNetworkString(2); - // kart is not authorized - answer->addUInt8(LE_KART_SELECTION_REFUSED).addUInt8(1); - peer->sendPacket(answer); - delete answer; - return; - } - - // send a kart update to everyone - NetworkString *answer = getNetworkString(3+kart_name.size()); - // This message must be handled synchronously on the client. - answer->setSynchronous(true); - // kart update (3), 1, race id - answer->addUInt8(LE_KART_SELECTION_UPDATE).addUInt8(player_id) - .encodeString(kart_name); - sendMessageToPeersChangingToken(answer); - delete answer; - m_game_setup->setPlayerKart(player_id, kart_name); } // kartSelectionRequested //----------------------------------------------------------------------------- @@ -1179,7 +1165,7 @@ void ServerLobby::playerLapsVote(Event* event) */ void ServerLobby::finishedLoadingWorld() { - m_server_has_loaded_world = true; + m_server_has_loaded_world.store(true); } // finishedLoadingWorld; //----------------------------------------------------------------------------- @@ -1188,30 +1174,10 @@ void ServerLobby::finishedLoadingWorld() */ void ServerLobby::finishedLoadingWorldClient(Event *event) { - if (!checkDataSize(event, 1)) return; - - const NetworkString &data = event->data(); - uint8_t player_count = data.getUInt8(); - m_client_ready_count.lock(); - for (unsigned int i = 0; i < player_count; i++) - { - uint8_t player_id = data.getUInt8(); - if (m_player_states[player_id]) - { - Log::error("ServerLobbyProtocol", - "Player %d send more than one ready message.", - player_id); - m_client_ready_count.unlock(); - return; - } - m_player_states[player_id] = true; - m_client_ready_count.getData()++; - Log::info("ServerLobbyeProtocol", "Player %d is ready (%d/%d).", - player_id, m_client_ready_count.getData(), - m_game_setup->getPlayerCount()); - } - m_client_ready_count.unlock(); - + std::shared_ptr peer = event->getPeerSP(); + m_peers_ready.at(peer) = true; + Log::info("ServerLobby", "Peer %d has finished loading world", + peer->getHostId()); } // finishedLoadingWorldClient //----------------------------------------------------------------------------- @@ -1225,20 +1191,19 @@ void ServerLobby::finishedLoadingWorldClient(Event *event) */ void ServerLobby::startedRaceOnClient(Event *event) { - m_client_ready_count.lock(); - Log::verbose("ServerLobby", "Host %d has started race at %lf.", - event->getPeer()->getHostId(), StkTime::getRealTime()); - m_client_ready_count.getData()++; - if (m_client_ready_count.getData() == m_game_setup->getPlayerCount()) + std::shared_ptr peer = event->getPeerSP(); + m_peers_ready.at(peer) = true; + Log::info("ServerLobby", "Peer %d has started race at %lf", + peer->getHostId(), StkTime::getRealTime()); + + if (checkPeersReady()) { m_state = DELAY_SERVER; - m_server_delay = StkTime::getRealTime() + 0.1f; + m_server_delay = StkTime::getRealTime() + 0.1; Log::verbose("ServerLobby", "Started delay at %lf set delay to %lf", - StkTime::getRealTime(), - m_server_delay); + StkTime::getRealTime(), m_server_delay); terminateLatencyProtocol(); } - m_client_ready_count.unlock(); } // startedRaceOnClient //----------------------------------------------------------------------------- diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 8a88ae5fa..2d5f6e192 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -3,9 +3,9 @@ #include "network/protocols/lobby_protocol.hpp" #include "utils/cpp2011.hpp" -#include "utils/synchronised.hpp" #include +#include #include #include #include @@ -42,19 +42,12 @@ private: * with data in server first. */ std::pair, std::set > m_available_kts; - /** Next id to assign to a peer. */ - Synchronised m_next_player_id; - /** Keeps track of the server state. */ - bool m_server_has_loaded_world; + std::atomic_bool m_server_has_loaded_world; - /** Counts how many clients have finished loading the world. */ - Synchronised m_client_ready_count; - - /** For debugging: keep track of the state (ready or not) of each player, - * to make sure no client/player reports more than once. Needs to be a - * map since the client IDs can be non-consecutive. */ - std::map m_player_states; + /** Counts how many peers have finished loading the world. */ + std::map, bool, + std::owner_less > > m_peers_ready; /** Keeps track of an artificial server delay (which makes sure that the * data from all clients has arrived when the server computes a certain @@ -62,8 +55,6 @@ private: * seconds), which is the real time at which the server should start. */ double m_server_delay; - bool m_selection_enabled; - bool m_has_created_server_id_file; /** It indicates if this server is registered with the stk server. */ @@ -101,6 +92,7 @@ private: void createServerIdFile(); void updatePlayerList(); void updateServerOwner(); + bool checkPeersReady() const; public: ServerLobby(); diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index 9d2c33075..c93269df9 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -21,6 +21,7 @@ #include "config/user_config.hpp" #include "io/file_manager.hpp" #include "network/event.hpp" +#include "network/game_setup.hpp" #include "network/network_config.hpp" #include "network/network_console.hpp" #include "network/network_string.hpp" @@ -72,8 +73,9 @@ void STKHost::create(std::shared_ptr server, SeparateProcess* p) assert(m_stk_host == NULL); if (NetworkConfig::get()->isServer()) { + std::shared_ptr sl = LobbyProtocol::create(); m_stk_host = new STKHost(NetworkConfig::get()->getServerName()); - LobbyProtocol::create()->requestStart(); + sl->requestStart(); } else { @@ -319,7 +321,6 @@ void STKHost::init() m_shutdown = false; m_authorised = false; m_network = NULL; - m_game_setup = NULL; m_exit_timeout.store(std::numeric_limits::max()); // Start with initialising ENet @@ -351,10 +352,6 @@ STKHost::~STKHost() requestShutdown(); if (m_network_console.joinable()) m_network_console.join(); - // delete the game setup - if (m_game_setup) - delete m_game_setup; - m_game_setup = NULL; disconnectAllPeers(true/*timeout_waiting*/); Network::closeLog(); @@ -607,18 +604,6 @@ void STKHost::setPrivatePort() m_private_port = ntohs(sin.sin_port); } // setPrivatePort -//----------------------------------------------------------------------------- -/** A previous GameSetup is deletea and a new one is created. - * \return Newly create GameSetup object. - */ -GameSetup* STKHost::setupNewGame() -{ - if (m_game_setup) - delete m_game_setup; - m_game_setup = new GameSetup(); - return m_game_setup; -} // setupNewGame - //----------------------------------------------------------------------------- /** Disconnect all connected peers. */ @@ -730,7 +715,7 @@ void STKHost::mainLoop() auto sl = LobbyProtocol::get(); if (direct_socket && sl && sl->waitingForPlayers()) { - handleDirectSocketRequest(direct_socket); + handleDirectSocketRequest(direct_socket, sl); } // if discovery host std::list sl) { const int LEN=2048; char buffer[LEN]; @@ -877,7 +863,7 @@ void STKHost::handleDirectSocketRequest(Network* direct_socket) s.addUInt8(NetworkConfig::m_server_version); s.encodeString(name); s.addUInt8(NetworkConfig::get()->getMaxPlayers()); - s.addUInt8(0); // FIXME: current number of connected players + s.addUInt8((uint8_t)sl->getGameSetup()->getPlayerCount()); s.addUInt16(m_private_port); s.addUInt8((uint8_t)race_manager->getDifficulty()); s.addUInt8((uint8_t) @@ -938,12 +924,6 @@ bool STKHost::peerExists(const TransportAddress& peer) return false; } // peerExists -// ---------------------------------------------------------------------------- -std::vector STKHost::getMyPlayerProfiles() -{ - return m_game_setup->getAllPlayersOnHost(m_host_id); -} // getMyPlayerProfiles - // ---------------------------------------------------------------------------- /** \brief Return the only server peer for client. * \return STKPeer the STKPeer of server. diff --git a/src/network/stk_host.hpp b/src/network/stk_host.hpp index 32cddb689..e04d4dca2 100644 --- a/src/network/stk_host.hpp +++ b/src/network/stk_host.hpp @@ -45,6 +45,7 @@ class GameSetup; class NetworkPlayerProfile; class Server; +class ServerLobby; class SeparateProcess; enum ENetCommandType : unsigned int @@ -104,9 +105,6 @@ private: /** Host id of this host. */ uint32_t m_host_id = 0; - /** Stores data about the online game to play. */ - GameSetup* m_game_setup; - /** Id of thread listening to enet events. */ std::thread m_listening_thread; @@ -133,17 +131,22 @@ private: /** The private port enet socket is bound. */ uint16_t m_private_port; - STKHost(std::shared_ptr server); - STKHost(const irr::core::stringw &server_name); - virtual ~STKHost(); + // ------------------------------------------------------------------------ + STKHost(std::shared_ptr server); + // ------------------------------------------------------------------------ + STKHost(const irr::core::stringw &server_name); + // ------------------------------------------------------------------------ + ~STKHost(); + // ------------------------------------------------------------------------ void init(); - void handleDirectSocketRequest(Network* direct_socket); + // ------------------------------------------------------------------------ + void handleDirectSocketRequest(Network* direct_socket, + std::shared_ptr sl); // ------------------------------------------------------------------------ void mainLoop(); public: - /** If a network console should be started. Note that the console can cause - * a crash in release mode on windows (see #1529). */ + /** If a network console should be started. */ static bool m_enable_console; /** Creates the STKHost. It takes all confifguration parameters from @@ -181,8 +184,6 @@ public: // ------------------------------------------------------------------------ void setPublicAddress(); // ------------------------------------------------------------------------ - GameSetup* setupNewGame(); - // ------------------------------------------------------------------------ void disconnectAllPeers(bool timeout_waiting = false); // ------------------------------------------------------------------------ bool connect(const TransportAddress& peer); @@ -217,19 +218,25 @@ public: // ------------------------------------------------------------------------ std::shared_ptr findPeerByHostId(uint32_t id) const; // ------------------------------------------------------------------------ - void sendPacketExcept(STKPeer* peer, - NetworkString *data, + void sendPacketExcept(STKPeer* peer, NetworkString *data, bool reliable = true); - void setupClient(int peer_count, int channel_limit, - uint32_t max_incoming_bandwidth, - uint32_t max_outgoing_bandwidth); - void startListening(); - void stopListening(); - bool peerExists(const TransportAddress& peer_address); - bool isConnectedTo(const TransportAddress& peer_address); + // ------------------------------------------------------------------------ + void setupClient(int peer_count, int channel_limit, + uint32_t max_incoming_bandwidth, + uint32_t max_outgoing_bandwidth); + // ------------------------------------------------------------------------ + void startListening(); + // ------------------------------------------------------------------------ + void stopListening(); + // ------------------------------------------------------------------------ + bool peerExists(const TransportAddress& peer_address); + // ------------------------------------------------------------------------ + bool isConnectedTo(const TransportAddress& peer_address); + // ------------------------------------------------------------------------ std::shared_ptr getServerPeerForClient() const; - std::vector getMyPlayerProfiles(); - void setErrorMessage(const irr::core::stringw &message); + // ------------------------------------------------------------------------ + void setErrorMessage(const irr::core::stringw &message); + // ------------------------------------------------------------------------ void addEnetCommand(ENetPeer* peer, ENetPacket* packet, uint32_t i, ENetCommandType ect) { @@ -245,9 +252,6 @@ public: * requested. */ bool requestedShutdown() const { return m_shutdown.load(); } // ------------------------------------------------------------------------ - /** Returns the current game setup. */ - GameSetup* getGameSetup() { return m_game_setup; } - // ------------------------------------------------------------------------ int receiveRawPacket(char *buffer, int buffer_len, TransportAddress* sender, int max_tries = -1) { diff --git a/src/network/stk_peer.hpp b/src/network/stk_peer.hpp index e7d6d0b62..d76dd1048 100644 --- a/src/network/stk_peer.hpp +++ b/src/network/stk_peer.hpp @@ -86,8 +86,8 @@ public: bool isSamePeer(const STKPeer* peer) const; bool isSamePeer(const ENetPeer* peer) const; // ------------------------------------------------------------------------ - std::vector > - getPlayerProfiles() const { return m_players; } + std::vector >& getPlayerProfiles() + { return m_players; } // ------------------------------------------------------------------------ bool hasPlayerProfiles() const { return !m_players.empty(); } // ------------------------------------------------------------------------ diff --git a/src/states_screens/network_kart_selection.cpp b/src/states_screens/network_kart_selection.cpp index 6ab37df02..39f5fcd79 100644 --- a/src/states_screens/network_kart_selection.cpp +++ b/src/states_screens/network_kart_selection.cpp @@ -27,6 +27,7 @@ #include "items/item_manager.hpp" #include "karts/kart_properties.hpp" #include "karts/kart_properties_manager.hpp" +#include "network/game_setup.hpp" #include "network/network_config.hpp" #include "network/network_player_profile.hpp" #include "network/protocol_manager.hpp" @@ -71,16 +72,13 @@ void NetworkKartSelectionScreen::init() back_button->setImage("gui/main_quit.png"); // add a widget for each player except self (already exists): - GameSetup* setup = STKHost::get()->getGameSetup(); - if (!setup) + GameSetup* game_setup = LobbyProtocol::get()->getGameSetup(); + if (!game_setup) { Log::error("NetworkKartSelectionScreen", "No network game setup registered."); return; } - std::vector players = setup->getPlayers(); - - Log::info("NKSS", "There are %d players", players.size()); // ---- Get available area for karts // make a copy of the area, ands move it to be outside the screen Widget* kartsAreaWidget = getWidget("playerskarts"); @@ -90,7 +88,6 @@ void NetworkKartSelectionScreen::init() kartsAreaWidget->m_y, kartsAreaWidget->m_x + shift + kartsAreaWidget->m_w, kartsAreaWidget->m_y + kartsAreaWidget->m_h); - GameSetup *game_setup = STKHost::get()->getGameSetup(); // FIXME: atm only adds the local master, split screen supports // needs to be added @@ -115,7 +112,6 @@ void NetworkKartSelectionScreen::init() assert(w != NULL); w->setSelection(UserConfigParams::m_default_kart, /*player id*/0, /*focus*/true); playerConfirm(0); - RaceSetupScreen::getInstance()->push(); } } // init @@ -142,81 +138,21 @@ void NetworkKartSelectionScreen::playerConfirm(const int playerID) SFXManager::get()->quickSound( "anvil" ); return; } - if(playerID == PLAYER_ID_GAME_MASTER) // self + if (playerID == PLAYER_ID_GAME_MASTER) // self { - auto clrp = LobbyProtocol::get(); - assert(clrp); // FIXME SPLITSCREEN: we need to supply the global player id of the // player selecting the kart here. For now ... just vote the same kart // for each local player. - std::vector players = - STKHost::get()->getMyPlayerProfiles(); - for(unsigned int i=0; irequestKartSelection(players[i]->getGlobalPlayerId(), - selection ); - } + uint8_t player_count = 1; + NetworkString kart(PROTOCOL_LOBBY_ROOM); + kart.addUInt8(LobbyProtocol::LE_KART_SELECTION).addUInt8(player_count) + .encodeString(selection); + STKHost::get()->sendToServer(&kart, true); input_manager->getDeviceManager()->setAssignMode(ASSIGN); + TracksScreen::getInstance()->push(); } } // playerConfirm -// ---------------------------------------------------------------------------- -void NetworkKartSelectionScreen::playerSelected(uint8_t player_id, - const std::string &kart_name) -{ - int widget_id = -1; - for (unsigned int i = 0; i < m_id_mapping.size(); i++) - { - Log::info("NKSS", "Checking race id %d : mapped of %d is %d", - player_id, i, m_id_mapping[i]); - if (m_id_mapping[i] == player_id) - widget_id = i; - } - - // This selection was for a remote kart, which is not shown - // Just ignore it. - if(widget_id==-1) - return; - - // In case of auto-connect the screen is already replaced, so - // m_kart_widget is empty. - if (! STKHost::get()->isAuthorisedToControl()) - { - KartSelectionScreen::updateKartWidgetModel(widget_id, kart_name, - irr::core::stringw(kart_name.c_str()), - /*Todo get color*/0.0f); - KartSelectionScreen::updateKartStats(widget_id, kart_name); - m_kart_widgets[widget_id].setKartInternalName(kart_name); - m_kart_widgets[widget_id].markAsReady(); // mark player ready - } - - // If this is the authorised client, send the currently set race config - // to the server. - if(STKHost::get()->isAuthorisedToControl()) - { - auto clrp = LobbyProtocol::get(); - assert(clrp); - // FIXME: for now we submit a vote from the authorised user - // for the various modes based on the settings in the race manager. - // This needs more/better gui elements (and some should be set when - // defining the server). - std::vector players = - STKHost::get()->getMyPlayerProfiles(); - for(unsigned int i=0; igetGlobalPlayerId(); - clrp->voteMajor(id, race_manager->getMajorMode()); - clrp->voteMinor(id, race_manager->getMinorMode()); - clrp->voteReversed(id, race_manager->getReverseTrack()); - clrp->voteRaceCount(id, 1); - clrp->voteLaps(id, 1); - } - //WaitingForOthersScreen::getInstance()->push(); - //return; - } - TracksScreen::getInstance()->push(); -} // playerSelected - // ---------------------------------------------------------------------------- bool NetworkKartSelectionScreen::onEscapePressed() { diff --git a/src/states_screens/network_kart_selection.hpp b/src/states_screens/network_kart_selection.hpp index 0c714e2ca..4e87cb25c 100644 --- a/src/states_screens/network_kart_selection.hpp +++ b/src/states_screens/network_kart_selection.hpp @@ -50,8 +50,6 @@ public: } virtual void init() OVERRIDE; virtual bool onEscapePressed() OVERRIDE; - virtual void playerSelected(uint8_t player_id, - const std::string &kart_name); }; #endif // NETWORK_KART_SELECTION_HPP diff --git a/src/states_screens/tracks_screen.cpp b/src/states_screens/tracks_screen.cpp index c527f2e4e..1dbfac7bc 100644 --- a/src/states_screens/tracks_screen.cpp +++ b/src/states_screens/tracks_screen.cpp @@ -96,13 +96,13 @@ void TracksScreen::eventCallback(Widget* widget, const std::string& name, // FIXME SPLITSCREEN: we need to supply the global player id of the // player selecting the track here. For now ... just vote the same // track for each local player. - std::vector players = + /*std::vector players = STKHost::get()->getMyPlayerProfiles(); for(unsigned int i=0; ivoteTrack(players[i]->getGlobalPlayerId(),selection); } - WaitingForOthersScreen::getInstance()->push(); + WaitingForOthersScreen::getInstance()->push();*/ } else { diff --git a/src/states_screens/waiting_for_others.cpp b/src/states_screens/waiting_for_others.cpp index ab9e72574..0dab10fdb 100644 --- a/src/states_screens/waiting_for_others.cpp +++ b/src/states_screens/waiting_for_others.cpp @@ -16,89 +16,38 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "states_screens/waiting_for_others.hpp" - -#include "config/user_config.hpp" #include "guiengine/widget.hpp" #include "guiengine/widgets/label_widget.hpp" -#include "input/device_manager.hpp" -#include "input/input_manager.hpp" -#include "input/keyboard_device.hpp" -#include "karts/kart_properties_manager.hpp" -#include "network/game_setup.hpp" -#include "network/network_player_profile.hpp" -#include "network/stk_host.hpp" -#include "network/stk_peer.hpp" -#include "race/race_manager.hpp" -#include "states_screens/state_manager.hpp" using namespace GUIEngine; DEFINE_SCREEN_SINGLETON( WaitingForOthersScreen ); // ----------------------------------------------------------------------------- - -WaitingForOthersScreen::WaitingForOthersScreen() : Screen("online/waiting_for_others.stkgui") +WaitingForOthersScreen::WaitingForOthersScreen() + : Screen("online/waiting_for_others.stkgui") { } // WaitingForOthersScreen // ----------------------------------------------------------------------------- - void WaitingForOthersScreen::loadedFromFile() { } // loadedFromFile // ----------------------------------------------------------------------------- - -void WaitingForOthersScreen::eventCallback(Widget* widget, const std::string& name, const int playerID) +void WaitingForOthersScreen::eventCallback(Widget* widget, + const std::string& name, + const int player_id) { } // eventCallback // ----------------------------------------------------------------------------- - void WaitingForOthersScreen::init() { Screen::init(); } //init // ----------------------------------------------------------------------------- - void WaitingForOthersScreen::onUpdate(float dt) { - const GameSetup *setup = STKHost::get()->getGameSetup(); - const std::vector &all_profiles = setup->getPlayers(); - - RaceConfig* config = STKHost::get()->getGameSetup()->getRaceConfig(); - core::stringw w; - for (unsigned int i = 0; i < all_profiles.size(); i++) - { - const NetworkPlayerProfile* profile = all_profiles[i]; - if (profile == NULL) - continue; - core::stringw name = profile->getName(); - - - int playerId = profile->getGlobalPlayerId(); - const std::string &kart = profile->getKartName(); - if (!kart.empty()) - name += StringUtils::insertValues(L" (%s)", core::stringw(kart.c_str())); - - w += name + L" : "; - const RaceVote& vote = config->getRaceVote(playerId); - if (vote.hasVotedTrack()) - { - w += vote.getTrackVote().c_str(); - } - else - { - w += L"..."; - } - - w += "\n"; - } - - GUIEngine::LabelWidget* lbl = getWidget("lblDetails"); - lbl->setText(w.c_str(), true); } - -// ----------------------------------------------------------------------------- - From 60d822da5073733f51fe3fe3b9c5cf9c27f9deb1 Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 15 Mar 2018 00:34:59 +0800 Subject: [PATCH 223/413] Only broadcast aloha to self if address is really localhost --- src/network/protocols/connect_to_peer.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/network/protocols/connect_to_peer.cpp b/src/network/protocols/connect_to_peer.cpp index 7bc7eda7b..8f4584a6c 100644 --- a/src/network/protocols/connect_to_peer.cpp +++ b/src/network/protocols/connect_to_peer.cpp @@ -123,13 +123,16 @@ void ConnectToPeer::asynchronousUpdate() BareNetworkString aloha(std::string("aloha_stk")); STKHost::get()->sendRawPacket(aloha, broadcast_address); - Log::info("ConnectToPeer", "Broadcast aloha sent."); + Log::verbose("ConnectToPeer", "Broadcast aloha sent."); StkTime::sleep(1); - broadcast_address.setIP(0x7f000001); // 127.0.0.1 (localhost) - broadcast_address.setPort(m_peer_address.getPort()); - STKHost::get()->sendRawPacket(aloha, broadcast_address); - Log::info("ConnectToPeer", "Broadcast aloha to self."); + if (m_peer_address.isPublicAddressLocalhost()) + { + broadcast_address.setIP(0x7f000001); // 127.0.0.1 (localhost) + broadcast_address.setPort(m_peer_address.getPort()); + STKHost::get()->sendRawPacket(aloha, broadcast_address); + Log::verbose("ConnectToPeer", "Broadcast aloha to self."); + } // 20 seconds timeout if (m_tried_connection++ > 10) From 532bd88062045014f6f62fd17436f59233297822 Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 15 Mar 2018 00:41:01 +0800 Subject: [PATCH 224/413] Avoid possible packet loss The connect to peer done by server will auto terminate if same peer from same port has connected already --- src/network/protocols/request_connection.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/network/protocols/request_connection.cpp b/src/network/protocols/request_connection.cpp index 65fcf964a..d6d12f028 100644 --- a/src/network/protocols/request_connection.cpp +++ b/src/network/protocols/request_connection.cpp @@ -113,7 +113,14 @@ void RequestConnection::asynchronousUpdate() // Direct socket always listens on server discovery port server_addr.setPort(NetworkConfig::get() ->getServerDiscoveryPort()); - STKHost::get()->sendRawPacket(message, server_addr); + // Avoid possible packet loss, the connect to peer done by + // server will auto terminate if same peer from same port + // has connected already + for (int i = 0; i < 5; i++) + { + STKHost::get()->sendRawPacket(message, server_addr); + StkTime::sleep(1); + } m_state = DONE; } else From 2e981b33e70fed24d546244a5ea01af61d4691a8 Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Wed, 14 Mar 2018 21:26:44 -0400 Subject: [PATCH 225/413] Patch textbox crash --- src/guiengine/widgets/CGUIEditBox.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/guiengine/widgets/CGUIEditBox.cpp b/src/guiengine/widgets/CGUIEditBox.cpp index 39dec5ff8..a5e2ccb27 100644 --- a/src/guiengine/widgets/CGUIEditBox.cpp +++ b/src/guiengine/widgets/CGUIEditBox.cpp @@ -819,6 +819,9 @@ bool CGUIEditBox::processKey(const SEvent& event) calculateScrollPos(); + if (CursorPos > Text.size()) + CursorPos = Text.size(); + #if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) switch(event.KeyInput.Key) { From f7b3950cd40920fb37cfdda955a6703aaa31ff02 Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 15 Mar 2018 10:39:02 +0800 Subject: [PATCH 226/413] Fix cursor stop working after pressing enter --- src/guiengine/widgets/CGUIEditBox.cpp | 2 +- src/guiengine/widgets/text_box_widget.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/guiengine/widgets/CGUIEditBox.cpp b/src/guiengine/widgets/CGUIEditBox.cpp index a5e2ccb27..013bff741 100644 --- a/src/guiengine/widgets/CGUIEditBox.cpp +++ b/src/guiengine/widgets/CGUIEditBox.cpp @@ -819,7 +819,7 @@ bool CGUIEditBox::processKey(const SEvent& event) calculateScrollPos(); - if (CursorPos > Text.size()) + if (CursorPos > (s32)Text.size()) CursorPos = Text.size(); #if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) diff --git a/src/guiengine/widgets/text_box_widget.cpp b/src/guiengine/widgets/text_box_widget.cpp index e04819027..02d32c4ea 100644 --- a/src/guiengine/widgets/text_box_widget.cpp +++ b/src/guiengine/widgets/text_box_widget.cpp @@ -68,7 +68,10 @@ public: for (unsigned int n=0; n Date: Thu, 15 Mar 2018 12:37:02 +0800 Subject: [PATCH 227/413] Change for new server code in stk-addons --- src/network/server.cpp | 4 ++-- src/network/server.hpp | 7 +++---- src/states_screens/dialogs/server_info_dialog.cpp | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/network/server.cpp b/src/network/server.cpp index ef698e35e..14ed1b227 100644 --- a/src/network/server.cpp +++ b/src/network/server.cpp @@ -46,7 +46,7 @@ Server::Server(const XMLNode& xml) m_lower_case_name = StringUtils::toLowerCase(m_lower_case_name); xml.get("id", &m_server_id); - xml.get("hostid", &m_host_id); + xml.get("host_id", &m_server_owner); xml.get("max_players", &m_max_players); xml.get("current_players", &m_current_players); uint32_t ip; @@ -80,7 +80,7 @@ Server::Server(unsigned server_id, const core::stringw &name, int max_players, m_lower_case_name = StringUtils::toLowerCase(StringUtils::wideToUtf8(name)); m_satisfaction_score = 0; m_server_id = server_id; - m_host_id = 0; + m_server_owner = 0; m_current_players = current_players; m_max_players = max_players; m_address.copy(address); diff --git a/src/network/server.hpp b/src/network/server.hpp index 0ee2339dd..e47b4264c 100644 --- a/src/network/server.hpp +++ b/src/network/server.hpp @@ -49,7 +49,7 @@ protected: std::string m_lower_case_name; uint32_t m_server_id; - uint32_t m_host_id; + uint32_t m_server_owner; /** The maximum number of players that the server supports */ int m_max_players; @@ -98,9 +98,8 @@ public: /** Returns the ID of this server. */ const uint32_t getServerId() const { return m_server_id; } // ------------------------------------------------------------------------ - /** Returns the unique host id of this server (wan game only), which is - * the user id in STK addon server of the server owner. */ - const uint32_t getHostId() const { return m_host_id; } + /** Returns the user id in STK addon server of the server owner (WAN). */ + const uint32_t getServerOwner() const { return m_server_owner; } // ------------------------------------------------------------------------ uint16_t getPrivatePort() const { return m_private_port; } // ------------------------------------------------------------------------ diff --git a/src/states_screens/dialogs/server_info_dialog.cpp b/src/states_screens/dialogs/server_info_dialog.cpp index 779606aee..fb13a23d8 100644 --- a/src/states_screens/dialogs/server_info_dialog.cpp +++ b/src/states_screens/dialogs/server_info_dialog.cpp @@ -46,8 +46,8 @@ using namespace Online; ServerInfoDialog::ServerInfoDialog(std::shared_ptr server) : ModalDialog(0.8f,0.8f), m_server(server), m_password(NULL) { - Log::info("ServerInfoDialog", "Server id is %d, Host id is %d", - server->getServerId(), server->getHostId()); + Log::info("ServerInfoDialog", "Server id is %d, owner is %d", + server->getServerId(), server->getServerOwner()); m_self_destroy = false; loadFromFile("online/server_info_dialog.stkgui"); From bc56c07c511c92f0b6f70244f36b41bcf444a5a4 Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 15 Mar 2018 14:02:29 +0800 Subject: [PATCH 228/413] Hide the chatbox when disable chatting --- src/states_screens/networking_lobby.cpp | 29 ++++++++++++++----------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/states_screens/networking_lobby.cpp b/src/states_screens/networking_lobby.cpp index 837a4a8fe..dc000b390 100644 --- a/src/states_screens/networking_lobby.cpp +++ b/src/states_screens/networking_lobby.cpp @@ -105,18 +105,6 @@ void NetworkingLobby::loadedFromFile() // --------------------------------------------------------------------------- void NetworkingLobby::beforeAddingWidget() { - if (!UserConfigParams::m_lobby_chat) - { - getWidget("chat")->setVisible(false); - getWidget("chat")->setActive(false); - getWidget("send")->setVisible(false); - } - else - { - getWidget("chat")->setVisible(true); - getWidget("chat")->setActive(true); - getWidget("send")->setVisible(true); - } } // beforeAddingWidget // ---------------------------------------------------------------------------- @@ -136,7 +124,22 @@ void NetworkingLobby::init() input_manager->getDeviceManager()->getLatestUsedDevice(); PlayerProfile* profile = PlayerManager::getCurrentPlayer(); StateManager::get()->createActivePlayer(profile, device); - m_chat_box->addListener(this); + + if (!UserConfigParams::m_lobby_chat) + { + getWidget("chat")->setVisible(false); + getWidget("chat")->setActive(false); + getWidget("send")->setVisible(false); + getWidget("send")->setActive(false); + } + else + { + m_chat_box->addListener(this); + getWidget("chat")->setVisible(true); + getWidget("chat")->setActive(true); + getWidget("send")->setVisible(true); + getWidget("send")->setActive(true); + } } // init // ---------------------------------------------------------------------------- From f78276a930132f8442e1b436f25e885852385a36 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 16 Mar 2018 00:54:02 +0800 Subject: [PATCH 229/413] Show player vote in message queue for immediate effect --- data/gui/tracks.stkgui | 20 ++ src/network/protocols/client_lobby.cpp | 339 ++---------------- src/network/protocols/client_lobby.hpp | 21 +- src/network/protocols/connect_to_peer.hpp | 2 - src/network/protocols/lobby_protocol.hpp | 14 +- src/network/protocols/server_lobby.cpp | 202 ++--------- src/network/protocols/server_lobby.hpp | 13 +- src/states_screens/network_kart_selection.cpp | 2 + src/states_screens/track_info_screen.cpp | 3 +- src/states_screens/tracks_screen.cpp | 106 ++++-- src/states_screens/tracks_screen.hpp | 20 +- 11 files changed, 182 insertions(+), 560 deletions(-) diff --git a/data/gui/tracks.stkgui b/data/gui/tracks.stkgui index 70b90016d..1b02d6c30 100644 --- a/data/gui/tracks.stkgui +++ b/data/gui/tracks.stkgui @@ -17,5 +17,25 @@ + +
+
+ +
+
+
diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index aaee48d29..65b12d817 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -38,8 +38,10 @@ #include "online/online_profile.hpp" #include "states_screens/networking_lobby.hpp" #include "states_screens/network_kart_selection.hpp" +#include "states_screens/tracks_screen.hpp" #include "states_screens/race_result_gui.hpp" #include "states_screens/state_manager.hpp" +#include "tracks/track.hpp" #include "tracks/track_manager.hpp" #include "utils/log.hpp" @@ -91,122 +93,6 @@ void ClientLobby::setup() m_state = NONE; } // setup -//----------------------------------------------------------------------------- -/** Sends the selection of a kart from this client to the server. - * \param player_id The global player id of the voting player. - * \param kart_name Name of the selected kart. - */ -void ClientLobby::requestKartSelection(uint8_t player_id, - const std::string &kart_name) -{ - NetworkString *request = getNetworkString(3+kart_name.size()); - request->addUInt8(LE_KART_SELECTION).addUInt8(player_id) - .encodeString(kart_name); - sendToServer(request, /*reliable*/ true); - delete request; -} // requestKartSelection - -//----------------------------------------------------------------------------- -/** Sends a vote for a major vote from a client to the server. Note that even - * this client will only store the vote when it is received back from the - * server. - * \param player_id The global player id of the voting player. - * \param major Major mode voted for. - */ -void ClientLobby::voteMajor(uint8_t player_id, uint32_t major) -{ - NetworkString *request = getNetworkString(6); - request->addUInt8(LE_VOTE_MAJOR).addUInt8(player_id) - .addUInt32(major); - sendToServer(request, true); - delete request; -} // voteMajor - -//----------------------------------------------------------------------------- -/** Sends a vote for the number of tracks from a client to the server. Note - * that even this client will only store the vote when it is received back - * from the server. - * \param player_id The global player id of the voting player. - * \param count NUmber of tracks to play. - */ -void ClientLobby::voteRaceCount(uint8_t player_id, uint8_t count) -{ - NetworkString *request = getNetworkString(3); - request->addUInt8(LE_VOTE_RACE_COUNT).addUInt8(player_id).addUInt8(count); - sendToServer(request, true); - delete request; -} // voteRaceCount - -//----------------------------------------------------------------------------- -/** Sends a vote for the minor game mode from a client to the server. Note that - * even this client will only store the vote when it is received back from the - * server. - * \param player_id The global player id of the voting player. - * \param minor Voted minor mode. - */ -void ClientLobby::voteMinor(uint8_t player_id, uint32_t minor) -{ - NetworkString *request = getNetworkString(6); - request->addUInt8(LE_VOTE_MINOR).addUInt8(player_id).addUInt32(minor); - sendToServer(request, true); - delete request; -} // voteMinor - -//----------------------------------------------------------------------------- -/** Sends the vote about which track to play at which place in the list of - * tracks (like a custom GP definition). Note that even this client will only - * store the vote when it is received back from the server. - * \param player_id The global player id of the voting player. - * \param track Name of the track. - * \param At which place in the list of tracks this track should be played. - */ -void ClientLobby::voteTrack(uint8_t player_id, - const std::string &track, - uint8_t track_nb) -{ - NetworkString *request = getNetworkString(2+1+track.size()); - request->addUInt8(LE_VOTE_TRACK).addUInt8(player_id).addUInt8(track_nb) - .encodeString(track); - sendToServer(request, true); - delete request; -} // voteTrack - -//----------------------------------------------------------------------------- -/** Sends a vote if a track at a specified place in the list of all tracks - * should be played in reverse or not. Note that even this client will only - * store the vote when it is received back from the server. - * \param player_id Global player id of the voting player. - * \param reversed True if the track should be played in reverse. - * \param track_nb Index for the track to be voted on in the list of all - * tracks. - */ -void ClientLobby::voteReversed(uint8_t player_id, bool reversed, - uint8_t track_nb) -{ - NetworkString *request = getNetworkString(9); - request->addUInt8(LE_VOTE_REVERSE).addUInt8(player_id).addUInt8(reversed) - .addUInt8(track_nb); - sendToServer(request, true); - delete request; -} // voteReversed - -//----------------------------------------------------------------------------- -/** Vote for the number of laps of the specified track. Note that even this - * client will only store the vote when it is received back from the server. - * \param player_id Global player id of the voting player. - * \param laps Number of laps for the specified track. - * \param track_nb Index of the track in the list of all tracks. - */ -void ClientLobby::voteLaps(uint8_t player_id, uint8_t laps, - uint8_t track_nb) -{ - NetworkString *request = getNetworkString(10); - request->addUInt8(LE_VOTE_LAPS).addUInt8(player_id).addUInt8(laps) - .addUInt8(track_nb); - sendToServer(request, true); - delete request; -} // voteLaps - //----------------------------------------------------------------------------- /** Called from the gui when a client clicked on 'continue' on the race result * screen. It notifies the server that this client has exited the screen and @@ -234,7 +120,6 @@ bool ClientLobby::notifyEvent(Event* event) switch(message_type) { case LE_START_SELECTION: startSelection(event); break; - case LE_KART_SELECTION_UPDATE: kartSelectionUpdate(event); break; case LE_LOAD_WORLD: loadWorld(); break; case LE_RACE_FINISHED: raceFinished(event); break; case LE_EXIT_RESULT: exitResultScreen(event); break; @@ -266,13 +151,7 @@ bool ClientLobby::notifyEventAsynchronous(Event* event) case LE_START_RACE: startGame(event); break; case LE_CONNECTION_REFUSED: connectionRefused(event); break; case LE_CONNECTION_ACCEPTED: connectionAccepted(event); break; - case LE_KART_SELECTION_REFUSED: kartSelectionRefused(event); break; - case LE_VOTE_MAJOR : playerMajorVote(event); break; - case LE_VOTE_RACE_COUNT: playerRaceCountVote(event); break; - case LE_VOTE_MINOR: playerMinorVote(event); break; - case LE_VOTE_TRACK: playerTrackVote(event); break; - case LE_VOTE_REVERSE: playerReversedVote(event); break; - case LE_VOTE_LAPS: playerLapsVote(event); break; + case LE_VOTE: displayPlayerVote(event); break; case LE_SERVER_OWNERSHIP: becomingServerOwner(); break; default: break; } // switch @@ -310,7 +189,6 @@ bool ClientLobby::notifyEventAsynchronous(Event* event) } // notifyEventAsynchronous //----------------------------------------------------------------------------- - void ClientLobby::update(float dt) { switch (m_state) @@ -382,7 +260,9 @@ void ClientLobby::update(float dt) screen->setAvailableKartsFromServer(m_available_karts); screen->push(); m_state = SELECTING_KARTS; - + // Todo: add max lap + TracksScreen::getInstance()->setNetworkTracks(); + TracksScreen::getInstance()->setMaxLap(3); std::make_shared()->requestStart(); Log::info("LobbyProtocol", "LatencyProtocol started."); } @@ -403,7 +283,32 @@ void ClientLobby::update(float dt) } // update //----------------------------------------------------------------------------- +void ClientLobby::displayPlayerVote(Event* event) +{ + if (!checkDataSize(event, 4)) return; + // Get the player name who voted + core::stringw player_name; + NetworkString& data = event->data(); + data.decodeStringW(&player_name); + player_name += ": "; + std::string track_name; + data.decodeString(&track_name); + Track* track = track_manager->getTrack(track_name); + if (!track) + Log::fatal("ClientLobby", "Missing track %s", track_name.c_str()); + core::stringw track_readable = track->getName(); + int lap = data.getUInt8(); + bool reversed = (bool)data.getUInt8(); + core::stringw yes = _("Yes"); + core::stringw no = _("No"); + //I18N: Vote message in network game from a player + core::stringw vote_msg = _("Track: %s, Laps: %d, Reversed: %s", + track_readable, lap, reversed ? yes : no); + vote_msg = player_name + vote_msg; + MessageQueue::add(MessageQueue::MT_GENERIC, vote_msg); +} // displayPlayerVote +//----------------------------------------------------------------------------- /*! \brief Called when a new player is disconnected * \param event : Event providing the information. * @@ -552,58 +457,6 @@ void ClientLobby::connectionRefused(Event* event) //----------------------------------------------------------------------------- -/*! \brief Called when the server refuses the kart selection request. - * \param event : Event providing the information. - * - * Format of the data : - * Byte 0 - * ---------------- - * Size | 1 | - * Data | refusal code | - * ---------------- - */ -void ClientLobby::kartSelectionRefused(Event* event) -{ - if(!checkDataSize(event, 1)) return; - - const NetworkString &data = event->data(); - - switch (data.getUInt8()) // the error code - { - case 0: - Log::info("ClientLobby", - "Kart selection refused : already taken."); - break; - case 1: - Log::info("ClientLobby", - "Kart selection refused : not available."); - break; - default: - Log::info("ClientLobby", "Kart selection refused."); - break; - } -} // kartSelectionRefused - -//----------------------------------------------------------------------------- - -/*! \brief Called when the server tells to update a player's kart. - * \param event : Event providing the information. - * - * Format of the data : - * Byte 0 1 2 3 N+3 - * -------------------------------------------------- - * Size | 1 | 1 | N | - * Data | player id | N (kart name size) | kart name | - * -------------------------------------------------- - */ -void ClientLobby::kartSelectionUpdate(Event* event) -{ - if(!checkDataSize(event, 3)) return; - -} // kartSelectionUpdate - -//----------------------------------------------------------------------------- - /*! \brief Called when the server broadcasts to start the race to all clients. * \param event : Event providing the information (no additional information * in this case). @@ -728,136 +581,6 @@ void ClientLobby::exitResultScreen(Event *event) m_state = NONE; } // exitResultScreen -//----------------------------------------------------------------------------- -/*! \brief Called when a player votes for a major race mode. - * \param event : Event providing the information. - * - * Format of the data : - * Byte 0 1 2 - * ------------------------------ - * Size | 1 | 1 | - * Data |player id | major mode vote | - * ------------------------------ - */ -void ClientLobby::playerMajorVote(Event* event) -{ - const NetworkString &data = event->data(); - if (!checkDataSize(event, 2)) - return; - uint8_t player_id = data.getUInt8(); - uint8_t mode = data.getUInt8(); - m_game_setup->getRaceConfig()->setPlayerMajorVote(player_id, mode); -} // playerMajorVote - -//----------------------------------------------------------------------------- -/*! \brief Called when a player votes for the number of races in a GP. - * \param event : Event providing the information. - * - * Format of the data : - * Byte 0 1 - * --------------------------- - * Size | 1 | 1 | - * Data | player id | races count | - * --------------------------- - */ -void ClientLobby::playerRaceCountVote(Event* event) -{ - if (!checkDataSize(event, 2)) return; - const NetworkString &data = event->data(); - uint8_t player_id = data.getUInt8(); - uint8_t count = data.getUInt8(); - m_game_setup->getRaceConfig()->setPlayerRaceCountVote(player_id, count); -} // playerRaceCountVote - -//----------------------------------------------------------------------------- -/*! \brief Called when a player votes for a minor race mode. - * \param event : Event providing the information. - * - * Format of the data : - * Byte 0 1 - * ------------------------------- - * Size | 1 | 4 | - * Data | player id | minor mode vote | - * ------------------------------- - */ -void ClientLobby::playerMinorVote(Event* event) -{ - if (!checkDataSize(event, 2)) return; - const NetworkString &data = event->data(); - uint8_t player_id = data.getUInt8(); - uint8_t minor = data.getUInt8(); - m_game_setup->getRaceConfig()->setPlayerMinorVote(player_id, minor); -} // playerMinorVote - -//----------------------------------------------------------------------------- - -/*! \brief Called when a player votes for a track. - * \param event : Event providing the information. - * - * Format of the data : - * Byte 0 1 2 3 - * -------------------------------------------------- - * Size | 1 | 1 | 1 | N | - * Data | player id | track number (gp) | N | track name | - * -------------------------------------------------- - */ -void ClientLobby::playerTrackVote(Event* event) -{ - if (!checkDataSize(event, 3)) return; - const NetworkString &data = event->data(); - std::string track_name; - uint8_t player_id = data.getUInt8(); - uint8_t number = data.getUInt8(); - data.decodeString(&track_name); - m_game_setup->getRaceConfig()->setPlayerTrackVote(player_id, track_name, - number); -} // playerTrackVote - -//----------------------------------------------------------------------------- - -/*! \brief Called when a player votes for the reverse mode of a race - * \param event : Event providing the information. - * - * Format of the data : - * Byte 0 1 2 - * ------------------------------------------- - * Size | 1 | 1 | 1 | - * Data | player id |reversed | track number (gp) | - * ------------------------------------------- - */ -void ClientLobby::playerReversedVote(Event* event) -{ - if (!checkDataSize(event, 3)) return; - const NetworkString &data = event->data(); - uint8_t player_id = data.getUInt8(); - uint8_t reversed = data.getUInt8(); - uint8_t number = data.getUInt8(); - m_game_setup->getRaceConfig()->setPlayerReversedVote(player_id, reversed!=0, - number); -} // playerReversedVote - -//----------------------------------------------------------------------------- - -/*! \brief Called when a player votes for a major race mode. - * \param event : Event providing the information. - * - * Format of the data : - * Byte 0 1 2 - * ---------------------------------------- - * Size | 1 | 1 | 1 | - * Data | player id | laps | track number (gp) | - * ---------------------------------------- - */ -void ClientLobby::playerLapsVote(Event* event) -{ - if (!checkDataSize(event, 3)) return; - const NetworkString &data = event->data(); - uint8_t player_id = data.getUInt8(); - uint8_t laps = data.getUInt8(); - uint8_t number = data.getUInt8(); - m_game_setup->getRaceConfig()->setPlayerLapsVote(player_id, laps, number); -} // playerLapsVote - //----------------------------------------------------------------------------- /** Callback when the world is loaded. The client will inform the server * that the players on this host are ready to start the race. It is called by diff --git a/src/network/protocols/client_lobby.hpp b/src/network/protocols/client_lobby.hpp index 1273d3c44..c09686e94 100644 --- a/src/network/protocols/client_lobby.hpp +++ b/src/network/protocols/client_lobby.hpp @@ -12,19 +12,12 @@ private: void disconnectedPlayer(Event* event); void connectionAccepted(Event* event); //!< Callback function on connection acceptation void connectionRefused(Event* event); //!< Callback function on connection refusal - void kartSelectionRefused(Event* event); - void kartSelectionUpdate(Event* event); void startGame(Event* event); void startSelection(Event* event); void raceFinished(Event* event); void exitResultScreen(Event *event); // race votes - void playerMajorVote(Event* event); - void playerRaceCountVote(Event* event); - void playerMinorVote(Event* event); - void playerTrackVote(Event* event); - void playerReversedVote(Event* event); - void playerLapsVote(Event* event); + void displayPlayerVote(Event* event); void updatePlayerList(Event* event); void handleChat(Event* event); void becomingServerOwner(); @@ -54,25 +47,13 @@ private: public: ClientLobby(); virtual ~ClientLobby(); - - virtual void requestKartSelection(uint8_t player_id, - const std::string &kart_name) OVERRIDE; void setAddress(const TransportAddress &address); - void voteMajor(uint8_t player_id, uint32_t major); - void voteRaceCount(uint8_t player_id, uint8_t count); - void voteMinor(uint8_t player_id, uint32_t minor); - void voteTrack(uint8_t player_id, const std::string &track, - uint8_t track_nb = 0); - void voteReversed(uint8_t player_id, bool reversed, uint8_t track_nb = 0); - void voteLaps(uint8_t player_id, uint8_t laps, uint8_t track_nb = 0); void doneWithResults(); void startingRaceNow(); - const std::set& getAvailableKarts() const { return m_available_karts; } const std::set& getAvailableTracks() const { return m_available_tracks; } - virtual bool notifyEvent(Event* event) OVERRIDE; virtual bool notifyEventAsynchronous(Event* event) OVERRIDE; virtual void finishedLoadingWorld() OVERRIDE; diff --git a/src/network/protocols/connect_to_peer.hpp b/src/network/protocols/connect_to_peer.hpp index ac075a0fd..c2b865153 100644 --- a/src/network/protocols/connect_to_peer.hpp +++ b/src/network/protocols/connect_to_peer.hpp @@ -23,8 +23,6 @@ #include "network/transport_address.hpp" #include "utils/cpp2011.hpp" -#include - /** One instance of this is started for every peer who tries to * connect to this server. */ diff --git a/src/network/protocols/lobby_protocol.hpp b/src/network/protocols/lobby_protocol.hpp index 8782a5e14..92c1cad69 100644 --- a/src/network/protocols/lobby_protocol.hpp +++ b/src/network/protocols/lobby_protocol.hpp @@ -53,15 +53,8 @@ public: LE_RACE_FINISHED, // race has finished, display result LE_RACE_FINISHED_ACK, // client went back to lobby LE_EXIT_RESULT, // Force clients to exit race result screen - LE_VOTE, // Any vote (race mode, track, ...) - LE_VOTE_MAJOR, // vote of major race mode - LE_VOTE_MINOR, // vote for minor race mode - LE_VOTE_RACE_COUNT, // vote for number of tracks - LE_VOTE_TRACK, // vote for a track - LE_VOTE_REVERSE, // vote if race in reverse - LE_VOTE_LAPS, // vote number of laps + LE_VOTE, // Track vote LE_CHAT, - LE_FINAL_PLAYER_LIST, LE_SERVER_OWNERSHIP, LE_KICK_HOST }; @@ -115,11 +108,6 @@ public: virtual void loadWorld(); virtual bool waitingForPlayers() const = 0; void terminateLatencyProtocol(); - virtual void requestKartSelection(uint8_t player_id, - const std::string &kart_name) - { - assert(false); // Only defined in client - }; GameSetup* getGameSetup() const { return m_game_setup; } }; // class LobbyProtocol diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index dbb061de9..818779a0a 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -205,12 +205,7 @@ bool ServerLobby::notifyEventAsynchronous(Event* event) case LE_KART_SELECTION: kartSelectionRequested(event); break; case LE_CLIENT_LOADED_WORLD: finishedLoadingWorldClient(event); break; case LE_STARTED_RACE: startedRaceOnClient(event); break; - case LE_VOTE_MAJOR: playerMajorVote(event); break; - case LE_VOTE_RACE_COUNT: playerRaceCountVote(event); break; - case LE_VOTE_MINOR: playerMinorVote(event); break; - case LE_VOTE_TRACK: playerTrackVote(event); break; - case LE_VOTE_REVERSE: playerReversedVote(event); break; - case LE_VOTE_LAPS: playerLapsVote(event); break; + case LE_VOTE: playerVote(event); break; case LE_RACE_FINISHED_ACK: playerFinishedResult(event); break; case LE_KICK_HOST: kickHost(event); break; case LE_CHAT: handleChat(event); break; @@ -572,6 +567,9 @@ void ServerLobby::startSelection(const Event *event) delete ns; m_state = SELECTING; + // 30 seconds for clients to choose kart and track + // will be increased later if there is grand prix + m_timeout = StkTime::getRealTime() + 30.0f; WaitingForOthersScreen::getInstance()->push(); std::make_shared()->requestStart(); @@ -954,7 +952,10 @@ void ServerLobby::kartSelectionRequested(Event* event) return; } - if (!checkDataSize(event, 1)) return; + if (!checkDataSize(event, 1) || + event->getPeer()->getPlayerProfiles().empty()) + return; + const NetworkString& data = event->data(); STKPeer* peer = event->getPeer(); unsigned player_count = data.getUInt8(); @@ -979,185 +980,24 @@ void ServerLobby::kartSelectionRequested(Event* event) } // kartSelectionRequested //----------------------------------------------------------------------------- - -/*! \brief Called when a player votes for a major race mode. +/*! \brief Called when a player votes for track(s). * \param event : Event providing the information. - * - * Format of the data : - * Byte 0 1 - * ------------------------------- - * Size | 1 | 4 | - * Data | player-id | major mode vote | - * ------------------------------- */ -void ServerLobby::playerMajorVote(Event* event) +void ServerLobby::playerVote(Event* event) { - if (!checkDataSize(event, 5)) return; + if (!checkDataSize(event, 4) || + event->getPeer()->getPlayerProfiles().empty()) + return; - NetworkString &data = event->data(); - uint8_t player_id = data.getUInt8(); - uint32_t major = data.getUInt32(); - m_game_setup->getRaceConfig()->setPlayerMajorVote(player_id, major); - // Send the vote to everybody (including the sender) - NetworkString *other = getNetworkString(6); - other->addUInt8(LE_VOTE_MAJOR).addUInt8(player_id).addUInt32(major); - sendMessageToPeersChangingToken(other); - delete other; -} // playerMajorVote + NetworkString& data = event->data(); + NetworkString other = NetworkString(PROTOCOL_LOBBY_ROOM); + other.addUInt8(LE_VOTE).encodeString(event->getPeer() + ->getPlayerProfiles()[0]->getName()); + other += data; + sendMessageToPeersChangingToken(&other); -//----------------------------------------------------------------------------- -/** \brief Called when a player votes for the number of races in a GP. - * \param event : Event providing the information. - * - * Format of the data : - * Byte 0 1 - * --------------------------- - * Size | 1 | 4 | - * Data | player-id | races count | - * --------------------------- - */ -void ServerLobby::playerRaceCountVote(Event* event) -{ - if (!checkDataSize(event, 1)) return; - NetworkString &data = event->data(); - uint8_t player_id = data.getUInt8(); - uint8_t race_count = data.getUInt8(); - m_game_setup->getRaceConfig()->setPlayerRaceCountVote(player_id, race_count); - // Send the vote to everybody (including the sender) - NetworkString *other = getNetworkString(3); - other->addUInt8(LE_VOTE_RACE_COUNT).addUInt8(player_id) - .addUInt8(race_count); - sendMessageToPeersChangingToken(other); - delete other; -} // playerRaceCountVote - -//----------------------------------------------------------------------------- - -/*! \brief Called when a player votes for a minor race mode. - * \param event : Event providing the information. - * - * Format of the data : - * Byte 0 1 - * ------------------------------- - * Size | 1 | 4 | - * Data | player-id | minor mode vote | - * ------------------------------- - */ -void ServerLobby::playerMinorVote(Event* event) -{ - if (!checkDataSize(event, 1)) return; - NetworkString &data = event->data(); - uint8_t player_id = data.getUInt8(); - uint32_t minor = data.getUInt32(); - m_game_setup->getRaceConfig()->setPlayerMinorVote(player_id, minor); - - // Send the vote to everybody (including the sender) - NetworkString *other = getNetworkString(3); - other->addUInt8(LE_VOTE_MINOR).addUInt8(player_id).addUInt8(minor); - sendMessageToPeersChangingToken(other); - delete other; -} // playerMinorVote - -//----------------------------------------------------------------------------- - -/*! \brief Called when a player votes for a track. - * \param event : Event providing the information. - * - * Format of the data : - * Byte 0 1 2 3 - * -------------------------------------------------- - * Size | 1 | 1 | 1 | N | - * Data | player id | track number (gp) | N | track name | - * -------------------------------------------------- - */ -void ServerLobby::playerTrackVote(Event* event) -{ - if (!checkDataSize(event, 3)) return; - NetworkString &data = event->data(); - uint8_t player_id = data.getUInt8(); - // As which track this track should be used, e.g. 1st track: Sandtrack - // 2nd track Mathclass, ... - uint8_t track_number = data.getUInt8(); - std::string track_name; - int N = data.decodeString(&track_name); - m_game_setup->getRaceConfig()->setPlayerTrackVote(player_id, track_name, - track_number); - // Send the vote to everybody (including the sender) - NetworkString *other = getNetworkString(3+1+data.size()); - other->addUInt8(LE_VOTE_TRACK).addUInt8(player_id).addUInt8(track_number) - .encodeString(track_name); - sendMessageToPeersChangingToken(other); - delete other; - - // Check if we received all information - if (m_game_setup->getRaceConfig()->getNumTrackVotes() == - m_game_setup->getPlayerCount()) - { - // Inform clients to start loading the world - NetworkString *ns = getNetworkString(1); - ns->setSynchronous(true); - ns->addUInt8(LE_LOAD_WORLD); - sendMessageToPeersChangingToken(ns, /*reliable*/true); - delete ns; - m_state = LOAD_WORLD; // Server can now load world - } -} // playerTrackVote - -//----------------------------------------------------------------------------- -/*! \brief Called when a player votes for the reverse mode of a race - * \param event : Event providing the information. - * - * Format of the data : - * Byte 0 1 2 - * -------------------------------------------- - * Size | 1 | 1 | 1 | - * Data | player id | reversed | track number (gp) | - * -------------------------------------------- - */ -void ServerLobby::playerReversedVote(Event* event) -{ - if (!checkDataSize(event, 3)) return; - - NetworkString &data = event->data(); - uint8_t player_id = data.getUInt8(); - uint8_t reverse = data.getUInt8(); - uint8_t nb_track = data.getUInt8(); - m_game_setup->getRaceConfig()->setPlayerReversedVote(player_id, - reverse!=0, nb_track); - // Send the vote to everybody (including the sender) - NetworkString *other = getNetworkString(4); - other->addUInt8(LE_VOTE_REVERSE).addUInt8(player_id).addUInt8(reverse) - .addUInt8(nb_track); - sendMessageToPeersChangingToken(other); - delete other; -} // playerReversedVote - -//----------------------------------------------------------------------------- -/*! \brief Called when a player votes for a major race mode. - * \param event : Event providing the information. - * - * Format of the data : - * Byte 0 1 2 - * ---------------------------------------- - * Size | 1 | 1 | 1 | - * Data | player id | laps | track number (gp) | - * ---------------------------------------- - */ -void ServerLobby::playerLapsVote(Event* event) -{ - if (!checkDataSize(event, 2)) return; - NetworkString &data = event->data(); - uint8_t player_id = data.getUInt8(); - uint8_t lap_count = data.getUInt8(); - uint8_t track_nb = data.getUInt8(); - m_game_setup->getRaceConfig()->setPlayerLapsVote(player_id, lap_count, - track_nb); - NetworkString *other = getNetworkString(4); - other->addUInt8(LE_VOTE_LAPS).addUInt8(player_id).addUInt8(lap_count) - .addUInt8(track_nb); - sendMessageToPeersChangingToken(other); - delete other; -} // playerLapsVote + // Save the vote +} // playerVote // ---------------------------------------------------------------------------- /** Called from the RaceManager of the server when the world is loaded. Marks diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 2d5f6e192..2dd10b3b9 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -63,7 +63,7 @@ private: /** Counts how many players are ready to go on. */ int m_player_ready_counter; - /** Timeout counter for showing the result screen. */ + /** Timeout counter for various state. */ float m_timeout; /** Lock this mutex whenever a client is connect / disconnect or @@ -75,13 +75,8 @@ private: void connectionRequested(Event* event); // kart selection void kartSelectionRequested(Event* event); - // race votes - void playerMajorVote(Event* event); - void playerRaceCountVote(Event* event); - void playerMinorVote(Event* event); - void playerTrackVote(Event* event); - void playerReversedVote(Event* event); - void playerLapsVote(Event* event); + // Track(s) votes + void playerVote(Event *event); void playerFinishedResult(Event *event); void registerServer(); void finishedLoadingWorldClient(Event *event); @@ -108,7 +103,7 @@ public: void startSelection(const Event *event=NULL); void checkIncomingConnectionRequests(); void checkRaceFinished(); - void finishedLoadingWorld(); + void finishedLoadingWorld() OVERRIDE; ServerState getCurrentState() const { return m_state.load(); } virtual bool waitingForPlayers() const OVERRIDE { return m_state.load() == ACCEPTING_CLIENTS; } diff --git a/src/states_screens/network_kart_selection.cpp b/src/states_screens/network_kart_selection.cpp index 39f5fcd79..d87551e68 100644 --- a/src/states_screens/network_kart_selection.cpp +++ b/src/states_screens/network_kart_selection.cpp @@ -149,6 +149,8 @@ void NetworkKartSelectionScreen::playerConfirm(const int playerID) .encodeString(selection); STKHost::get()->sendToServer(&kart, true); input_manager->getDeviceManager()->setAssignMode(ASSIGN); + // Remove kart screen + StateManager::get()->popMenu(); TracksScreen::getInstance()->push(); } } // playerConfirm diff --git a/src/states_screens/track_info_screen.cpp b/src/states_screens/track_info_screen.cpp index c86d47ac9..d09c742e3 100644 --- a/src/states_screens/track_info_screen.cpp +++ b/src/states_screens/track_info_screen.cpp @@ -374,7 +374,8 @@ void TrackInfoScreen::onEnterPressedInternal() num_ai = m_ai_kart_spinner->getValue(); - if (UserConfigParams::m_num_karts_per_gamemode[race_manager->getMinorMode()] != (local_players + num_ai)) + if (UserConfigParams::m_num_karts_per_gamemode + [race_manager->getMinorMode()] != unsigned(local_players + num_ai)) { race_manager->setNumKarts(local_players + num_ai); UserConfigParams::m_num_karts_per_gamemode[race_manager->getMinorMode()] = local_players + num_ai; diff --git a/src/states_screens/tracks_screen.cpp b/src/states_screens/tracks_screen.cpp index 1dbfac7bc..edaeb5fd0 100644 --- a/src/states_screens/tracks_screen.cpp +++ b/src/states_screens/tracks_screen.cpp @@ -22,10 +22,12 @@ #include "config/user_config.hpp" #include "graphics/stk_tex_manager.hpp" #include "guiengine/widget.hpp" +#include "guiengine/widgets/check_box_widget.hpp" #include "guiengine/widgets/dynamic_ribbon_widget.hpp" #include "guiengine/widgets/icon_button_widget.hpp" +#include "guiengine/widgets/label_widget.hpp" +#include "guiengine/widgets/spinner_widget.hpp" #include "io/file_manager.hpp" -#include "network/network_player_profile.hpp" #include "network/protocols/client_lobby.hpp" #include "network/network_config.hpp" #include "network/stk_host.hpp" @@ -88,21 +90,16 @@ void TracksScreen::eventCallback(Widget* widget, const std::string& name, if (track) { - if(STKHost::existHost()) + if (STKHost::existHost()) { - auto clrp = LobbyProtocol::get(); - // server never shows the track screen. - assert(clrp); - // FIXME SPLITSCREEN: we need to supply the global player id of the - // player selecting the track here. For now ... just vote the same - // track for each local player. - /*std::vector players = - STKHost::get()->getMyPlayerProfiles(); - for(unsigned int i=0; ivoteTrack(players[i]->getGlobalPlayerId(),selection); - } - WaitingForOthersScreen::getInstance()->push();*/ + NetworkString vote(PROTOCOL_LOBBY_ROOM); + vote.addUInt8(LobbyProtocol::LE_VOTE); + vote.encodeString(track->getIdent()).addUInt8 + (getWidget("lap-spinner")->getValue()) + .addUInt8( + getWidget("reverse")->getState()); + STKHost::get()->sendToServer(&vote, true); + WaitingForOthersScreen::getInstance()->push(); } else { @@ -125,10 +122,28 @@ void TracksScreen::eventCallback(Widget* widget, const std::string& name, } // eventCallback // ----------------------------------------------------------------------------- +bool TracksScreen::onEscapePressed() +{ + if (m_network_tracks) + { + // Remove this screen + StateManager::get()->popMenu(); + STKHost::get()->shutdown(); + } + return true; // remove the screen +} // onEscapePressed +// ----------------------------------------------------------------------------- +void TracksScreen::tearDown() +{ + m_network_tracks = false; +} // tearDown + +// ----------------------------------------------------------------------------- void TracksScreen::beforeAddingWidget() { Screen::init(); + RibbonWidget* tabs = getWidget("trackgroups"); tabs->clearAllChildren(); @@ -147,12 +162,24 @@ void TracksScreen::beforeAddingWidget() DynamicRibbonWidget* tracks_widget = getWidget("tracks"); tracks_widget->setItemCountHint( (int)track_manager->getNumberOfTracks()+1 ); + } // beforeAddingWidget // ----------------------------------------------------------------------------- - void TracksScreen::init() { + // change the back button image (because it makes the game quit) + if (m_network_tracks) + { + IconButtonWidget* back_button = getWidget("back"); + back_button->setImage("gui/main_quit.png"); + } + else + { + IconButtonWidget* back_button = getWidget("back"); + back_button->setImage("gui/back.png"); + } + DynamicRibbonWidget* tracks_widget = getWidget("tracks"); assert(tracks_widget != NULL); @@ -171,16 +198,48 @@ void TracksScreen::init() tracks_widget->setSelection(0, PLAYER_ID_GAME_MASTER, true); } STKTexManager::getInstance()->unsetTextureErrorMessage(); - if (NetworkConfig::get()->isAutoConnect()) + if (!m_network_tracks) { - DynamicRibbonWidget* tw = getWidget("tracks"); - tw->setSelection(UserConfigParams::m_last_track, 0, - /*focus*/true); - eventCallback(tw, "tracks", - /*player id*/0); + getWidget("lap-text")->setActive(false); + getWidget("lap-text")->setVisible(false); + getWidget("lap-spinner")->setActive(false); + getWidget("lap-spinner")->setVisible(false); + getWidget("reverse-text")->setActive(false); + getWidget("reverse-text")->setVisible(false); + getWidget("reverse")->setActive(false); + getWidget("reverse")->setVisible(false); + } + else + { + getWidget("lap-text")->setActive(true); + getWidget("lap-text")->setVisible(true); + getWidget("lap-spinner")->setActive(true); + getWidget("lap-spinner")->setVisible(true); + getWidget("lap-spinner")->setMin(1); + getWidget("lap-spinner")->setMax(m_max_lap); + getWidget("lap-spinner")->setValue(1); + getWidget("reverse-text")->setActive(true); + getWidget("reverse-text")->setVisible(true); + getWidget("reverse")->setActive(true); + getWidget("reverse")->setVisible(true); + getWidget("reverse")->setState(false); } } // init +// ----------------------------------------------------------------------------- +void TracksScreen::onUpdate(float dt) +{ + if (NetworkConfig::get()->isAutoConnect() && m_network_tracks) + { + assert(!m_random_track_list.empty()); + NetworkString vote(PROTOCOL_LOBBY_ROOM); + vote.addUInt8(LobbyProtocol::LE_VOTE); + vote.encodeString(m_random_track_list[0]).addUInt8(1).addUInt8(0); + STKHost::get()->sendToServer(&vote, true); + WaitingForOthersScreen::getInstance()->push(); + } +} // onUpdate + // ----------------------------------------------------------------------------- /** Rebuild the list of tracks. This need to be recomputed e.g. to * take unlocked tracks into account. @@ -257,7 +316,6 @@ void TracksScreen::buildTrackList() } // buildTrackList // ----------------------------------------------------------------------------- - void TracksScreen::setFocusOnTrack(const std::string& trackName) { DynamicRibbonWidget* tracks_widget = this->getWidget("tracks"); @@ -266,5 +324,3 @@ void TracksScreen::setFocusOnTrack(const std::string& trackName) // so it's safe to use 'PLAYER_ID_GAME_MASTER' tracks_widget->setSelection(trackName, PLAYER_ID_GAME_MASTER, true); } // setFocusOnTrack - -// ----------------------------------------------------------------------------- diff --git a/src/states_screens/tracks_screen.hpp b/src/states_screens/tracks_screen.hpp index 5ca99313e..df35b0f8d 100644 --- a/src/states_screens/tracks_screen.hpp +++ b/src/states_screens/tracks_screen.hpp @@ -33,7 +33,14 @@ class TracksScreen : public GUIEngine::Screen, friend class GUIEngine::ScreenSingleton; private: - TracksScreen() : Screen("tracks.stkgui") {} + TracksScreen() : Screen("tracks.stkgui") + { + m_network_tracks = false; + m_max_lap = 0; + } + + bool m_network_tracks; + unsigned m_max_lap; /** adds the tracks from the current track group into the tracks ribbon */ void buildTrackList(); @@ -56,8 +63,19 @@ public: /** \brief implement callback from parent class GUIEngine::Screen */ virtual void beforeAddingWidget() OVERRIDE; + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void tearDown() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void onUpdate(float dt) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual bool onEscapePressed() OVERRIDE; + void setFocusOnTrack(const std::string& trackName); + void setNetworkTracks() { m_network_tracks = true; } + void setMaxLap(unsigned lap) { m_max_lap = lap; } }; #endif From 2cedd6729ab1fd934a0fe69f918ab13f48756fd8 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 16 Mar 2018 01:14:43 +0800 Subject: [PATCH 230/413] Try to connect anyway even without aloha message This should allow clients with strong firewall to connect to server not behind any firewall --- src/network/protocols/connect_to_server.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/network/protocols/connect_to_server.cpp b/src/network/protocols/connect_to_server.cpp index 36eabdd7f..cdfd8522a 100644 --- a/src/network/protocols/connect_to_server.cpp +++ b/src/network/protocols/connect_to_server.cpp @@ -159,9 +159,13 @@ void ConnectToServer::asynchronousUpdate() } if (m_tried_connection++ > 10) { - Log::error("ConnectToServer", "Timeout waiting for aloha"); - m_state = NetworkConfig::get()->isWAN() ? - HIDING_ADDRESS : DONE; + Log::warn("ConnectToServer", "Timeout waiting for" + " aloha, trying to connect anyway."); + m_state = CONNECTING; + // Reset timer for next usage + m_timer = 0.0; + m_tried_connection = 0; + return; } if ((!NetworkConfig::m_disable_lan && m_server_address.getIP() == From 3d787f413492b4783c97c78714aecc8640c2e057 Mon Sep 17 00:00:00 2001 From: Deve Date: Thu, 15 Mar 2018 21:35:12 +0100 Subject: [PATCH 231/413] Send text message when pressing enter on android keyboard --- src/guiengine/screen_keyboard.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/guiengine/screen_keyboard.cpp b/src/guiengine/screen_keyboard.cpp index 5387afa53..7c7ae5ac1 100644 --- a/src/guiengine/screen_keyboard.cpp +++ b/src/guiengine/screen_keyboard.cpp @@ -233,6 +233,7 @@ EventPropagation ScreenKeyboard::processEvent(const std::string& eventSource) SEvent event; bool send_event = false; + bool close_keyboard = false; if (eventSource == "Shift") { @@ -264,8 +265,10 @@ EventPropagation ScreenKeyboard::processEvent(const std::string& eventSource) } else if (eventSource == "Enter") { - dismiss(); - return EVENT_BLOCK; + event.KeyInput.Key = IRR_KEY_RETURN; + event.KeyInput.Char = 0; + send_event = true; + close_keyboard = true; } else if (eventSource == "Back") { @@ -295,6 +298,11 @@ EventPropagation ScreenKeyboard::processEvent(const std::string& eventSource) m_edit_box->OnEvent(event); } + + if (close_keyboard) + { + dismiss(); + } return EVENT_BLOCK; } // processEvent From f8f77edc4732af9b364c4a5bfa5e4521fa652bdd Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Thu, 15 Mar 2018 21:29:45 -0400 Subject: [PATCH 232/413] Fix GUI skin issue --- src/guiengine/skin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guiengine/skin.cpp b/src/guiengine/skin.cpp index c4427a554..3d566ac2a 100644 --- a/src/guiengine/skin.cpp +++ b/src/guiengine/skin.cpp @@ -1675,7 +1675,7 @@ void Skin::renderSections(PtrVector* within_vector) if (widget.m_type == WTYPE_DIV) { - if (widget.m_show_bounding_box) + if (widget.m_show_bounding_box && widget.isVisible()) { if (widget.m_is_bounding_box_round) { From cf898c279595bae063ee5986db89537edcb90bc8 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 16 Mar 2018 11:35:16 +0800 Subject: [PATCH 233/413] Remove waiting for other screen now that you can instantly know votes --- data/gui/online/waiting_for_others.stkgui | 11 ---- src/network/protocols/client_lobby.cpp | 4 -- src/network/protocols/server_lobby.cpp | 16 ++++-- src/states_screens/network_kart_selection.cpp | 2 +- src/states_screens/tracks_and_gp_screen.cpp | 1 - src/states_screens/tracks_screen.cpp | 28 +++------- src/states_screens/tracks_screen.hpp | 8 +-- src/states_screens/waiting_for_others.cpp | 53 ------------------- src/states_screens/waiting_for_others.hpp | 49 ----------------- 9 files changed, 20 insertions(+), 152 deletions(-) delete mode 100644 data/gui/online/waiting_for_others.stkgui delete mode 100644 src/states_screens/waiting_for_others.cpp delete mode 100644 src/states_screens/waiting_for_others.hpp diff --git a/data/gui/online/waiting_for_others.stkgui b/data/gui/online/waiting_for_others.stkgui deleted file mode 100644 index 5af100c2a..000000000 --- a/data/gui/online/waiting_for_others.stkgui +++ /dev/null @@ -1,11 +0,0 @@ - - -
- -
- - - -
-
diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index 65b12d817..d7df44573 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -38,7 +38,6 @@ #include "online/online_profile.hpp" #include "states_screens/networking_lobby.hpp" #include "states_screens/network_kart_selection.hpp" -#include "states_screens/tracks_screen.hpp" #include "states_screens/race_result_gui.hpp" #include "states_screens/state_manager.hpp" #include "tracks/track.hpp" @@ -260,9 +259,6 @@ void ClientLobby::update(float dt) screen->setAvailableKartsFromServer(m_available_karts); screen->push(); m_state = SELECTING_KARTS; - // Todo: add max lap - TracksScreen::getInstance()->setNetworkTracks(); - TracksScreen::getInstance()->setMaxLap(3); std::make_shared()->requestStart(); Log::info("LobbyProtocol", "LatencyProtocol started."); } diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 818779a0a..7d848074d 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -37,7 +37,6 @@ #include "race/race_manager.hpp" #include "states_screens/networking_lobby.hpp" #include "states_screens/race_result_gui.hpp" -#include "states_screens/waiting_for_others.hpp" #include "tracks/track_manager.hpp" #include "utils/log.hpp" #include "utils/random_generator.hpp" @@ -567,10 +566,9 @@ void ServerLobby::startSelection(const Event *event) delete ns; m_state = SELECTING; - // 30 seconds for clients to choose kart and track - // will be increased later if there is grand prix - m_timeout = StkTime::getRealTime() + 30.0f; - WaitingForOthersScreen::getInstance()->push(); + + // Will be changed after the first vote received + m_timeout = std::numeric_limits::max(); std::make_shared()->requestStart(); Log::info("LobbyProtocol", "LatencyProtocol started."); @@ -985,6 +983,14 @@ void ServerLobby::kartSelectionRequested(Event* event) */ void ServerLobby::playerVote(Event* event) { + std::lock_guard lock(m_connection_mutex); + if (m_state != SELECTING) + { + Log::warn("ServerLobby", "Received track vote while in state %d.", + m_state.load()); + return; + } + if (!checkDataSize(event, 4) || event->getPeer()->getPlayerProfiles().empty()) return; diff --git a/src/states_screens/network_kart_selection.cpp b/src/states_screens/network_kart_selection.cpp index d87551e68..ed9bb6f68 100644 --- a/src/states_screens/network_kart_selection.cpp +++ b/src/states_screens/network_kart_selection.cpp @@ -36,7 +36,6 @@ #include "states_screens/race_setup_screen.hpp" #include "states_screens/state_manager.hpp" #include "states_screens/tracks_screen.hpp" -#include "states_screens/waiting_for_others.hpp" static const char ID_LOCKED[] = "locked/"; @@ -151,6 +150,7 @@ void NetworkKartSelectionScreen::playerConfirm(const int playerID) input_manager->getDeviceManager()->setAssignMode(ASSIGN); // Remove kart screen StateManager::get()->popMenu(); + TracksScreen::getInstance()->setNetworkTracks(); TracksScreen::getInstance()->push(); } } // playerConfirm diff --git a/src/states_screens/tracks_and_gp_screen.cpp b/src/states_screens/tracks_and_gp_screen.cpp index 558cbea6d..2b70b7ae4 100644 --- a/src/states_screens/tracks_and_gp_screen.cpp +++ b/src/states_screens/tracks_and_gp_screen.cpp @@ -30,7 +30,6 @@ #include "states_screens/state_manager.hpp" #include "states_screens/track_info_screen.hpp" #include "states_screens/gp_info_screen.hpp" -#include "states_screens/waiting_for_others.hpp" #include "tracks/track.hpp" #include "tracks/track_manager.hpp" #include "utils/translation.hpp" diff --git a/src/states_screens/tracks_screen.cpp b/src/states_screens/tracks_screen.cpp index edaeb5fd0..fb69b59fe 100644 --- a/src/states_screens/tracks_screen.cpp +++ b/src/states_screens/tracks_screen.cpp @@ -33,7 +33,6 @@ #include "network/stk_host.hpp" #include "states_screens/state_manager.hpp" #include "states_screens/track_info_screen.hpp" -#include "states_screens/waiting_for_others.hpp" #include "tracks/track.hpp" #include "tracks/track_manager.hpp" #include "utils/translation.hpp" @@ -49,7 +48,13 @@ static const char ALL_TRACK_GROUPS_ID[] = "all"; DEFINE_SCREEN_SINGLETON( TracksScreen ); // ----------------------------------------------------------------------------- +void TracksScreen::loadedFromFile() +{ + getWidget("reverse")->setState(false); + getWidget("lap-spinner")->setValue(1); +} // loadedFromFile +// ----------------------------------------------------------------------------- void TracksScreen::eventCallback(Widget* widget, const std::string& name, const int playerID) { @@ -99,7 +104,6 @@ void TracksScreen::eventCallback(Widget* widget, const std::string& name, .addUInt8( getWidget("reverse")->getState()); STKHost::get()->sendToServer(&vote, true); - WaitingForOthersScreen::getInstance()->push(); } else { @@ -200,35 +204,18 @@ void TracksScreen::init() STKTexManager::getInstance()->unsetTextureErrorMessage(); if (!m_network_tracks) { - getWidget("lap-text")->setActive(false); getWidget("lap-text")->setVisible(false); - getWidget("lap-spinner")->setActive(false); getWidget("lap-spinner")->setVisible(false); - getWidget("reverse-text")->setActive(false); getWidget("reverse-text")->setVisible(false); - getWidget("reverse")->setActive(false); getWidget("reverse")->setVisible(false); } else { - getWidget("lap-text")->setActive(true); getWidget("lap-text")->setVisible(true); - getWidget("lap-spinner")->setActive(true); getWidget("lap-spinner")->setVisible(true); - getWidget("lap-spinner")->setMin(1); - getWidget("lap-spinner")->setMax(m_max_lap); - getWidget("lap-spinner")->setValue(1); - getWidget("reverse-text")->setActive(true); getWidget("reverse-text")->setVisible(true); - getWidget("reverse")->setActive(true); getWidget("reverse")->setVisible(true); - getWidget("reverse")->setState(false); } -} // init - -// ----------------------------------------------------------------------------- -void TracksScreen::onUpdate(float dt) -{ if (NetworkConfig::get()->isAutoConnect() && m_network_tracks) { assert(!m_random_track_list.empty()); @@ -236,9 +223,8 @@ void TracksScreen::onUpdate(float dt) vote.addUInt8(LobbyProtocol::LE_VOTE); vote.encodeString(m_random_track_list[0]).addUInt8(1).addUInt8(0); STKHost::get()->sendToServer(&vote, true); - WaitingForOthersScreen::getInstance()->push(); } -} // onUpdate +} // init // ----------------------------------------------------------------------------- /** Rebuild the list of tracks. This need to be recomputed e.g. to diff --git a/src/states_screens/tracks_screen.hpp b/src/states_screens/tracks_screen.hpp index df35b0f8d..6fd79191e 100644 --- a/src/states_screens/tracks_screen.hpp +++ b/src/states_screens/tracks_screen.hpp @@ -36,11 +36,9 @@ private: TracksScreen() : Screen("tracks.stkgui") { m_network_tracks = false; - m_max_lap = 0; } bool m_network_tracks; - unsigned m_max_lap; /** adds the tracks from the current track group into the tracks ribbon */ void buildTrackList(); @@ -50,7 +48,7 @@ private: public: /** \brief implement callback from parent class GUIEngine::Screen */ - virtual void loadedFromFile() OVERRIDE {}; + virtual void loadedFromFile() OVERRIDE; /** \brief implement callback from parent class GUIEngine::Screen */ virtual void eventCallback(GUIEngine::Widget* widget, @@ -66,16 +64,12 @@ public: /** \brief implement callback from parent class GUIEngine::Screen */ virtual void tearDown() OVERRIDE; - /** \brief implement callback from parent class GUIEngine::Screen */ - virtual void onUpdate(float dt) OVERRIDE; - /** \brief implement callback from parent class GUIEngine::Screen */ virtual bool onEscapePressed() OVERRIDE; void setFocusOnTrack(const std::string& trackName); void setNetworkTracks() { m_network_tracks = true; } - void setMaxLap(unsigned lap) { m_max_lap = lap; } }; #endif diff --git a/src/states_screens/waiting_for_others.cpp b/src/states_screens/waiting_for_others.cpp deleted file mode 100644 index 0dab10fdb..000000000 --- a/src/states_screens/waiting_for_others.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009-2015 Marianne Gagnon -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "states_screens/waiting_for_others.hpp" -#include "guiengine/widget.hpp" -#include "guiengine/widgets/label_widget.hpp" - -using namespace GUIEngine; - -DEFINE_SCREEN_SINGLETON( WaitingForOthersScreen ); - -// ----------------------------------------------------------------------------- -WaitingForOthersScreen::WaitingForOthersScreen() - : Screen("online/waiting_for_others.stkgui") -{ -} // WaitingForOthersScreen - -// ----------------------------------------------------------------------------- -void WaitingForOthersScreen::loadedFromFile() -{ -} // loadedFromFile - -// ----------------------------------------------------------------------------- -void WaitingForOthersScreen::eventCallback(Widget* widget, - const std::string& name, - const int player_id) -{ -} // eventCallback - -// ----------------------------------------------------------------------------- -void WaitingForOthersScreen::init() -{ - Screen::init(); -} //init - -// ----------------------------------------------------------------------------- -void WaitingForOthersScreen::onUpdate(float dt) -{ -} diff --git a/src/states_screens/waiting_for_others.hpp b/src/states_screens/waiting_for_others.hpp deleted file mode 100644 index b89b6a7a9..000000000 --- a/src/states_screens/waiting_for_others.hpp +++ /dev/null @@ -1,49 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009-2015 Marianne Gagnon -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef HEADER_WAITING_FOR_OTHERS_HPP -#define HEADER_WAITING_FOR_OTHERS_HPP - -#include "guiengine/screen.hpp" - -namespace GUIEngine { class Widget; } - -/** - * \brief Help screen, part 1 - * \ingroup states_screens - */ -class WaitingForOthersScreen : public GUIEngine::Screen, public GUIEngine::ScreenSingleton -{ - friend class GUIEngine::ScreenSingleton; - WaitingForOthersScreen(); - -public: - - /** \brief implement callback from parent class GUIEngine::Screen */ - virtual void loadedFromFile() OVERRIDE; - - /** \brief implement callback from parent class GUIEngine::Screen */ - virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, - const int playerID) OVERRIDE; - - /** \brief implement callback from parent class GUIEngine::Screen */ - virtual void init() OVERRIDE; - - virtual void onUpdate(float dt) OVERRIDE; -}; - -#endif From d6936c5746c9b1a9add8a522b644bf4b0257125f Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 16 Mar 2018 12:45:38 +0800 Subject: [PATCH 234/413] Add remaining time to vote message --- src/guiengine/message_queue.cpp | 5 +++-- src/guiengine/message_queue.hpp | 1 + src/network/protocols/client_lobby.cpp | 7 ++++--- src/network/protocols/server_lobby.cpp | 8 +++++++- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/guiengine/message_queue.cpp b/src/guiengine/message_queue.cpp index 2cbed4902..5d9c3ebd2 100644 --- a/src/guiengine/message_queue.cpp +++ b/src/guiengine/message_queue.cpp @@ -101,7 +101,7 @@ private: public: TextMessage(MessageQueue::MessageType mt, const core::stringw &message) : - Message(5.0f) + Message(mt == MessageQueue::MT_NETWORK_MSG ? 1.0f : 5.0f) { m_message_type = mt; m_message = message; @@ -111,7 +111,8 @@ public: m_render_type = "achievement-message::neutral"; else if (mt == MessageQueue::MT_ERROR) m_render_type = "error-message::neutral"; - else if (mt == MessageQueue::MT_GENERIC) + else if (mt == MessageQueue::MT_GENERIC || + mt == MessageQueue::MT_NETWORK_MSG) m_render_type = "generic-message::neutral"; else m_render_type = "friend-message::neutral"; diff --git a/src/guiengine/message_queue.hpp b/src/guiengine/message_queue.hpp index 863f3d96e..e3c1a20ca 100644 --- a/src/guiengine/message_queue.hpp +++ b/src/guiengine/message_queue.hpp @@ -40,6 +40,7 @@ namespace MessageQueue MT_ACHIEVEMENT, MT_ERROR, MT_GENERIC, + MT_NETWORK_MSG, MT_PROGRESS }; diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index d7df44573..aa9b161b9 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -295,13 +295,14 @@ void ClientLobby::displayPlayerVote(Event* event) core::stringw track_readable = track->getName(); int lap = data.getUInt8(); bool reversed = (bool)data.getUInt8(); + int t = data.getUInt8(); core::stringw yes = _("Yes"); core::stringw no = _("No"); //I18N: Vote message in network game from a player - core::stringw vote_msg = _("Track: %s, Laps: %d, Reversed: %s", - track_readable, lap, reversed ? yes : no); + core::stringw vote_msg = _("Track: %s, laps: %d, reversed: %s, " + "remaining time: %ds", track_readable, lap, reversed ? yes : no, t); vote_msg = player_name + vote_msg; - MessageQueue::add(MessageQueue::MT_GENERIC, vote_msg); + MessageQueue::add(MessageQueue::MT_NETWORK_MSG, vote_msg); } // displayPlayerVote //----------------------------------------------------------------------------- diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 7d848074d..d683bf5e0 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -1000,9 +1000,15 @@ void ServerLobby::playerVote(Event* event) other.addUInt8(LE_VOTE).encodeString(event->getPeer() ->getPlayerProfiles()[0]->getName()); other += data; + // Check if first vote, if so start counter (10 seconds voting time) + if (m_timeout == std::numeric_limits::max()) + m_timeout = (float)StkTime::getRealTime() + 10.0f; + float remaining_time = m_timeout - (float)StkTime::getRealTime(); + if (remaining_time < 0.0f) + remaining_time = 0.0f; + other.addUInt8((uint8_t)remaining_time); sendMessageToPeersChangingToken(&other); - // Save the vote } // playerVote // ---------------------------------------------------------------------------- From a5c1dbb4462188bed941ce3e678871d92873239f Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 16 Mar 2018 14:36:11 +0800 Subject: [PATCH 235/413] Move voting to server lobby --- src/network/game_setup.cpp | 24 +- src/network/game_setup.hpp | 24 +- src/network/protocols/client_lobby.cpp | 1 - src/network/protocols/lobby_protocol.cpp | 41 +- src/network/protocols/lobby_protocol.hpp | 3 + src/network/protocols/server_lobby.cpp | 109 +++++- src/network/protocols/server_lobby.hpp | 6 + src/network/race_config.cpp | 452 ----------------------- src/network/race_config.hpp | 136 ------- 9 files changed, 161 insertions(+), 635 deletions(-) delete mode 100644 src/network/race_config.cpp delete mode 100644 src/network/race_config.hpp diff --git a/src/network/game_setup.cpp b/src/network/game_setup.cpp index 5663dc434..2bfcbc631 100644 --- a/src/network/game_setup.cpp +++ b/src/network/game_setup.cpp @@ -18,8 +18,8 @@ #include "network/game_setup.hpp" +#include "config/player_manager.hpp" #include "karts/abstract_kart.hpp" -#include "network/race_config.hpp" #include "modes/world.hpp" #include "network/network_player_profile.hpp" #include "online/online_profile.hpp" @@ -27,23 +27,14 @@ #include "utils/log.hpp" //----------------------------------------------------------------------------- - GameSetup::GameSetup() { - m_race_config = new RaceConfig(); m_num_local_players = 0; m_local_master = 0; + m_laps = 0; + m_reverse = false; } // GameSetup -//----------------------------------------------------------------------------- - -GameSetup::~GameSetup() -{ - // remove all players - m_players.clear(); - delete m_race_config; -} // ~GameSetup - //----------------------------------------------------------------------------- void GameSetup::addPlayer(std::shared_ptr profile) { @@ -95,7 +86,16 @@ bool GameSetup::isLocalMaster(uint8_t player_id) } // isLocalMaster //----------------------------------------------------------------------------- +void GameSetup::loadWorld() +{ + assert(!m_track.empty()); + // Disable accidentally unlocking of a challenge + PlayerManager::getCurrentPlayer()->setCurrentChallenge(""); + race_manager->setReverseTrack(m_reverse); + race_manager->startSingleRace(m_track, m_laps, false/*from_overworld*/); +} // loadWorld +//----------------------------------------------------------------------------- void GameSetup::bindKartsToProfiles() { World::KartList karts = World::getWorld()->getKarts(); diff --git a/src/network/game_setup.hpp b/src/network/game_setup.hpp index fb423ea70..afb9e200c 100644 --- a/src/network/game_setup.hpp +++ b/src/network/game_setup.hpp @@ -31,7 +31,6 @@ #include class NetworkPlayerProfile; -class RaceConfig; // ============================================================================ /*! \class GameSetup @@ -46,9 +45,6 @@ private: /** Information about all connected players. */ std::vector > m_players; - /** The race configuration. */ - RaceConfig* m_race_config; - /** Stores the number of local players. */ int m_num_local_players; @@ -56,11 +52,17 @@ private: * kart selection screen. */ uint8_t m_local_master; + std::string m_track; + + unsigned m_laps; + + bool m_reverse; + public: // ------------------------------------------------------------------------ GameSetup(); // ------------------------------------------------------------------------ - ~GameSetup(); + ~GameSetup() {} // ------------------------------------------------------------------------ void addPlayer(std::shared_ptr profile); // ------------------------------------------------------------------------ @@ -78,9 +80,6 @@ public: /** Returns the nunber of local players. */ int getNumLocalPlayers() const { return m_num_local_players; } // ------------------------------------------------------------------------ - /** Returns the configuration for this race. */ - RaceConfig* getRaceConfig() { return m_race_config; } - // ------------------------------------------------------------------------ /** \brief Get the players that are in the game * \return A vector containing pointers on the players profiles. */ std::vector > getPlayers() const @@ -98,6 +97,15 @@ public: // ------------------------------------------------------------------------ /** Returns the id of the local master. */ int getLocalMasterID() const { return m_local_master; } + // ------------------------------------------------------------------------ + void setRace(const std::string& track, unsigned laps, bool reverse) + { + m_track = track; + m_laps = laps; + m_reverse = reverse; + } + // ------------------------------------------------------------------------ + void loadWorld(); }; #endif // GAME_SETUP_HPP diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index aa9b161b9..d135b70a1 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -30,7 +30,6 @@ #include "network/network_player_profile.hpp" #include "network/protocol_manager.hpp" #include "network/protocols/latency_protocol.hpp" -#include "network/race_config.hpp" #include "network/race_event_manager.hpp" #include "network/stk_host.hpp" #include "network/stk_peer.hpp" diff --git a/src/network/protocols/lobby_protocol.cpp b/src/network/protocols/lobby_protocol.cpp index 2c68388db..4cb148d64 100644 --- a/src/network/protocols/lobby_protocol.cpp +++ b/src/network/protocols/lobby_protocol.cpp @@ -28,7 +28,6 @@ #include "network/protocols/game_protocol.hpp" #include "network/protocols/game_events_protocol.hpp" #include "network/protocols/latency_protocol.hpp" -#include "network/race_config.hpp" #include "network/race_event_manager.hpp" #include "network/rewind_manager.hpp" #include "race/race_manager.hpp" @@ -74,6 +73,27 @@ void LobbyProtocol::loadWorld() race_manager->setNumPlayers(m_game_setup->getPlayerCount(), m_game_setup->getNumLocalPlayers()); + // Make sure that if there is only a single local player this player can + // use all input devices. + StateManager::ActivePlayer *ap = race_manager->getNumLocalPlayers()>1 + ? NULL + : StateManager::get()->getActivePlayer(0); + + input_manager->getDeviceManager()->setSinglePlayer(ap); + + Log::info("LobbyProtocol", "Player configuration ready."); + + // Load the actual world. + m_game_setup->loadWorld(); + World::getWorld()->setNetworkWorld(true); + GameProtocol::createInstance()->requestStart(); + std::make_shared()->requestStart(); + +} // loadWorld + +// ---------------------------------------------------------------------------- +void LobbyProtocol::configRemoteKart() +{ // Create the kart information for the race manager: // ------------------------------------------------- /*std::vector players = m_game_setup->getPlayers(); @@ -114,24 +134,7 @@ void LobbyProtocol::loadWorld() // Inform the race manager about the data for this kart. race_manager->setPlayerKart(i, rki); } // for i in players*/ - - // Make sure that if there is only a single local player this player can - // use all input devices. - StateManager::ActivePlayer *ap = race_manager->getNumLocalPlayers()>1 - ? NULL - : StateManager::get()->getActivePlayer(0); - - input_manager->getDeviceManager()->setSinglePlayer(ap); - - Log::info("LobbyProtocol", "Player configuration ready."); - - // Load the actual world. - m_game_setup->getRaceConfig()->loadWorld(); - World::getWorld()->setNetworkWorld(true); - GameProtocol::createInstance()->requestStart(); - std::make_shared()->requestStart(); - -} // loadWorld +} // configRemoteKart // ---------------------------------------------------------------------------- /** Terminates the LatencyProtocol. diff --git a/src/network/protocols/lobby_protocol.hpp b/src/network/protocols/lobby_protocol.hpp index 92c1cad69..5ea1bc88a 100644 --- a/src/network/protocols/lobby_protocol.hpp +++ b/src/network/protocols/lobby_protocol.hpp @@ -23,6 +23,7 @@ #include "network/network_string.hpp" class GameSetup; +class NetworkPlayerProfile; /*! * \class LobbyProtocol @@ -74,6 +75,8 @@ protected: /** Stores data about the online game to play. */ GameSetup* m_game_setup; + void configRemoteKart(); + public: /** Creates either a client or server lobby protocol as a singleton. */ diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index d683bf5e0..388a7e277 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -28,7 +28,6 @@ #include "network/protocols/connect_to_peer.hpp" #include "network/protocols/latency_protocol.hpp" #include "network/protocol_manager.hpp" -#include "network/race_config.hpp" #include "network/race_event_manager.hpp" #include "network/stk_host.hpp" #include "network/stk_peer.hpp" @@ -124,6 +123,7 @@ void ServerLobby::setup() // the server are ready: m_server_has_loaded_world.store(false); m_peers_ready.clear(); + m_peers_votes.clear(); m_server_delay = 0.0; Log::info("ServerLobby", "Reset server to initial state."); } // setup @@ -995,22 +995,117 @@ void ServerLobby::playerVote(Event* event) event->getPeer()->getPlayerProfiles().empty()) return; - NetworkString& data = event->data(); - NetworkString other = NetworkString(PROTOCOL_LOBBY_ROOM); - other.addUInt8(LE_VOTE).encodeString(event->getPeer() - ->getPlayerProfiles()[0]->getName()); - other += data; // Check if first vote, if so start counter (10 seconds voting time) if (m_timeout == std::numeric_limits::max()) m_timeout = (float)StkTime::getRealTime() + 10.0f; float remaining_time = m_timeout - (float)StkTime::getRealTime(); if (remaining_time < 0.0f) - remaining_time = 0.0f; + { + // Start world + handleVote(); + configRemoteKart(); + // Reset for next state usage + for (auto p : m_peers_ready) + { + p.second = false; + } + + m_state = LOAD_WORLD; + return; + } + + NetworkString& data = event->data(); + NetworkString other = NetworkString(PROTOCOL_LOBBY_ROOM); + other.addUInt8(LE_VOTE).encodeString(event->getPeer() + ->getPlayerProfiles()[0]->getName()); + other += data; + + std::string track_name; + data.decodeString(&track_name); + m_peers_votes.emplace(event->getPeerSP(), std::forward_as_tuple(track_name, + data.getUInt8(), (bool)data.getUInt8())); other.addUInt8((uint8_t)remaining_time); sendMessageToPeersChangingToken(&other); } // playerVote +// ---------------------------------------------------------------------------- +void ServerLobby::handleVote() +{ + // Default settings if no votes at all + RandomGenerator rg; + std::set::iterator it = m_available_kts.second.begin(); + std::advance(it, rg.get((int)m_available_kts.second.size())); + std::string final_track = *it; + unsigned final_laps = UserConfigParams::m_num_laps; + bool final_reverse = (bool)(final_track.size() % 2); + + std::map tracks; + std::map laps; + std::map reverses; + for (auto p : m_peers_votes) + { + if (p.first.expired()) + continue; + auto track_vote = tracks.find(std::get<0>(p.second)); + if (track_vote == tracks.end()) + tracks[std::get<0>(p.second)] = 1; + else + track_vote->second++; + auto lap_vote = laps.find(std::get<1>(p.second)); + if (lap_vote == laps.end()) + laps[std::get<1>(p.second)] = 1; + else + lap_vote->second++; + auto reverse_vote = reverses.find(std::get<2>(p.second)); + if (reverse_vote == reverses.end()) + reverses[std::get<2>(p.second)] = 1; + else + reverse_vote->second++; + } + + unsigned vote = 0; + auto track_vote = tracks.begin(); + for (auto c_vote = tracks.begin(); c_vote != tracks.end(); c_vote++) + { + if (c_vote->second > vote) + { + vote = c_vote->second; + track_vote = c_vote; + } + } + if (track_vote != tracks.end()) + final_track = track_vote->first; + + vote = 0; + auto lap_vote = laps.begin(); + for (auto c_vote = laps.begin(); c_vote != laps.end(); c_vote++) + { + if (c_vote->second > vote) + { + vote = c_vote->second; + lap_vote = c_vote; + } + } + if (lap_vote != laps.end()) + final_laps = lap_vote->first; + + vote = 0; + auto reverse_vote = reverses.begin(); + for (auto c_vote = reverses.begin(); c_vote != reverses.end(); c_vote++) + { + if (c_vote->second > vote) + { + vote = c_vote->second; + reverse_vote = c_vote; + } + } + if (reverse_vote != reverses.end()) + final_reverse = reverse_vote->first; + + m_game_setup->setRace(final_track, final_laps, final_reverse); +} // handleVote + // ---------------------------------------------------------------------------- /** Called from the RaceManager of the server when the world is loaded. Marks * the server to be ready to start the race. diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 2dd10b3b9..4a500769d 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -9,6 +9,7 @@ #include #include #include +#include class STKPeer; @@ -49,6 +50,10 @@ private: std::map, bool, std::owner_less > > m_peers_ready; + /** Vote from each peer. */ + std::map, std::tuple, + std::owner_less > > m_peers_votes; + /** Keeps track of an artificial server delay (which makes sure that the * data from all clients has arrived when the server computes a certain * timestep.(. It stores the real time since epoch + delta (atm 0.1 @@ -88,6 +93,7 @@ private: void updatePlayerList(); void updateServerOwner(); bool checkPeersReady() const; + void handleVote(); public: ServerLobby(); diff --git a/src/network/race_config.cpp b/src/network/race_config.cpp deleted file mode 100644 index 26f57e218..000000000 --- a/src/network/race_config.cpp +++ /dev/null @@ -1,452 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2014-2015 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/race_config.hpp" - -#include "config/user_config.hpp" -#include "network/network_config.hpp" -#include "race/race_manager.hpp" -#include "utils/log.hpp" - -#include - -/** \brief Gets the element with the highest count in a std::map. - * \param histogram : A pointer to the histogram. - * \return The key of type S that has the highest second value. - */ -template -S getHighestInHistogram(std::map* histogram, S default_value) -{ - if(histogram->empty()) - return default_value; - - S best_item = histogram->begin()->first; - uint8_t highest_count = histogram->begin()->second; - for (typename std::map::iterator it = histogram->begin(); - it != histogram->end(); it++) - { - if (it->second > highest_count) - { - highest_count = it->second; - best_item = it->first; - } - } - return best_item; -} // getHighestInHistogram - -//----------------------------------------------------------------------------- -//--------------------------------- TrackVote -------------------------------- -//----------------------------------------------------------------------------- -/** Constructor for a track vote. - */ -TrackVote::TrackVote() -{ - has_voted_laps = false; - has_voted_track = false; - has_voted_reversed = false; -} // TrackVote - -//----------------------------------------------------------------------------- -/** Sets that this vote is for the specified track. - */ -void TrackVote::voteTrack(const std::string &track) -{ - track_info.track = track; - has_voted_track = true; -} // voteTrack - -//----------------------------------------------------------------------------- -/** Sets if this vote is for normal or reversed driving. - * \param reversed True if this vote is for reversed racing. - */ -void TrackVote::voteReversed(bool reversed) -{ - track_info.reversed = reversed; - has_voted_reversed = true; -} // voteReversed - -//----------------------------------------------------------------------------- -/** Votes for the number of laps. - * \param laps Numger of laps. - */ -void TrackVote::voteLaps(uint8_t laps) -{ - track_info.laps = laps; - has_voted_laps = true; -} // voteLaps - -//----------------------------------------------------------------------------- -//--------------------------------- RaceVote --------------------------------- -//----------------------------------------------------------------------------- -/** Constructor for a race vote. - */ -RaceVote::RaceVote() -{ - m_has_voted_major = false; - m_has_voted_minor = false; - m_has_voted_races_count = false; - m_major_mode = 0; - m_minor_mode = 0; - m_races_count = 0; - m_tracks_vote.resize(1); -} // RaceVote - -//----------------------------------------------------------------------------- -/** Sets the selected major race vote. - */ -void RaceVote::voteMajor(uint32_t major) -{ - m_has_voted_major = true; - m_major_mode = major; -} // voteMajor - -//----------------------------------------------------------------------------- -/** Sets the vote for race count. - */ -void RaceVote::voteRaceCount(uint8_t count) -{ - m_has_voted_races_count = true; - m_races_count = count; -} // voteRaceCount - -//----------------------------------------------------------------------------- -/** Sets vote for minor race mode. - */ -void RaceVote::voteMinor(uint32_t minor) -{ - m_has_voted_minor = true; - m_minor_mode = minor; -} // voteMinor - -//----------------------------------------------------------------------------- -/** Sets a track vote. - * \param track Name of the track. - */ -void RaceVote::voteTrack(const std::string &track, uint8_t track_number) -{ - m_tracks_vote[track_number].voteTrack(track); -} // voteTrack - -//----------------------------------------------------------------------------- -/** Sets a vote for reveresed racing. - */ -void RaceVote::voteReversed(bool reversed, uint8_t track_number) -{ - m_tracks_vote[track_number].voteReversed(reversed); -} // voteReversed - -//----------------------------------------------------------------------------- -/** Sets a vote for number of laps. - */ -void RaceVote::voteLaps(uint8_t laps, uint8_t track_number) -{ - m_tracks_vote[track_number].voteLaps(laps); -} // voteLaps - -//----------------------------------------------------------------------------- -bool RaceVote::hasVotedMajor() const -{ - return m_has_voted_major; -} // hasVotedMajor -//----------------------------------------------------------------------------- -bool RaceVote::hasVotedRacesCount() const -{ - return m_has_voted_races_count; -} // hasVotedRacesCount - -//----------------------------------------------------------------------------- -bool RaceVote::hasVotedMinor() const -{ - return m_has_voted_minor; -} // hasVotedMinor - -//----------------------------------------------------------------------------- -bool RaceVote::hasVotedTrack(uint8_t track_number) const -{ - return m_tracks_vote[track_number].has_voted_track; -} // hasVotedTrack - -//----------------------------------------------------------------------------- -bool RaceVote::hasVotedReversed(uint8_t track_number) const -{ - return m_tracks_vote[track_number].has_voted_reversed; -} // hasVotedReversed - -//----------------------------------------------------------------------------- -bool RaceVote::hasVotedLaps(uint8_t track_number) const -{ - return m_tracks_vote[track_number].has_voted_laps; -} // hasVotedLaps - -//----------------------------------------------------------------------------- -uint8_t RaceVote::getMajorVote() const -{ - return m_major_mode; -} // getMajorVote - -//----------------------------------------------------------------------------- -uint8_t RaceVote::getRacesCountVote() const -{ - return m_races_count; -} // getRacesCountVote - -//----------------------------------------------------------------------------- -uint8_t RaceVote::getMinorVote() const -{ - return m_minor_mode; -} // getMinorVote - -//----------------------------------------------------------------------------- -const std::string &RaceVote::getTrackVote(uint8_t track_number) const -{ - return m_tracks_vote[track_number].track_info.track; -} // getTrackVote - -//----------------------------------------------------------------------------- -bool RaceVote::getReversedVote(uint8_t track_number) const -{ - return m_tracks_vote[track_number].track_info.reversed; -} // getReversedVote - -//----------------------------------------------------------------------------- -uint8_t RaceVote::getLapsVote(uint8_t track_number) const -{ - return m_tracks_vote[track_number].track_info.laps; -} // getLapsVote - -//----------------------------------------------------------------------------- -//--------------------------------- RaceConfig ------------------------------- -//----------------------------------------------------------------------------- - -RaceConfig::RaceConfig() -{ - m_max_players = NetworkConfig::get()->getMaxPlayers(); -} // RaceConfig - -//----------------------------------------------------------------------------- -void RaceConfig::setPlayerMajorVote(uint8_t player_id, uint32_t major) -{ - Log::info("RaceConfig", "Player %d voted for major %d", player_id, major); - m_votes[player_id].voteMajor(major); -} // setPlayerMajorVote - -//----------------------------------------------------------------------------- -void RaceConfig::setPlayerRaceCountVote(uint8_t player_id, uint8_t count) -{ - Log::info("RaceConfig", "Player %d voted for %d races in GP", - player_id, count); - m_votes[player_id].voteRaceCount(count); -} // setPlayerRaceCountVote - -//----------------------------------------------------------------------------- -void RaceConfig::setPlayerMinorVote(uint8_t player_id, uint32_t minor) -{ - Log::info("RaceConfig", "Player %d voted for minor %d", player_id, minor); - m_votes[player_id].voteMinor(minor); -} // setPlayerMinorVote - -//----------------------------------------------------------------------------- -void RaceConfig::setPlayerTrackVote(uint8_t player_id, - const std::string &track, uint8_t track_nb) -{ - Log::info("RaceConfig", "Player %d voted for track %s", - player_id, track.c_str()); - m_votes[player_id].voteTrack(track, track_nb); -} // setPlayerTrackVote - -//----------------------------------------------------------------------------- -void RaceConfig::setPlayerReversedVote(uint8_t player_id, bool reversed, - uint8_t track_nb) -{ - if (reversed) - Log::info("RaceConfig", "Player %d voted map %d to be reversed", - player_id, track_nb); - else - Log::info("RaceConfig", "Player %d voted map %d NOT to be reversed", - player_id, track_nb); - m_votes[player_id].voteReversed(reversed, track_nb); -} // setPlayerReversedVote - -//----------------------------------------------------------------------------- -void RaceConfig::setPlayerLapsVote(uint8_t player_id, uint8_t lap_count, - uint8_t track_nb) -{ - Log::info("RaceConfig", "Player %d voted map %d to have %d laps", - player_id, track_nb, lap_count); - m_votes[player_id].voteLaps(lap_count, track_nb); -} // setPlayerLapsVote - -//----------------------------------------------------------------------------- -/** Computes the selected race mode. - */ -void RaceConfig::computeRaceMode() -{ - // calculate the race type and number of tracks (in GP mode). - std::map major_histogram; - std::map races_count_histogram; - std::map minor_histogram; - for (unsigned int i = 0; i < m_max_players; i++) - { - // increase the count of votes - if (m_votes[i].hasVotedMajor()) - { - try - { - major_histogram.at(m_votes[i].getMajorVote()) ++; - } - catch (const std::out_of_range&) // doesn't exist in the map - { - major_histogram[m_votes[i].getMajorVote()] = 1; - } - } - else if (m_votes[i].hasVotedRacesCount()) - { - try - { - races_count_histogram.at(m_votes[i].getRacesCountVote()) ++; - } - catch (const std::out_of_range&) // doesn't exist in the map - { - races_count_histogram[m_votes[i].getRacesCountVote()] = 1; - } - } - else if (m_votes[i].hasVotedMinor()) - { - try - { - minor_histogram.at(m_votes[i].getMinorVote()) ++; - } - catch (const std::out_of_range&) // doesn't exist in the map - { - minor_histogram[m_votes[i].getMinorVote()] = 1; - } - } - } - // now we know : - m_major_mode = getHighestInHistogram(&major_histogram, - (int)RaceManager::MAJOR_MODE_SINGLE); - m_minor_mode = getHighestInHistogram(&minor_histogram, - (int)RaceManager::MINOR_MODE_NORMAL_RACE); - - if (m_major_mode == RaceManager::MAJOR_MODE_GRAND_PRIX) - { - m_races_count = getHighestInHistogram(&races_count_histogram, 1); - m_tracks.resize(m_races_count); - } - else - { - m_tracks.resize(1); - m_races_count = 1; - } - - Log::info("RaceConfig", "Major mode will be %d with %d races. Minor is %d", - m_major_mode, m_races_count, m_minor_mode); -} // computeRaceMode - -// ---------------------------------------------------------------------------- -void RaceConfig::computeNextTrack() -{ - for (unsigned int j = 0; j < m_races_count; j++) - { - // first create histograms of the votes - std::map tracks_histogram; - std::map reversed_histogram; - std::map laps_histogram; - for (unsigned int i = 0; i < m_max_players; i++) - { - // increase the count of votes - if (m_votes[i].hasVotedTrack()) - { - try // maps - { - tracks_histogram.at(m_votes[i].getTrackVote()) ++; - } - catch (const std::out_of_range&) // doesn't exist in the map - { - tracks_histogram[m_votes[i].getTrackVote()] = 1; - } - } - if (m_votes[i].hasVotedReversed()) - { - try // reversed - { - reversed_histogram.at(m_votes[i].getReversedVote()) ++; - } - catch (const std::out_of_range&) // doesn't exist in the map - { - reversed_histogram[m_votes[i].getReversedVote()] = 1; - } - } - if (m_votes[i].hasVotedLaps()) - { - try // laps - { - laps_histogram.at(m_votes[i].getLapsVote()) ++; - } - catch (const std::out_of_range&) // doesn't exist in the mapt - { - laps_histogram[m_votes[i].getLapsVote()] = 1; - } - } - } - // now find the highest votes - m_tracks[j].track = getHighestInHistogram(&tracks_histogram, - UserConfigParams::m_last_track); - m_tracks[j].reversed = getHighestInHistogram(&reversed_histogram, false); - m_tracks[j].laps = getHighestInHistogram(&laps_histogram, - UserConfigParams::m_num_laps); - if (m_tracks[j].reversed) - Log::info("RaceConfig", - "Race %d will be on %s with %d laps and reversed", - j, m_tracks[j].track.c_str(), m_tracks[j].laps); - else - Log::info("RaceConfig", "Race %d will be on %s with %d laps", - j, m_tracks[j].track.c_str(), m_tracks[j].laps); - } -} // computeNextTrack - -//----------------------------------------------------------------------------- -/** Computes the selected setting (based on the users' vote) and sets them - * in the race manager. Then it loads the world. - */ -void RaceConfig::loadWorld() -{ - computeRaceMode(); - computeNextTrack(); - race_manager->startSingleRace(m_tracks[0].track, m_tracks[0].laps, - m_tracks[0].reversed); -} // loadWorld - -//----------------------------------------------------------------------------- -const TrackInfo* RaceConfig::getNextTrackInfo() const -{ - return &m_tracks[0]; -} // getNextTrackInfo - -//----------------------------------------------------------------------------- -int RaceConfig::getNumTrackVotes() const -{ - int count = 0; - - for (auto entry : m_votes) - { - if (entry.second.hasVotedTrack()) - count++; - } - - return count; -} // getNumTrackVotes \ No newline at end of file diff --git a/src/network/race_config.hpp b/src/network/race_config.hpp deleted file mode 100644 index e88979862..000000000 --- a/src/network/race_config.hpp +++ /dev/null @@ -1,136 +0,0 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2014-2015 SuperTuxKart-Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef RACE_CONFIG_HPP -#define RACE_CONFIG_HPP - -#include -#include -#include -#include "utils/types.hpp" - -/** Stores the name of a track, number of laps, and reverse driving. - */ -class TrackInfo -{ -public: - std::string track; - bool reversed; - uint8_t laps; - TrackInfo() { laps = 0; reversed = false; } -}; // TrackInfo - -// ============================================================================ -/** Stores a vote about the name of a track, number of laps, and reverse - * driving. - */ -class TrackVote -{ -public: - TrackVote(); - - void voteTrack(const std::string &track); - void voteReversed(bool reversed); - void voteLaps(uint8_t laps); - - TrackInfo track_info; - - bool has_voted_track; - bool has_voted_reversed; - bool has_voted_laps; -}; // class TrackVote - -// ============================================================================ -class RaceVote -{ - public: - RaceVote(); - - void voteMajor(uint32_t major); - void voteRaceCount(uint8_t count); - void voteMinor(uint32_t minor); - void voteTrack(const std::string &track, uint8_t track_number = 0); - void voteReversed(bool reversed, uint8_t track_number = 0); - void voteLaps(uint8_t laps, uint8_t track_number = 0); - - bool hasVotedMajor() const; - bool hasVotedRacesCount() const; - bool hasVotedMinor() const; - bool hasVotedTrack(uint8_t track_number = 0) const; - bool hasVotedReversed(uint8_t track_number = 0) const; - bool hasVotedLaps(uint8_t track_number = 0) const; - - uint8_t getMajorVote() const; - uint8_t getRacesCountVote() const; - uint8_t getMinorVote() const; - const std::string &getTrackVote(uint8_t track_number = 0) const; - bool getReversedVote(uint8_t track_number = 0) const; - uint8_t getLapsVote(uint8_t track_number = 0) const; - - private: - uint32_t m_major_mode; - uint32_t m_minor_mode; - uint8_t m_races_count; //!< Stores the number of races that will be in a GP - bool m_has_voted_major; - bool m_has_voted_minor; - bool m_has_voted_races_count; - std::vector m_tracks_vote; -}; // RaceVote - -// ============================================================================ -class RaceConfig -{ -private: - void computeRaceMode(); - void computeNextTrack(); -public: - RaceConfig(); - - void setPlayerMajorVote(uint8_t player_id, uint32_t major); - void setPlayerRaceCountVote(uint8_t player_id, uint8_t count); - void setPlayerMinorVote(uint8_t player_id, uint32_t minor); - void setPlayerTrackVote(uint8_t player_id, const std::string &track, - uint8_t track_nb = 0); - void setPlayerReversedVote(uint8_t player_id, bool reversed, - uint8_t track_nb = 0); - void setPlayerLapsVote(uint8_t player_id, uint8_t lap_count, - uint8_t track_nb = 0); - - void loadWorld(); - - const TrackInfo* getNextTrackInfo() const; - bool getReverse() const; - bool getLapCount() const; - int getNumTrackVotes() const; - - const RaceVote& getRaceVote(int global_player_id) { return m_votes[global_player_id]; } - int getMaxPlayers() const { return m_max_players; } - -protected: - std::vector m_tracks; - int m_minor_mode; - int m_major_mode; - unsigned int m_races_count; - - /** Key: globalPlayerID */ - std::map m_votes; - - uint8_t m_max_players; -}; // class RaceConfig - - -#endif // RACE_CONFIG_HPP From 82be7572a1c38fba08811f9a26b9895990efe72e Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 16 Mar 2018 17:11:23 +0800 Subject: [PATCH 236/413] Use the new method to connect / start game --- src/modes/world.cpp | 12 ++-- src/network/game_setup.cpp | 54 ----------------- src/network/game_setup.hpp | 25 +++++--- src/network/network_player_profile.hpp | 4 +- src/network/protocols/client_lobby.cpp | 49 ++++++++++++++- src/network/protocols/client_lobby.hpp | 2 + src/network/protocols/lobby_protocol.cpp | 28 ++++----- src/network/protocols/lobby_protocol.hpp | 6 +- src/network/protocols/server_lobby.cpp | 76 +++++++++++++++++------- src/network/protocols/server_lobby.hpp | 2 +- src/network/stk_peer.cpp | 2 +- 11 files changed, 152 insertions(+), 108 deletions(-) diff --git a/src/modes/world.cpp b/src/modes/world.cpp index e356ebf70..cbdcccbd1 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -341,6 +341,13 @@ AbstractKart *World::createKart(const std::string &kart_ident, int index, gk = ReplayPlay::get()->getNumGhostKart(); std::shared_ptr ri = std::make_shared(); + if (global_player_id > -1 && race_manager->getKartInfo(global_player_id) + .getDefaultKartColor() > 0.0f) + { + ri->setHue(race_manager->getKartInfo(global_player_id) + .getDefaultKartColor()); + } + int position = index+1; btTransform init_pos = getStartTransform(index - gk); AbstractKart *new_kart; @@ -357,10 +364,7 @@ AbstractKart *World::createKart(const std::string &kart_ident, int index, { case RaceManager::KT_PLAYER: { - controller = new LocalPlayerController(new_kart, - - local_player_id); - + controller = new LocalPlayerController(new_kart, local_player_id); const float hue = StateManager::get()->getActivePlayer(local_player_id) ->getConstProfile()->getDefaultKartColor(); if (hue > 0.0f) diff --git a/src/network/game_setup.cpp b/src/network/game_setup.cpp index 2bfcbc631..e26a1e0b8 100644 --- a/src/network/game_setup.cpp +++ b/src/network/game_setup.cpp @@ -66,25 +66,6 @@ void GameSetup::update(bool remove_disconnected_players) return; } // removePlayer -//----------------------------------------------------------------------------- -/** Sets the player id of the local master. - * \param player_id The id of the player who is the local master. - */ -void GameSetup::setLocalMaster(uint8_t player_id) -{ - m_local_master = player_id; -} // setLocalMaster - -//----------------------------------------------------------------------------- -/** Returns true if the player id is the local game master (used in the - * network game selection. - * \param Local player id to test. - */ -bool GameSetup::isLocalMaster(uint8_t player_id) -{ - return m_local_master == player_id; -} // isLocalMaster - //----------------------------------------------------------------------------- void GameSetup::loadWorld() { @@ -94,38 +75,3 @@ void GameSetup::loadWorld() race_manager->setReverseTrack(m_reverse); race_manager->startSingleRace(m_track, m_laps, false/*from_overworld*/); } // loadWorld - -//----------------------------------------------------------------------------- -void GameSetup::bindKartsToProfiles() -{ - World::KartList karts = World::getWorld()->getKarts(); - - /*for (unsigned int i = 0; i < m_players.size(); i++) - { - Log::info("GameSetup", "Player %d has id %d and kart %s", i, - m_players[i]->getGlobalPlayerId(), - m_players[i]->getKartName().c_str()); - } - for (unsigned int i = 0; i < karts.size(); i++) - { - Log::info("GameSetup", "Kart %d has id %d and kart %s", i, - karts[i]->getWorldKartId(), karts[i]->getIdent().c_str()); - } - for (unsigned int j = 0; j < m_players.size(); j++) - { - bool found = false; - for (unsigned int i = 0 ; i < karts.size(); i++) - { - if (karts[i]->getIdent() == m_players[j]->getKartName()) - { - m_players[j]->setWorldKartID(karts[i]->getWorldKartId()); - found = true; - break; - } - } - if (!found) - { - Log::error("GameSetup", "Error while binding world kart ids to players profiles."); - } - }*/ -} // bindKartsToProfiles diff --git a/src/network/game_setup.hpp b/src/network/game_setup.hpp index afb9e200c..7124cfe34 100644 --- a/src/network/game_setup.hpp +++ b/src/network/game_setup.hpp @@ -24,6 +24,7 @@ #include "network/remote_kart_info.hpp" +#include #include #include #include @@ -68,26 +69,34 @@ public: // ------------------------------------------------------------------------ void update(bool remove_disconnected_players); // ------------------------------------------------------------------------ - void bindKartsToProfiles(); - // ------------------------------------------------------------------------ - void setLocalMaster(uint8_t player_id); - // ------------------------------------------------------------------------ - bool isLocalMaster(uint8_t player_id); - // ------------------------------------------------------------------------ /** Sets the number of local players. */ void setNumLocalPlayers(int n) { m_num_local_players = n; } // ------------------------------------------------------------------------ /** Returns the nunber of local players. */ int getNumLocalPlayers() const { return m_num_local_players; } // ------------------------------------------------------------------------ - /** \brief Get the players that are in the game + /** \brief Get the players that are / were in the game * \return A vector containing pointers on the players profiles. */ - std::vector > getPlayers() const + const std::vector >& getPlayers() const { std::lock_guard lock(m_players_mutex); return m_players; } // getPlayers // ------------------------------------------------------------------------ + /** \brief Get the players that are in the game + * \return A vector containing pointers on the players profiles. */ + std::vector > + getConnectedPlayers() const + { + std::lock_guard lock(m_players_mutex); + std::vector > players; + std::transform(m_players.begin(), m_players.end(), + std::back_inserter(players), + std::bind(&std::weak_ptr::lock, + std::placeholders::_1)); + return players; + } // getConnectedPlayers + // ------------------------------------------------------------------------ /** Returns the number of connected players. */ unsigned getPlayerCount() { diff --git a/src/network/network_player_profile.hpp b/src/network/network_player_profile.hpp index f85884a70..8feb37b3d 100644 --- a/src/network/network_player_profile.hpp +++ b/src/network/network_player_profile.hpp @@ -91,7 +91,7 @@ public: int getGlobalPlayerId() const { return m_global_player_id; } // ------------------------------------------------------------------------ /** Returns the host id of this player. */ - uint8_t getHostId() const { return m_host_id; } + uint32_t getHostId() const { return m_host_id; } // ------------------------------------------------------------------------ /** Sets the kart name for this player. */ void setKartName(const std::string &kart_name) { m_kart_name = kart_name; } @@ -116,7 +116,7 @@ public: // ------------------------------------------------------------------------ float getDefaultKartColor() const { return m_default_kart_color; } // ------------------------------------------------------------------------ - uint32_t getOnlineID() const { return m_online_id; } + uint32_t getOnlineId() const { return m_online_id; } // ------------------------------------------------------------------------ bool isOfflineAccount() const { return m_online_id == 0; } // ------------------------------------------------------------------------ diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index d135b70a1..86586958a 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -105,7 +105,6 @@ void ClientLobby::doneWithResults() } // doneWithResults //----------------------------------------------------------------------------- - bool ClientLobby::notifyEvent(Event* event) { assert(m_game_setup); // assert that the setup exists @@ -118,7 +117,7 @@ bool ClientLobby::notifyEvent(Event* event) switch(message_type) { case LE_START_SELECTION: startSelection(event); break; - case LE_LOAD_WORLD: loadWorld(); break; + case LE_LOAD_WORLD: addAllPlayers(event); break; case LE_RACE_FINISHED: raceFinished(event); break; case LE_EXIT_RESULT: exitResultScreen(event); break; case LE_UPDATE_PLAYER_LIST: updatePlayerList(event); break; @@ -131,7 +130,6 @@ bool ClientLobby::notifyEvent(Event* event) } // notifyEvent //----------------------------------------------------------------------------- - bool ClientLobby::notifyEventAsynchronous(Event* event) { assert(m_game_setup); // assert that the setup exists @@ -186,6 +184,51 @@ bool ClientLobby::notifyEventAsynchronous(Event* event) return false; } // notifyEventAsynchronous +//----------------------------------------------------------------------------- +void ClientLobby::addAllPlayers(Event* event) +{ + if (!checkDataSize(event, 1)) + { + // If recieved invalid message for players leave now + STKHost::get()->disconnectAllPeers(false/*timeout_waiting*/); + STKHost::get()->requestShutdown(); + return; + } + NetworkString& data = event->data(); + std::string track_name; + data.decodeString(&track_name); + uint8_t lap = data.getUInt8(); + bool reverse = (bool)data.getUInt8(); + m_game_setup->setRace(track_name, lap, reverse); + + std::shared_ptr peer = event->getPeerSP(); + peer->cleanPlayerProfiles(); + + std::vector > players; + unsigned player_count = data.getUInt8(); + assert(m_game_setup->getPlayerCount() == 0); + + for (unsigned i = 0; i < player_count; i++) + { + core::stringw player_name; + data.decodeStringW(&player_name); + uint32_t host_id = data.getUInt32(); + float kart_color = data.getFloat(); + uint32_t online_id = data.getUInt32(); + PerPlayerDifficulty ppd = (PerPlayerDifficulty)data.getUInt8(); + auto player = std::make_shared(peer, player_name, + host_id, kart_color, online_id, ppd); + std::string kart_name; + data.decodeString(&kart_name); + player->setKartName(kart_name); + peer->addPlayer(player); + m_game_setup->addPlayer(player); + players.push_back(player); + } + configRemoteKart(players); + loadWorld(); +} // addAllPlayers + //----------------------------------------------------------------------------- void ClientLobby::update(float dt) { diff --git a/src/network/protocols/client_lobby.hpp b/src/network/protocols/client_lobby.hpp index c09686e94..24eabe54d 100644 --- a/src/network/protocols/client_lobby.hpp +++ b/src/network/protocols/client_lobby.hpp @@ -44,6 +44,8 @@ private: std::set m_available_karts; std::set m_available_tracks; + void addAllPlayers(Event* event); + public: ClientLobby(); virtual ~ClientLobby(); diff --git a/src/network/protocols/lobby_protocol.cpp b/src/network/protocols/lobby_protocol.cpp index 4cb148d64..6088c063e 100644 --- a/src/network/protocols/lobby_protocol.cpp +++ b/src/network/protocols/lobby_protocol.cpp @@ -66,13 +66,6 @@ void LobbyProtocol::loadWorld() // This creates the network world. RaceEventManager::getInstance()->start(); - // The number of karts includes the AI karts, which are not supported atm - race_manager->setNumKarts(m_game_setup->getPlayerCount()); - - // Set number of global and local players. - race_manager->setNumPlayers(m_game_setup->getPlayerCount(), - m_game_setup->getNumLocalPlayers()); - // Make sure that if there is only a single local player this player can // use all input devices. StateManager::ActivePlayer *ap = race_manager->getNumLocalPlayers()>1 @@ -81,8 +74,6 @@ void LobbyProtocol::loadWorld() input_manager->getDeviceManager()->setSinglePlayer(ap); - Log::info("LobbyProtocol", "Player configuration ready."); - // Load the actual world. m_game_setup->loadWorld(); World::getWorld()->setNetworkWorld(true); @@ -92,15 +83,22 @@ void LobbyProtocol::loadWorld() } // loadWorld // ---------------------------------------------------------------------------- -void LobbyProtocol::configRemoteKart() +void LobbyProtocol::configRemoteKart( + const std::vector >& players) const { + // The number of karts includes the AI karts, which are not supported atm + race_manager->setNumKarts(players.size()); + + // Set number of global and local players. + race_manager->setNumPlayers(players.size(), + m_game_setup->getNumLocalPlayers()); + // Create the kart information for the race manager: // ------------------------------------------------- - /*std::vector players = m_game_setup->getPlayers(); int local_player_id = 0; for (unsigned int i = 0; i < players.size(); i++) { - NetworkPlayerProfile* profile = players[i]; + std::shared_ptr profile = players[i]; bool is_local = profile->isLocalPlayer(); // All non-local players are created here. This means all players @@ -123,7 +121,8 @@ void LobbyProtocol::configRemoteKart() profile->getName(), profile->getHostId(), !is_local); - rki.setGlobalPlayerId(profile->getGlobalPlayerId()); + rki.setGlobalPlayerId(i); + rki.setDefaultKartColor(profile->getDefaultKartColor()); rki.setPerPlayerDifficulty(profile->getPerPlayerDifficulty()); if (is_local) { @@ -133,7 +132,8 @@ void LobbyProtocol::configRemoteKart() // Inform the race manager about the data for this kart. race_manager->setPlayerKart(i, rki); - } // for i in players*/ + } // for i in players + Log::info("LobbyProtocol", "Player configuration ready."); } // configRemoteKart // ---------------------------------------------------------------------------- diff --git a/src/network/protocols/lobby_protocol.hpp b/src/network/protocols/lobby_protocol.hpp index 5ea1bc88a..76f6518e6 100644 --- a/src/network/protocols/lobby_protocol.hpp +++ b/src/network/protocols/lobby_protocol.hpp @@ -25,6 +25,9 @@ class GameSetup; class NetworkPlayerProfile; +#include +#include + /*! * \class LobbyProtocol * \brief Base class for both client and server lobby. The lobbies are started @@ -75,7 +78,8 @@ protected: /** Stores data about the online game to play. */ GameSetup* m_game_setup; - void configRemoteKart(); + void configRemoteKart( + const std::vector >& players) const; public: diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 388a7e277..cc45df9ff 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -317,6 +317,48 @@ void ServerLobby::asynchronousUpdate() m_state = RACING; World::getWorld()->setReadyToRace(); } + break; + case SELECTING: + if (m_timeout < (float)StkTime::getRealTime()) + { + auto result = handleVote(); + // Remove disconnected player (if any) one last time + m_game_setup->update(true); + auto players = m_game_setup->getConnectedPlayers(); + // TODO: sort players for grand prix here + configRemoteKart(players); + // Reset for next state usage + for (auto p : m_peers_ready) + { + p.second = false; + } + m_state = LOAD_WORLD; + NetworkString* load_world = getNetworkString(); + load_world->setSynchronous(true); + load_world->addUInt8(LE_LOAD_WORLD).encodeString(std::get<0>(result)) + .addUInt8(std::get<1>(result)).addUInt8(std::get<2>(result)) + .addUInt8((uint8_t)players.size()); + for (auto player : players) + { + load_world->encodeString(player->getName()) + .addUInt32(player->getHostId()) + .addFloat(player->getDefaultKartColor()) + .addUInt32(player->getOnlineId()) + .addUInt8(player->getPerPlayerDifficulty()); + if (player->getKartName().empty()) + { + RandomGenerator rg; + std::set::iterator it = + m_available_kts.first.begin(); + std::advance(it, rg.get((int)m_available_kts.first.size())); + player->setKartName(*it); + } + load_world->encodeString(player->getKartName()); + } + sendMessageToPeersChangingToken(load_world); + delete load_world; + } + break; default: break; } @@ -883,7 +925,7 @@ void ServerLobby::updatePlayerList() pl->addUInt8(LE_UPDATE_PLAYER_LIST).addUInt8((uint8_t)all_profiles.size()); for (auto profile : all_profiles) { - pl->addUInt32(profile->getHostId()).addUInt32(profile->getOnlineID()) + pl->addUInt32(profile->getHostId()).addUInt32(profile->getOnlineId()) .encodeString(profile->getName()); uint8_t server_owner = 0; if (m_server_owner.lock() == profile->getPeer()) @@ -963,15 +1005,16 @@ void ServerLobby::kartSelectionRequested(Event* event) data.decodeString(&kart); if (m_available_kts.first.find(kart) == m_available_kts.first.end()) { + // Will be reset to a random one later Log::debug("ServerLobby", "Player %d from peer %d chose unknown " "kart %s, use a random one", i, peer->getHostId(), kart.c_str()); - RandomGenerator rg; - std::set::iterator it = m_available_kts.first.begin(); - std::advance(it, rg.get((int)m_available_kts.first.size())); - kart = *it; + continue; + } + else + { + peer->getPlayerProfiles()[i]->setKartName(kart); } - peer->getPlayerProfiles()[i]->setKartName(kart); Log::verbose("ServerLobby", "Player %d from peer %d chose %s", i, peer->getHostId(), kart.c_str()); } @@ -1001,17 +1044,7 @@ void ServerLobby::playerVote(Event* event) float remaining_time = m_timeout - (float)StkTime::getRealTime(); if (remaining_time < 0.0f) { - // Start world - handleVote(); - configRemoteKart(); - // Reset for next state usage - for (auto p : m_peers_ready) - { - p.second = false; - } - - m_state = LOAD_WORLD; - return; + remaining_time = 0.0f; } NetworkString& data = event->data(); @@ -1022,15 +1055,17 @@ void ServerLobby::playerVote(Event* event) std::string track_name; data.decodeString(&track_name); - m_peers_votes.emplace(event->getPeerSP(), std::forward_as_tuple(track_name, - data.getUInt8(), (bool)data.getUInt8())); + uint8_t lap = data.getUInt8(); + bool reverse = (bool)data.getUInt8(); + m_peers_votes[event->getPeerSP()] = + std::make_tuple(track_name,lap, reverse); other.addUInt8((uint8_t)remaining_time); sendMessageToPeersChangingToken(&other); } // playerVote // ---------------------------------------------------------------------------- -void ServerLobby::handleVote() +std::tuple ServerLobby::handleVote() { // Default settings if no votes at all RandomGenerator rg; @@ -1104,6 +1139,7 @@ void ServerLobby::handleVote() final_reverse = reverse_vote->first; m_game_setup->setRace(final_track, final_laps, final_reverse); + return std::make_tuple(final_track, final_laps, final_reverse); } // handleVote // ---------------------------------------------------------------------------- diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 4a500769d..0b143a063 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -93,7 +93,7 @@ private: void updatePlayerList(); void updateServerOwner(); bool checkPeersReady() const; - void handleVote(); + std::tuple handleVote(); public: ServerLobby(); diff --git a/src/network/stk_peer.cpp b/src/network/stk_peer.cpp index 1bab905d1..c1602ba8a 100644 --- a/src/network/stk_peer.cpp +++ b/src/network/stk_peer.cpp @@ -142,7 +142,7 @@ bool STKPeer::isBanned() const return true; for (auto p : m_players) { - if (ret->second == p->getOnlineID()) + if (ret->second == p->getOnlineId()) return true; } } From 362b74a77c6493e41fa4fba1eaca873149d7ca27 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 16 Mar 2018 17:38:23 +0800 Subject: [PATCH 237/413] For server-only there can be no player profile --- src/modes/world.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 8ba3046e5..d38050866 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -365,11 +365,11 @@ AbstractKart *World::createKart(const std::string &kart_ident, int index, case RaceManager::KT_PLAYER: { controller = new LocalPlayerController(new_kart, local_player_id); - const float hue = StateManager::get()->getActivePlayer(local_player_id) - ->getConstProfile()->getDefaultKartColor(); - if (hue > 0.0f) + const PlayerProfile* p = StateManager::get() + ->getActivePlayer(local_player_id)->getConstProfile(); + if (p && p->getDefaultKartColor() > 0.0f) { - ri->setHue(hue); + ri->setHue(p->getDefaultKartColor()); } m_num_players ++; From 0fef316313d06470847352d9a17b3b362409759e Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 16 Mar 2018 17:42:21 +0800 Subject: [PATCH 238/413] Use lock and push_back --- src/network/game_setup.hpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/network/game_setup.hpp b/src/network/game_setup.hpp index 7124cfe34..8f6d3dd31 100644 --- a/src/network/game_setup.hpp +++ b/src/network/game_setup.hpp @@ -24,8 +24,6 @@ #include "network/remote_kart_info.hpp" -#include -#include #include #include #include @@ -90,10 +88,11 @@ public: { std::lock_guard lock(m_players_mutex); std::vector > players; - std::transform(m_players.begin(), m_players.end(), - std::back_inserter(players), - std::bind(&std::weak_ptr::lock, - std::placeholders::_1)); + for (auto player_weak : m_players) + { + if (auto player_connected = player_weak.lock()) + players.push_back(player_connected); + } return players; } // getConnectedPlayers // ------------------------------------------------------------------------ From 78ac3a922de4196394305fdccab379a751137c8e Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 16 Mar 2018 19:06:20 +0800 Subject: [PATCH 239/413] Fix compiler warnings --- src/network/protocols/client_lobby.cpp | 8 ++++---- src/network/protocols/lobby_protocol.cpp | 4 ++-- src/network/protocols/server_lobby.cpp | 6 +++--- src/network/servers_manager.cpp | 4 ++-- src/network/stk_host.hpp | 6 +++--- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index 86586958a..cff7953cb 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -198,8 +198,8 @@ void ClientLobby::addAllPlayers(Event* event) std::string track_name; data.decodeString(&track_name); uint8_t lap = data.getUInt8(); - bool reverse = (bool)data.getUInt8(); - m_game_setup->setRace(track_name, lap, reverse); + uint8_t reverse = data.getUInt8(); + m_game_setup->setRace(track_name, lap, reverse == 1); std::shared_ptr peer = event->getPeerSP(); peer->cleanPlayerProfiles(); @@ -336,13 +336,13 @@ void ClientLobby::displayPlayerVote(Event* event) Log::fatal("ClientLobby", "Missing track %s", track_name.c_str()); core::stringw track_readable = track->getName(); int lap = data.getUInt8(); - bool reversed = (bool)data.getUInt8(); + int rev = data.getUInt8(); int t = data.getUInt8(); core::stringw yes = _("Yes"); core::stringw no = _("No"); //I18N: Vote message in network game from a player core::stringw vote_msg = _("Track: %s, laps: %d, reversed: %s, " - "remaining time: %ds", track_readable, lap, reversed ? yes : no, t); + "remaining time: %ds", track_readable, lap, rev == 1 ? yes : no, t); vote_msg = player_name + vote_msg; MessageQueue::add(MessageQueue::MT_NETWORK_MSG, vote_msg); } // displayPlayerVote diff --git a/src/network/protocols/lobby_protocol.cpp b/src/network/protocols/lobby_protocol.cpp index 6088c063e..c60565cc2 100644 --- a/src/network/protocols/lobby_protocol.cpp +++ b/src/network/protocols/lobby_protocol.cpp @@ -87,10 +87,10 @@ void LobbyProtocol::configRemoteKart( const std::vector >& players) const { // The number of karts includes the AI karts, which are not supported atm - race_manager->setNumKarts(players.size()); + race_manager->setNumKarts((int)players.size()); // Set number of global and local players. - race_manager->setNumPlayers(players.size(), + race_manager->setNumPlayers((int)players.size(), m_game_setup->getNumLocalPlayers()); // Create the kart information for the race manager: diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index cc45df9ff..7c0181007 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -1056,9 +1056,9 @@ void ServerLobby::playerVote(Event* event) std::string track_name; data.decodeString(&track_name); uint8_t lap = data.getUInt8(); - bool reverse = (bool)data.getUInt8(); + uint8_t reverse = data.getUInt8(); m_peers_votes[event->getPeerSP()] = - std::make_tuple(track_name,lap, reverse); + std::make_tuple(track_name, lap, reverse == 1); other.addUInt8((uint8_t)remaining_time); sendMessageToPeersChangingToken(&other); @@ -1073,7 +1073,7 @@ std::tuple ServerLobby::handleVote() std::advance(it, rg.get((int)m_available_kts.second.size())); std::string final_track = *it; unsigned final_laps = UserConfigParams::m_num_laps; - bool final_reverse = (bool)(final_track.size() % 2); + bool final_reverse = final_track.size() % 2 == 0; std::map tracks; std::map laps; diff --git a/src/network/servers_manager.cpp b/src/network/servers_manager.cpp index 37a1d4eaa..e07e98c23 100644 --- a/src/network/servers_manager.cpp +++ b/src/network/servers_manager.cpp @@ -150,7 +150,7 @@ Online::XMLRequest* ServersManager::getLANRefreshRequest() const double start_time = StkTime::getRealTime(); const double DURATION = 1.0; const auto& servers = ServersManager::get()->getServers(); - int cur_server_id = servers.size(); + int cur_server_id = (int)servers.size(); assert(cur_server_id == 0); std::vector > servers_now; while (StkTime::getRealTime() - start_time < DURATION) @@ -178,7 +178,7 @@ Online::XMLRequest* ServersManager::getLANRefreshRequest() const uint8_t password = s.getUInt8(); servers_now.emplace_back(std::make_shared (cur_server_id++, name, max_players, players, - difficulty, mode, sender, password)); + difficulty, mode, sender, password == 1)); } // if received_data } // while still waiting m_success = true; diff --git a/src/network/stk_host.hpp b/src/network/stk_host.hpp index e04d4dca2..3da51f11e 100644 --- a/src/network/stk_host.hpp +++ b/src/network/stk_host.hpp @@ -86,8 +86,8 @@ private: /** Let (atm enet_peer_send and enet_peer_disconnect) run in the listening * thread. */ - std::list > m_enet_cmd; /** Protect \ref m_enet_cmd from multiple threads usage. */ @@ -288,7 +288,7 @@ public: unsigned int getPeerCount() const { std::lock_guard lock(m_peers_mutex); - return m_peers.size(); + return (unsigned)m_peers.size(); } // ------------------------------------------------------------------------ /** Sets the global host id of this host (client use). */ From 8c702a376e78c598e9c36ef2e3a9500fe5a2d7a7 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 16 Mar 2018 21:31:46 +0800 Subject: [PATCH 240/413] Move assign mode to exit callback --- src/states_screens/network_kart_selection.cpp | 9 ++++++++- src/states_screens/network_kart_selection.hpp | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/states_screens/network_kart_selection.cpp b/src/states_screens/network_kart_selection.cpp index ed9bb6f68..f26e45e81 100644 --- a/src/states_screens/network_kart_selection.cpp +++ b/src/states_screens/network_kart_selection.cpp @@ -147,7 +147,6 @@ void NetworkKartSelectionScreen::playerConfirm(const int playerID) kart.addUInt8(LobbyProtocol::LE_KART_SELECTION).addUInt8(player_count) .encodeString(selection); STKHost::get()->sendToServer(&kart, true); - input_manager->getDeviceManager()->setAssignMode(ASSIGN); // Remove kart screen StateManager::get()->popMenu(); TracksScreen::getInstance()->setNetworkTracks(); @@ -155,11 +154,19 @@ void NetworkKartSelectionScreen::playerConfirm(const int playerID) } } // playerConfirm +// ---------------------------------------------------------------------------- +void NetworkKartSelectionScreen::tearDown() +{ + KartSelectionScreen::tearDown(); + input_manager->getDeviceManager()->setAssignMode(ASSIGN); +} // tearDown + // ---------------------------------------------------------------------------- bool NetworkKartSelectionScreen::onEscapePressed() { // then remove the lobby screen (you left the server) StateManager::get()->popMenu(); STKHost::get()->shutdown(); + input_manager->getDeviceManager()->setAssignMode(NO_ASSIGN); return true; // remove the screen } // onEscapePressed diff --git a/src/states_screens/network_kart_selection.hpp b/src/states_screens/network_kart_selection.hpp index 4e87cb25c..8c01a63e2 100644 --- a/src/states_screens/network_kart_selection.hpp +++ b/src/states_screens/network_kart_selection.hpp @@ -50,6 +50,9 @@ public: } virtual void init() OVERRIDE; virtual bool onEscapePressed() OVERRIDE; + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void tearDown() OVERRIDE; + }; #endif // NETWORK_KART_SELECTION_HPP From 1a852b7f2264df2909171e9f44b79f6e58850372 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 16 Mar 2018 21:32:12 +0800 Subject: [PATCH 241/413] configRemoteKart needs to include the random kart --- src/network/protocols/server_lobby.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 7c0181007..84bb381da 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -325,14 +325,6 @@ void ServerLobby::asynchronousUpdate() // Remove disconnected player (if any) one last time m_game_setup->update(true); auto players = m_game_setup->getConnectedPlayers(); - // TODO: sort players for grand prix here - configRemoteKart(players); - // Reset for next state usage - for (auto p : m_peers_ready) - { - p.second = false; - } - m_state = LOAD_WORLD; NetworkString* load_world = getNetworkString(); load_world->setSynchronous(true); load_world->addUInt8(LE_LOAD_WORLD).encodeString(std::get<0>(result)) @@ -355,6 +347,15 @@ void ServerLobby::asynchronousUpdate() } load_world->encodeString(player->getKartName()); } + // TODO: sort players for grand prix here + configRemoteKart(players); + + // Reset for next state usage + for (auto p : m_peers_ready) + { + p.second = false; + } + m_state = LOAD_WORLD; sendMessageToPeersChangingToken(load_world); delete load_world; } From bb7c3c0de37e6ee8ccbd412f4c6d019c1c22c0ba Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 16 Mar 2018 21:49:14 +0800 Subject: [PATCH 242/413] Move voting timeout to user config --- src/config/user_config.hpp | 4 +++- src/network/protocols/server_lobby.cpp | 10 ++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/config/user_config.hpp b/src/config/user_config.hpp index fec3f916a..7c2652af8 100644 --- a/src/config/user_config.hpp +++ b/src/config/user_config.hpp @@ -752,7 +752,9 @@ namespace UserConfigParams PARAM_DEFAULT(BoolUserConfigParam(false, "lobby-chat", &m_network_group, "Enable chatting in networking lobby, if off than " "no chat message will be displayed from any players.")); - + PARAM_PREFIX FloatUserConfigParam m_voting_timeout + PARAM_DEFAULT(FloatUserConfigParam(10.0f, "voting-timeout", + &m_network_group, "Timeout in seconds for voting tracks in server.")); // ---- Gamemode setup PARAM_PREFIX UIntToUIntUserConfigParam m_server_ban_list PARAM_DEFAULT(UIntToUIntUserConfigParam("server_ban_list", diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 84bb381da..a110b9e18 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -1027,7 +1027,6 @@ void ServerLobby::kartSelectionRequested(Event* event) */ void ServerLobby::playerVote(Event* event) { - std::lock_guard lock(m_connection_mutex); if (m_state != SELECTING) { Log::warn("ServerLobby", "Received track vote while in state %d.", @@ -1039,13 +1038,16 @@ void ServerLobby::playerVote(Event* event) event->getPeer()->getPlayerProfiles().empty()) return; - // Check if first vote, if so start counter (10 seconds voting time) + // Check if first vote, if so start counter if (m_timeout == std::numeric_limits::max()) - m_timeout = (float)StkTime::getRealTime() + 10.0f; + { + m_timeout = (float)StkTime::getRealTime() + + UserConfigParams::m_voting_timeout; + } float remaining_time = m_timeout - (float)StkTime::getRealTime(); if (remaining_time < 0.0f) { - remaining_time = 0.0f; + return; } NetworkString& data = event->data(); From f9d2d539ff92fae4ae84e9391bce92979aad4b81 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 16 Mar 2018 23:09:21 +0800 Subject: [PATCH 243/413] Server never has local player at the moment --- src/network/network_player_profile.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/network/network_player_profile.cpp b/src/network/network_player_profile.cpp index f7f1c2425..b00045d07 100644 --- a/src/network/network_player_profile.cpp +++ b/src/network/network_player_profile.cpp @@ -17,8 +17,9 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "network/network_player_profile.hpp" - +#include "network/network_config.hpp" #include "network/stk_host.hpp" + // ---------------------------------------------------------------------------- /** Returns true if this player is local, i.e. running on this computer. This * is done by comparing the host id of this player with the host id of this @@ -26,5 +27,7 @@ */ bool NetworkPlayerProfile::isLocalPlayer() const { - return m_host_id == STKHost::get()->getMyHostId(); + // Server never has local player atm + return NetworkConfig::get()->isClient() && + m_host_id == STKHost::get()->getMyHostId(); } // isLocalPlayer From e66c4eed35f52d1477b90f2b1654fe2a9aa496c3 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 17 Mar 2018 11:12:21 +0800 Subject: [PATCH 244/413] Properly hide the rectangle box in ghost track screen --- data/gui/tracks.stkgui | 6 +- src/guiengine/widget.cpp | 3 +- src/states_screens/tracks_screen.cpp | 82 ++++++++++++++++++++-------- src/states_screens/tracks_screen.hpp | 13 ++++- 4 files changed, 76 insertions(+), 28 deletions(-) diff --git a/data/gui/tracks.stkgui b/data/gui/tracks.stkgui index 1b02d6c30..2210b9aba 100644 --- a/data/gui/tracks.stkgui +++ b/data/gui/tracks.stkgui @@ -17,9 +17,9 @@ - +
-