From 6496289707df72bacbb4058ea37113374d9db2bf Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 8 Jun 2018 15:27:21 +1000 Subject: [PATCH 01/28] Added missing virtual destructor. --- src/items/item.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/items/item.hpp b/src/items/item.hpp index b1ea7f98d..326a831c8 100644 --- a/src/items/item.hpp +++ b/src/items/item.hpp @@ -146,6 +146,7 @@ public: } // ItemState(ItemType) // ------------------------------------------------------------------------ + virtual ~ItemState() {} void setDisappearCounter(); void update(int ticks); virtual void collected(const AbstractKart *kart); From 8a534bb7955960da72fe9d00bfb8937c09bec7ed Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 8 Jun 2018 15:25:22 +1000 Subject: [PATCH 02/28] Fixed missing collisions. --- src/physics/btKart.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) mode change 100644 => 100755 src/physics/btKart.cpp diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp old mode 100644 new mode 100755 index 7586cdf4a..95b812e35 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -353,17 +353,17 @@ void btKart::getVisualContactPoint(float visual_rotation, m_visual_wheels_touch_ground = true; short int old_group = 0; + if (m_chassisBody->getBroadphaseHandle()) + { + old_group = m_chassisBody->getBroadphaseHandle() + ->m_collisionFilterGroup; + m_chassisBody->getBroadphaseHandle()->m_collisionFilterGroup = 0; + } for (int index = 2; index <= 3; index++) { // Map index 0-1 to wheel 2-3 (which are the rear wheels) btWheelInfo &wheel = m_wheelInfo[index]; updateWheelTransformsWS(wheel, false); - if (m_chassisBody->getBroadphaseHandle()) - { - old_group = m_chassisBody->getBroadphaseHandle() - ->m_collisionFilterGroup; - m_chassisBody->getBroadphaseHandle()->m_collisionFilterGroup = 0; - } btScalar max_susp_len = wheel.getSuspensionRestLength() + wheel.m_maxSuspensionTravel; From b9bf3fa1252e1dec0a43b9ab7cdf6bf1b6c80409 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 8 Jun 2018 14:24:21 +0800 Subject: [PATCH 03/28] Use map to store peer and ticks in network item manager --- src/items/item_manager.hpp | 5 ++- src/items/network_item_manager.cpp | 51 ++++++++++++++++--------- src/items/network_item_manager.hpp | 11 +++++- src/network/protocols/game_protocol.cpp | 10 ++--- 4 files changed, 48 insertions(+), 29 deletions(-) diff --git a/src/items/item_manager.hpp b/src/items/item_manager.hpp index 98764421a..e5473e466 100644 --- a/src/items/item_manager.hpp +++ b/src/items/item_manager.hpp @@ -30,10 +30,12 @@ #include #include +#include #include #include class Kart; +class STKPeer; /** * \ingroup items @@ -129,7 +131,8 @@ public: bool randomItemsForArena(const AlignedArray& pos); // ------------------------------------------------------------------------ /** Only used in the NetworkItemManager. */ - virtual void setItemConfirmationTime(int host_id, int ticks) + virtual void setItemConfirmationTime(std::weak_ptr peer, + int ticks) { assert(false); } diff --git a/src/items/network_item_manager.cpp b/src/items/network_item_manager.cpp index 0a08b20c8..9815ae84e 100644 --- a/src/items/network_item_manager.cpp +++ b/src/items/network_item_manager.cpp @@ -20,8 +20,11 @@ #include "karts/abstract_kart.hpp" #include "modes/world.hpp" +#include "network/game_setup.hpp" #include "network/network_config.hpp" #include "network/protocols/game_protocol.hpp" +#include "network/stk_host.hpp" +#include "network/stk_peer.hpp" #include "network/rewind_manager.hpp" #include "network/stk_host.hpp" @@ -42,16 +45,19 @@ NetworkItemManager::NetworkItemManager() : Rewinder(/*can be deleted*/false), ItemManager() { - m_last_confirmed_item_ticks.lock(); - m_last_confirmed_item_ticks.getData().clear(); + m_last_confirmed_item_ticks.clear(); if (NetworkConfig::get()->isServer()) { - m_last_confirmed_item_ticks.getData() - .resize(STKHost::get()->getPeerCount(), 0); + auto peers = STKHost::get()->getPeers(); + for (auto& p : peers) + { + if (!p->isValidated()) + continue; + m_last_confirmed_item_ticks[p] = 0; + } } - m_last_confirmed_item_ticks.unlock(); } // NetworkItemManager //----------------------------------------------------------------------------- @@ -141,23 +147,32 @@ Item* NetworkItemManager::dropNewItem(ItemState::ItemType type, /** Called by the GameProtocol when a confirmation for an item event is * received by a host. Once all hosts have confirmed an event, it can be * deleted and won't be send to any clients again. - * \param host_id Host identification of the host confirming the latest - * event time received. + * \param peer Peer confirming the latest event time received. * \param ticks Time at which the last event was received. */ -void NetworkItemManager::setItemConfirmationTime(int host_id, int ticks) +void NetworkItemManager::setItemConfirmationTime(std::weak_ptr peer, + int ticks) { assert(NetworkConfig::get()->isServer()); - m_last_confirmed_item_ticks.lock(); - if (ticks > m_last_confirmed_item_ticks.getData()[host_id]) - m_last_confirmed_item_ticks.getData()[host_id] = ticks; + if (ticks > m_last_confirmed_item_ticks.at(peer)) + m_last_confirmed_item_ticks.at(peer) = ticks; - // Now discard unneeded events, i.e. all events that have - // been confirmed by all clients: - int min_time = 999999; - for (auto i : m_last_confirmed_item_ticks.getData()) - if (i < min_time) min_time = i; - m_last_confirmed_item_ticks.unlock(); + // Now discard unneeded events and expired (disconnected) peer, i.e. all + // events that have been confirmed by all clients: + int min_time = std::numeric_limits::max(); + for (auto it = m_last_confirmed_item_ticks.begin(); + it != m_last_confirmed_item_ticks.end();) + { + if (it->first.expired()) + { + it = m_last_confirmed_item_ticks.erase(it); + } + else + { + if (it->second < min_time) min_time = it->second; + it++; + } + } // Find the last entry before the minimal confirmed time. // Since the event list is sorted, all events up to this @@ -169,7 +184,6 @@ void NetworkItemManager::setItemConfirmationTime(int host_id, int ticks) m_item_events.getData().erase(m_item_events.getData().begin(), p); m_item_events.unlock(); - // TODO: Get informed when a client drops out!!! } // setItemConfirmationTime //----------------------------------------------------------------------------- @@ -290,7 +304,6 @@ void NetworkItemManager::restoreState(BareNetworkString *buffer, int count) if(iei.isItemCollection()) { int index = iei.getIndex(); - ItemState *item_state = m_confirmed_state[index]; // An item on the track was collected: AbstractKart *kart = World::getWorld()->getKart(iei.getKartId()); m_confirmed_state[index]->collected(kart); diff --git a/src/items/network_item_manager.hpp b/src/items/network_item_manager.hpp index f300e1d0c..c49c12de5 100644 --- a/src/items/network_item_manager.hpp +++ b/src/items/network_item_manager.hpp @@ -25,6 +25,11 @@ #include "utils/cpp2011.hpp" #include "utils/synchronised.hpp" +#include +#include + +class STKPeer; + /** \ingroup items * The network item manager is responsible for handling all network related * item manager tasks - synchronisation between clients and servers. It @@ -47,7 +52,8 @@ private: int m_confirmed_state_time; /** Stores on the server the latest confirmed tick from each client. */ - Synchronised< std::vector > m_last_confirmed_item_ticks; + std::map, int32_t, + std::owner_less > > m_last_confirmed_item_ticks; /** List of all items events. */ Synchronised< std::vector > m_item_events; @@ -65,7 +71,8 @@ public: void saveInitialState(); virtual void reset(); - virtual void setItemConfirmationTime(int host_id, int ticks) OVERRIDE; + virtual void setItemConfirmationTime(std::weak_ptr peer, + int ticks) OVERRIDE; virtual void collectedItem(Item *item, AbstractKart *kart) OVERRIDE; virtual Item* dropNewItem(ItemState::ItemType type, const AbstractKart *kart, const Vec3 *xyz=NULL) OVERRIDE; diff --git a/src/network/protocols/game_protocol.cpp b/src/network/protocols/game_protocol.cpp index b33a5380a..ed1ba0b2a 100644 --- a/src/network/protocols/game_protocol.cpp +++ b/src/network/protocols/game_protocol.cpp @@ -101,9 +101,6 @@ bool GameProtocol::notifyEventAsynchronous(Event* event) NetworkString &data = event->data(); uint8_t message_type = data.getUInt8(); - if (message_type != GP_CONTROLLER_ACTION && - message_type != GP_STATE) - printf(""); switch (message_type) { case GP_CONTROLLER_ACTION: handleControllerAction(event); break; @@ -298,10 +295,9 @@ void GameProtocol::sendItemEventConfirmation(int ticks) void GameProtocol::handleItemEventConfirmation(Event *event) { assert(NetworkConfig::get()->isServer()); - int host_id = event->getPeer()->getHostId(); - int ticks = event->data().getTime(); - NetworkItemManager::get()->setItemConfirmationTime(host_id, ticks); - + int ticks = event->data().getTime(); + NetworkItemManager::get()->setItemConfirmationTime(event->getPeerSP(), + ticks); } // handleItemEventConfirmation // ---------------------------------------------------------------------------- From 01dc948247f2b48114745aff5265a453e4cad871 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 8 Jun 2018 14:31:55 +0800 Subject: [PATCH 04/28] Fix #3286 In the future move trigger to check structure --- src/items/item_manager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/items/item_manager.cpp b/src/items/item_manager.cpp index 493c03084..80862f7f5 100644 --- a/src/items/item_manager.cpp +++ b/src/items/item_manager.cpp @@ -543,6 +543,8 @@ void ItemManager::switchItems() ItemState::ItemType new_type = m_switch_to[(*i)->getType()]; + if (new_type == (*i)->getType()) + continue; if(m_switch_ticks<0) (*i)->switchTo(new_type, m_item_mesh[(int)new_type], m_item_lowres_mesh[(int)new_type]); else From 9af27d93c439c92310185f51da0ec72f9329baa3 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 8 Jun 2018 14:56:49 +0800 Subject: [PATCH 05/28] Remove unneeded header --- src/items/network_item_manager.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/items/network_item_manager.cpp b/src/items/network_item_manager.cpp index 9815ae84e..007b37269 100644 --- a/src/items/network_item_manager.cpp +++ b/src/items/network_item_manager.cpp @@ -20,14 +20,11 @@ #include "karts/abstract_kart.hpp" #include "modes/world.hpp" -#include "network/game_setup.hpp" #include "network/network_config.hpp" #include "network/protocols/game_protocol.hpp" -#include "network/stk_host.hpp" -#include "network/stk_peer.hpp" #include "network/rewind_manager.hpp" #include "network/stk_host.hpp" - +#include "network/stk_peer.hpp" //----------------------------------------------------------------------------- /** Creates one instance of the item manager. */ From d9ede4f213341e3f0950f239471049970d8a8836 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 8 Jun 2018 22:59:07 +0800 Subject: [PATCH 06/28] Add state to avoid deadlock when finishing the race --- src/items/network_item_manager.cpp | 3 +- src/items/network_item_manager.hpp | 2 +- src/network/protocols/client_lobby.cpp | 14 ++-- src/network/protocols/server_lobby.cpp | 105 ++++++++++++++----------- src/network/protocols/server_lobby.hpp | 10 ++- src/network/rewind_manager.cpp | 20 +++-- 6 files changed, 86 insertions(+), 68 deletions(-) diff --git a/src/items/network_item_manager.cpp b/src/items/network_item_manager.cpp index 007b37269..ecb11d39a 100644 --- a/src/items/network_item_manager.cpp +++ b/src/items/network_item_manager.cpp @@ -332,7 +332,8 @@ void NetworkItemManager::restoreState(BareNetworkString *buffer, int count) } // while count >0 // Inform the server which events have been received. - GameProtocol::lock()->sendItemEventConfirmation(World::getWorld()->getTimeTicks()); + if (auto gp = GameProtocol::lock()) + gp->sendItemEventConfirmation(World::getWorld()->getTimeTicks()); // Forward the confirmed item state till the world time: int dt = World::getWorld()->getTimeTicks() - current_time; diff --git a/src/items/network_item_manager.hpp b/src/items/network_item_manager.hpp index c49c12de5..16a30d55f 100644 --- a/src/items/network_item_manager.hpp +++ b/src/items/network_item_manager.hpp @@ -70,7 +70,7 @@ public: void sendItemUpdate(); void saveInitialState(); - virtual void reset(); + virtual void reset() OVERRIDE; virtual void setItemConfirmationTime(std::weak_ptr peer, int ticks) OVERRIDE; virtual void collectedItem(Item *item, AbstractKart *kart) OVERRIDE; diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index 5d735caa1..e6e1853f5 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -322,6 +322,11 @@ void ClientLobby::update(int ticks) case PLAYING: break; case RACE_FINISHED: + if (!RaceEventManager::getInstance()->protocolStopped() || + !GameProtocol::emptyInstance()) + return; + if (!m_received_server_result) + m_received_server_result = true; break; case DONE: m_state.store(EXITING); @@ -762,16 +767,9 @@ void ClientLobby::raceFinished(Event* event) // stop race protocols RaceEventManager::getInstance()->stop(); - RaceEventManager::getInstance()->getProtocol()->requestTerminate(); GameProtocol::lock()->requestTerminate(); - - while (!RaceEventManager::getInstance()->protocolStopped()) - StkTime::sleep(1); - while (!GameProtocol::emptyInstance()) - StkTime::sleep(1); - - m_received_server_result = true; + m_state.store(RACE_FINISHED); } // raceFinished //----------------------------------------------------------------------------- diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 40d0d3641..54a12fe85 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -99,6 +99,9 @@ ServerLobby::ServerLobby() : LobbyProtocol(NULL) "STK addons server, don't bother host one if you don't have the " "corresponding permission, they will be rejected if so."); } + m_result_ns = getNetworkString(); + m_result_ns->setSynchronous(true); + m_waiting_for_reset = false; } // ServerLobby //----------------------------------------------------------------------------- @@ -110,6 +113,7 @@ ServerLobby::~ServerLobby() { unregisterServer(true/*now*/); } + delete m_result_ns; } // ~ServerLobby //----------------------------------------------------------------------------- @@ -142,6 +146,8 @@ void ServerLobby::setup() m_peers_votes.clear(); m_server_delay = 0.0; m_timeout.store(std::numeric_limits::max()); + m_waiting_for_reset = false; + Log::info("ServerLobby", "Reset server to initial state."); } // setup @@ -426,6 +432,20 @@ void ServerLobby::asynchronousUpdate() void ServerLobby::update(int ticks) { // Reset server to initial state if no more connected players + if (m_waiting_for_reset) + { + if (!RaceEventManager::getInstance()->protocolStopped() || + !GameProtocol::emptyInstance()) + return; + + RaceResultGUI::getInstance()->backToLobby(); + std::lock_guard lock(m_connection_mutex); + m_game_setup->stopGrandPrix(); + m_state = NetworkConfig::get()->isLAN() ? + ACCEPTING_CLIENTS : REGISTER_SELF_ADDRESS; + setup(); + } + if ((m_state.load() > ACCEPTING_CLIENTS || m_game_setup->isGrandPrixStarted()) && STKHost::get()->getPeerCount() == 0 && @@ -434,13 +454,12 @@ void ServerLobby::update(int ticks) if (RaceEventManager::getInstance() && RaceEventManager::getInstance()->isRunning()) { - stopCurrentRace(); + RaceEventManager::getInstance()->stop(); + RaceEventManager::getInstance()->getProtocol()->requestTerminate(); + GameProtocol::lock()->requestTerminate(); } - std::lock_guard lock(m_connection_mutex); - m_game_setup->stopGrandPrix(); - m_state = NetworkConfig::get()->isLAN() ? - ACCEPTING_CLIENTS : REGISTER_SELF_ADDRESS; - setup(); + m_waiting_for_reset = true; + return; } // Reset for ranked server if in kart / track selection has only 1 player @@ -502,6 +521,22 @@ void ServerLobby::update(int ticks) checkRaceFinished(); } break; + case WAIT_FOR_RACE_STOPPED: + if (!RaceEventManager::getInstance()->protocolStopped() || + !GameProtocol::emptyInstance()) + return; + + // This will go back to lobby in server (and exit the current race) + RaceResultGUI::getInstance()->backToLobby(); + // Reset for next state usage + resetPeersReady(); + // Set the delay before the server forces all clients to exit the race + // result screen and go back to the lobby + m_timeout.store((float)StkTime::getRealTime() + 15.0f); + m_state = RESULT_DISPLAY; + sendMessageToPeers(m_result_ns, /*reliable*/ true); + Log::info("ServerLobby", "End of game message sent"); + break; case RESULT_DISPLAY: if (checkPeersReady() || StkTime::getRealTime() > m_timeout.load()) @@ -811,28 +846,32 @@ void ServerLobby::checkRaceFinished() if (!RaceEventManager::getInstance()->isRaceOver()) return; Log::info("ServerLobby", "The game is considered finish."); + // notify the network world that it is stopped + RaceEventManager::getInstance()->stop(); - // Reset for next state usage - resetPeersReady(); - NetworkString* total = getNetworkString(); - total->setSynchronous(true); - total->addUInt8(LE_RACE_FINISHED); + // stop race protocols before going back to lobby (end race) + RaceEventManager::getInstance()->getProtocol()->requestTerminate(); + GameProtocol::lock()->requestTerminate(); + + // Save race result before delete the world + m_result_ns->clear(); + m_result_ns->addUInt8(LE_RACE_FINISHED); if (m_game_setup->isGrandPrix()) { // fastest lap int fastest_lap = static_cast(World::getWorld())->getFastestLapTicks(); - total->addUInt32(fastest_lap); + m_result_ns->addUInt32(fastest_lap); // all gp tracks - total->addUInt8((uint8_t)m_game_setup->getTotalGrandPrixTracks()) + m_result_ns->addUInt8((uint8_t)m_game_setup->getTotalGrandPrixTracks()) .addUInt8((uint8_t)m_game_setup->getAllTracks().size()); for (const std::string& gp_track : m_game_setup->getAllTracks()) - total->encodeString(gp_track); + m_result_ns->encodeString(gp_track); // each kart score and total time auto& players = m_game_setup->getPlayers(); - total->addUInt8((uint8_t)players.size()); + m_result_ns->addUInt8((uint8_t)players.size()); for (unsigned i = 0; i < players.size(); i++) { int last_score = race_manager->getKartScore(i); @@ -846,7 +885,7 @@ void ServerLobby::checkRaceFinished() player->setScore(cur_score); player->setOverallTime(overall_time); } - total->addUInt32(last_score).addUInt32(cur_score) + m_result_ns->addUInt32(last_score).addUInt32(cur_score) .addFloat(overall_time); } } @@ -854,23 +893,14 @@ void ServerLobby::checkRaceFinished() { int fastest_lap = static_cast(World::getWorld())->getFastestLapTicks(); - total->addUInt32(fastest_lap); + m_result_ns->addUInt32(fastest_lap); } if (NetworkConfig::get()->isRankedServer()) { computeNewRankings(); submitRankingsToAddons(); } - - stopCurrentRace(); - // Set the delay before the server forces all clients to exit the race - // result screen and go back to the lobby - m_timeout.store((float)StkTime::getRealTime() + 15.0f); - m_state = RESULT_DISPLAY; - sendMessageToPeers(total, /*reliable*/ true); - delete total; - Log::info("ServerLobby", "End of game message sent"); - + m_state.store(WAIT_FOR_RACE_STOPPED); } // checkRaceFinished //----------------------------------------------------------------------------- @@ -1060,27 +1090,6 @@ double ServerLobby::distributeBasePoints(uint32_t online_id) return 0.0; } // distributeBasePoints -//----------------------------------------------------------------------------- -/** Stop any race currently in server, should only be called in main thread. - */ -void ServerLobby::stopCurrentRace() -{ - // notify the network world that it is stopped - RaceEventManager::getInstance()->stop(); - - // stop race protocols before going back to lobby (end race) - RaceEventManager::getInstance()->getProtocol()->requestTerminate(); - GameProtocol::lock()->requestTerminate(); - - while (!RaceEventManager::getInstance()->protocolStopped()) - StkTime::sleep(1); - while (!GameProtocol::emptyInstance()) - StkTime::sleep(1); - - // This will go back to lobby in server (and exit the current race) - RaceResultGUI::getInstance()->backToLobby(); -} // stopCurrentRace - //----------------------------------------------------------------------------- /** Called when a client disconnects. * \param event The disconnect event. diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index dbe9c612e..4a5823fd4 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -16,6 +16,7 @@ #include class BareNetworkString; +class NetworkString; class NetworkPlayerProfile; class STKPeer; @@ -34,6 +35,7 @@ public: WAIT_FOR_RACE_STARTED, // Wait for all clients to have started the race DELAY_SERVER, // Additional server delay RACING, // racing + WAIT_FOR_RACE_STOPPED, // Wait server for stopping all race protocols RESULT_DISPLAY, // Show result screen ERROR_LEAVE, // shutting down server EXITING @@ -119,6 +121,10 @@ private: /** Number of ranked races done for each current players */ std::map m_num_ranked_races; + bool m_waiting_for_reset; + + NetworkString* m_result_ns; + // connection management void clientDisconnected(Event* event); void connectionRequested(Event* event); @@ -183,8 +189,6 @@ private: uint32_t online_id, const irr::core::stringw& online_name); std::tuple handleVote(); - void stopCurrentRace(); - void getRankingForPlayer(std::shared_ptr p); void submitRankingsToAddons(); void computeNewRankings(); @@ -193,6 +197,7 @@ private: double distributeBasePoints(uint32_t online_id); double getModeFactor(); double getModeSpread(); + void checkRaceFinished(); public: ServerLobby(); @@ -207,7 +212,6 @@ public: void signalRaceStartToClients(); void startSelection(const Event *event=NULL); void checkIncomingConnectionRequests(); - void checkRaceFinished(); void finishedLoadingWorld() OVERRIDE; ServerState getCurrentState() const { return m_state.load(); } void updateBanList(); diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index feeee4a93..2431b94b4 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -162,17 +162,20 @@ void RewindManager::addNetworkState(BareNetworkString *buffer, int ticks) void RewindManager::saveState(bool local_save) { PROFILER_PUSH_CPU_MARKER("RewindManager - save state", 0x20, 0x7F, 0x20); - GameProtocol::lock()->startNewState(local_save); + auto gp = GameProtocol::lock(); + if (!gp) + return; + gp->startNewState(local_save); AllRewinder::const_iterator rewinder; for (rewinder = m_all_rewinder.begin(); rewinder != m_all_rewinder.end(); ++rewinder) { // TODO: check if it's worth passing in a sufficiently large buffer from // GameProtocol - this would save the copy operation. BareNetworkString *buffer = (*rewinder)->saveState(); - if (buffer && buffer->size() >= 0) + if (buffer) { m_overall_state_size += buffer->size(); - GameProtocol::lock()->addState(buffer); + gp->addState(buffer); } // size >= 0 delete buffer; // buffer can be freed } @@ -186,10 +189,14 @@ void RewindManager::saveState(bool local_save) */ void RewindManager::saveLocalState() { + auto gp = GameProtocol::lock(); + if (!gp) + return; + int ticks = World::getWorld()->getTimeTicks(); saveState(/*local_state*/true); - NetworkString *state = GameProtocol::lock()->getState(); + NetworkString *state = gp->getState(); // Copy the data to a new string, making the buffer in // GameProtocol availble for again. @@ -208,7 +215,6 @@ void RewindManager::saveLocalState() void RewindManager::restoreState(BareNetworkString *data) { data->reset(); - int index = 0; for (auto rewinder = m_all_rewinder.begin(); rewinder != m_all_rewinder.end(); ++rewinder) @@ -230,7 +236,6 @@ void RewindManager::update(int ticks_not_used) m_all_rewinder.size() == 0 || m_is_rewinding) return; - float time = World::getWorld()->getTime(); int ticks = World::getWorld()->getTimeTicks(); m_not_rewound_ticks.store(ticks, std::memory_order_relaxed); @@ -245,7 +250,8 @@ void RewindManager::update(int ticks_not_used) // Save state saveState(/**allow_local_save*/false); PROFILER_PUSH_CPU_MARKER("RewindManager - send state", 0x20, 0x7F, 0x40); - GameProtocol::lock()->sendState(); + if (auto gp = GameProtocol::lock()) + gp->sendState(); PROFILER_POP_CPU_MARKER(); m_last_saved_state = ticks; } // update From 1e85e742192d294b78a739c2308369a6f7737954 Mon Sep 17 00:00:00 2001 From: jpenguin Date: Fri, 8 Jun 2018 13:50:01 -0700 Subject: [PATCH 07/28] Fedora deepedency installation (#3282) * Update README.md Fedoraa dependency instructions * Update README.md * Update README.md changed fedora dependencies to match *buntu ones --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fe132907f..f4bb6423f 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,14 @@ libcurl4-gnutls-dev libfreetype6-dev libfribidi-dev libgl1-mesa-dev \ libjpeg-dev libogg-dev libopenal-dev libpng-dev libvorbis-dev libxrandr-dev \ mesa-common-dev pkg-config zlib1g-dev ``` +Fedora command: + +```bash +sudo dnf install @development-tools cmake bluez-libs-devel \ +openssl-devel libcurl-devel freetype-devel fribidi-devel mesa-libGL-devel \ +libjpeg-turbo-devel ibogg-devel openal-soft-devel libpng-devel libvorbis-devel \ +libXrandr-devel libGLEW pkgconf zlib-devel +``` ### In-game recorder To build the in-game recorder for STK, you have to install @@ -93,7 +101,7 @@ make -j4 ##### Build Speed Optimization -"-j4" is an example, for a faster build, use "-jx" instead, where "x" is the amount of CPU threads you have, minus one. +"-j4" is an example, for a faster build, use "-jx" instead, where "x" is the amount of CPU threads you have, minus one. "-j$(nproc)" usually works. ### Further options From fe322d1209cd5359f2b81278cac2e179150c02ea Mon Sep 17 00:00:00 2001 From: Deve Date: Fri, 8 Jun 2018 22:54:22 +0200 Subject: [PATCH 08/28] Move building instructions to separate file --- INSTALL.md | 253 +++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 252 +--------------------------------------------------- 2 files changed, 254 insertions(+), 251 deletions(-) create mode 100644 INSTALL.md diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 000000000..1510d7f64 --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,253 @@ +## Building from source + +In order to build SuperTuxKart from source, you'll need both the code and the assets (See for more information): + +```bash +git clone https://github.com/supertuxkart/stk-code stk-code +svn co https://svn.code.sf.net/p/supertuxkart/code/stk-assets stk-assets +``` + +## Building SuperTuxKart on Linux + +### Dependencies + +To build SuperTuxKart from source, you'll need to install the following packages: + + * OpenGL (mesa) + * OpenAL (recommended: openal-soft-devel) + * Ogg (libogg-dev) + * Vorbis (libvorbis-dev) + * Freetype (libfreetype6-dev) + * libcurl (libcurl-devel) + * libbluetooth (bluez-devel) + * libpng (libpng-devel) + * zlib (zlib-devel) + * jpeg (libjpeg-turbo-devel) + +Ubuntu command: + +```bash +sudo apt-get install build-essential cmake libbluetooth-dev \ +libcurl4-gnutls-dev libfreetype6-dev libfribidi-dev libgl1-mesa-dev \ +libjpeg-dev libogg-dev libopenal-dev libpng-dev libvorbis-dev libxrandr-dev \ +mesa-common-dev pkg-config zlib1g-dev +``` +Fedora command: + +```bash +sudo dnf install @development-tools cmake bluez-libs-devel \ +openssl-devel libcurl-devel freetype-devel fribidi-devel mesa-libGL-devel \ +libjpeg-turbo-devel ibogg-devel openal-soft-devel libpng-devel libvorbis-devel \ +libXrandr-devel libGLEW pkgconf zlib-devel +``` +### In-game recorder + +To build the in-game recorder for STK, you have to install +libopenglrecorder from your distribution, or compile it yourself from [here](https://github.com/Benau/libopenglrecorder). +Compilation instruction is explained there. If you don't need this feature, pass `-DBUILD_RECORDER=off` to cmake. + +### Compiling + +To compile SuperTuxKart, run the following commands inside `stk-code` directory: + +```bash +mkdir cmake_build +cd cmake_build +cmake .. +make -j4 +``` +STK can then be run from the build directory with `bin/supertuxkart` + +#### Keeping your build up to date + +To recompile the latest code without redownloading the entire source, first run the ```svn up``` command inside the 'stk-assets' directory, then run the following commands inside the 'stk-code' directory: + +```bash +git pull +cd cmake_build +cmake .. +make -j4 +``` + +##### Build Speed Optimization + +"-j4" is an example, for a faster build, use "-jx" instead, where "x" is the amount of CPU threads you have, minus one. "-j$(nproc)" usually works. + +### Further options + +To create a debug version of STK, run: + +```bash +cmake .. -DCMAKE_BUILD_TYPE=Debug +``` + +You can install your build system-wide: + +```bash +sudo make install +``` + +The default install location is `/usr/local`, i.e. the data files will +be written to `/usr/local/share/games/supertuxkart`, the executable +will be copied to `/usr/local/bin`. To change the default installation +location, specify `CMAKE_INSTALL_PREFIX` when running cmake, e.g.: +`cmake .. -DCMAKE_INSTALL_PREFIX=/opt/stk` + + + +## Building SuperTuxKart on Windows +To Build SuperTuxKart on Windows, follow these instructions: + +1. Download and install Visual Studio from here: [Visual Studio - Download](https://www.visualstudio.com/downloads/). The free Visual Studio Community edition works fine. +2. Download the SuperTuxKart source package from either [SuperTuxKart download area - SourceForge.net](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart/) or [SuperTuxKart.net - Source Control](https://supertuxkart.net/Source_control), and unpack it. +*Note: If you downloaded the source package from here: [SuperTuxKart.net - Source Control](https://supertuxkart.net/Source_control), then both `stk-code` and `stk-assets` **must** be in the same directory, otherwise the build can result in failure* +3. Download the Windows dependencies package from either [SuperTuxKart download area: Dependencies - SourceForge.net](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart%20Dependencies/Windows/) +or [SuperTuxKart on GitHub - Dependencies](https://github.com/supertuxkart/dependencies), and unpack it; then, copy the `dependencies` directory from either the `windows` or the `windows_64bit` directories into the `stk-code` directory, rename it to `dependencies-64bit` if you want to compile a 64bit build. +4. Download CMake from here: [CMake - download page](https://cmake.org/download/), install it; once CMake is installed, double click on the CMake icon on your desktop, and point it towards your `stk-code` directory in the 'Where is the source code' field, and point it to a directory called `build` or `bld` inside the stk-code directory. +5. Press 'Configure'; CMake will ask you if it is OK to create the aforementioned directory, press `Yes`. CMake will then ask you about your version of Visual Studio. +Confirm your selection; *Please look at the table below to avoid confusion between version numbers and releases of Visual Studio*; +CMake will begin creating the required files for the build in the directory. +6. Navigate to your build directory and open the `SuperTuxKart.sln` file; Visual Studio will now load the solution. +7. In the 'Solution Explorer', right click on the `supertuxkart` project and select "Set as StartUp project" +8. Open the 'Build' menu and select 'Build Solution'; or, press the default keyboard shortcut: `CTRL + SHIFT + B` to build the solution. + +*Note: To avoid confusion between releases and versions, refer to this table:* + +Visual Studio Release | Version +----------------------|------------ +Visual Studio 2017| 15 +Visual Studio 2015| 14 +Visual Studio 2013| 13 + +## Building SuperTuxKart on Windows (from PowerShell/Command line) +1. Download and install Visual Studio from here: [Visual Studio - Download](https://www.visualstudio.com/downloads/), the free Visual Studio Community edition works fine. + +2. Download a source package from either [SuperTuxKart 0.9.2 download area - SourceForge.net](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart/0.9.2) or [SuperTuxKart.net - Source Control](https://supertuxkart.net/Source_control) +NOTE: the `stk-code` and `stk-assets` directories **must** be in the same directory +3. Download the Windows dependencies package from either [SuperTuxKart download area - SourceForge.net](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart%20Dependencies/Windows/) +or [SuperTuxKart on GitHub - Dependencies](https://github.com/supertuxkart/dependencies) +and unpack the archive; once unpacked, copy the `dependencies` directory from either the `windows` or the `windows_64bit` directories into the `stk-code` directory +4. Download CMake from here: [CMake - download page](https://cmake.org/download/); and install it. Navigate to the `stk-code` directory; and create an directory called "build": +```cmd +mkdir build +cd build +``` +5. Once inside the build directory; run CMake to start the compilation process: +```cmd +cmake .. +``` +6. Now that CMake finished configuring and creating the necessary files for the build, run the build command in the same directory: +```cmd +msbuild.exe SuperTuxKart.sln +``` +SuperTuxKart can now be run as `bin\Debug\supertuxkart.exe` or `bin\Release\supertuxkart.exe` + +## Building SuperTuxKart on macOS + +### Getting Started + +Install the developer tools, either from the OS X Install DVD or from Apple's website. + +If you have never built anything before, you have create `/usr/local/include/` first: + +```bash +sudo mkdir -p /usr/local/include/ +``` + +Symlink the `include`-folder of OpenGL framework to `/usr/local/include/GL` (Unix programs have an easier time finding it this way): + +```bash +sudo ln -s /System/Library/Frameworks/OpenGL.framework/Versions/A/Headers/ /usr/local/include/GL +``` + +On OS X 10.9.5, you might need the following workaround: + +```bash +sudo ln -s `xcrun --show-sdk-path`/usr/include/ /usr/include +sudo ln -s `xcrun --show-sdk-path`/System/Library/Frameworks/OpenGL.framework/Headers/ /usr/local/include/OpenGL +``` +The first link is required in order to find libcurl, the second to find opengl. + +### CMake + +CMake is used to build STK. At this time CMake will not make a binary that is ready for distribution. + +You'll have to run these commands inside your stk-code directory. + + +### STK 0.9.4 or later (or latest git) + +Install homebrew ( https://brew.sh/) +Install all of the dependencies using homebrew : + +```bash +brew install libogg +brew install libvorbis +brew install openal-soft +brew install freetype +brew install curl +brew install openssl@1.1 +brew install fribidi +brew install glew +``` + +Build STK +```bash +mkdir cmake_build +cd cmake_build +CMAKE_PREFIX_PATH=/usr/local/opt/freetype/:/usr/local/opt/curl/:/usr/local/opt/libogg/:/usr/local/opt/libogg/:/usr/local/opt/libvorbis/:/usr/local/opt/openssl\@1.1/:/usr/local/opt/glew/:/usr/local/opt/fribidi/ /usr/local/opt/cmake/bin/cmake .. -DFREETYPE_INCLUDE_DIRS=/usr/local/opt/freetype/include/freetype2/ -DUSE_SYSTEM_GLEW=1 -DOPENAL_INCLUDE_DIR=/usr/local/opt/openal-soft/include/ -DOPENAL_LIBRARY=/usr/local/opt/openal-soft/lib/libopenal.dylib +make +``` + +#### (Optional) packaging for distribution + +By default, the executable that is produced is not ready for distribution. Install https://github.com/auriamg/macdylibbundler + +```bash +dylibbundler -od -b -x ./bin/SuperTuxKart.app/Contents/MacOS/supertuxkart -d ./bin/SuperTuxKart.app/Contents/libs/ -p @executable_path/../libs/ +``` + +then copy the datafiles into /SuperTuxKart.app/Contents/Resources/data + + +### STK 0.9.3 or earlier + +Download pre-built dependencies from [here](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart%20Dependencies/OSX/) and put the frameworks in [hard disk root]/Library/Frameworks + +Building with clang: + +```bash +mkdir cmake_build +cd cmake_build +cmake .. +make +``` + +Building with GCC: +```bash +mkdir cmake_build +cd cmake_build +cmake .. -DCMAKE_CXX_COMPILER=/usr/bin/g++ -DCMAKE_C_COMPILER=/usr/bin/gcc +make +``` + +Building on 10.10 with 10.9 compatibility: +```bash +cmake .. -DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk -DCMAKE_OSX_DEPLOYMENT_TARGET=10.9 +``` + +#### Xcode + +Place an additional copy of the dependencies into `Users//Library/Frameworks`. +Then cd to your cloned stk-code directory and execute the following commands: + +```bash +mkdir xcode_build && cd xcode_build +cmake .. -GXcode +``` + +Use Finder to navigate to your stk-code/xcode_build folder and open the newly generated Xcode project (`SuperTuxKart.xcodeproj`). + +You can then build the project in Xcode using Product -> Build + +Note: Xcode is much less well tested than makefiles, so there may be issues when trying to use Xcode. diff --git a/README.md b/README.md index f4bb6423f..0bd4e7070 100644 --- a/README.md +++ b/README.md @@ -30,254 +30,4 @@ The export utilities perform the needed transformation, so in Blender you just ## Building from source -In order to build SuperTuxKart from source, you'll need both the code and the assets (See for more information): - -```bash -git clone https://github.com/supertuxkart/stk-code stk-code -svn co https://svn.code.sf.net/p/supertuxkart/code/stk-assets stk-assets -``` - -## Building SuperTuxKart on Linux - -### Dependencies - -To build SuperTuxKart from source, you'll need to install the following packages: - - * OpenGL (mesa) - * OpenAL (recommended: openal-soft-devel) - * Ogg (libogg-dev) - * Vorbis (libvorbis-dev) - * Freetype (libfreetype6-dev) - * libcurl (libcurl-devel) - * libbluetooth (bluez-devel) - * libpng (libpng-devel) - * zlib (zlib-devel) - * jpeg (libjpeg-turbo-devel) - -Ubuntu command: - -```bash -sudo apt-get install build-essential cmake libbluetooth-dev \ -libcurl4-gnutls-dev libfreetype6-dev libfribidi-dev libgl1-mesa-dev \ -libjpeg-dev libogg-dev libopenal-dev libpng-dev libvorbis-dev libxrandr-dev \ -mesa-common-dev pkg-config zlib1g-dev -``` -Fedora command: - -```bash -sudo dnf install @development-tools cmake bluez-libs-devel \ -openssl-devel libcurl-devel freetype-devel fribidi-devel mesa-libGL-devel \ -libjpeg-turbo-devel ibogg-devel openal-soft-devel libpng-devel libvorbis-devel \ -libXrandr-devel libGLEW pkgconf zlib-devel -``` -### In-game recorder - -To build the in-game recorder for STK, you have to install -libopenglrecorder from your distribution, or compile it yourself from [here](https://github.com/Benau/libopenglrecorder). -Compilation instruction is explained there. If you don't need this feature, pass `-DBUILD_RECORDER=off` to cmake. - -### Compiling - -To compile SuperTuxKart, run the following commands inside `stk-code` directory: - -```bash -mkdir cmake_build -cd cmake_build -cmake .. -make -j4 -``` -STK can then be run from the build directory with `bin/supertuxkart` - -#### Keeping your build up to date - -To recompile the latest code without redownloading the entire source, first run the ```svn up``` command inside the 'stk-assets' directory, then run the following commands inside the 'stk-code' directory: - -```bash -git pull -cd cmake_build -cmake .. -make -j4 -``` - -##### Build Speed Optimization - -"-j4" is an example, for a faster build, use "-jx" instead, where "x" is the amount of CPU threads you have, minus one. "-j$(nproc)" usually works. - -### Further options - -To create a debug version of STK, run: - -```bash -cmake .. -DCMAKE_BUILD_TYPE=Debug -``` - -You can install your build system-wide: - -```bash -sudo make install -``` - -The default install location is `/usr/local`, i.e. the data files will -be written to `/usr/local/share/games/supertuxkart`, the executable -will be copied to `/usr/local/bin`. To change the default installation -location, specify `CMAKE_INSTALL_PREFIX` when running cmake, e.g.: -`cmake .. -DCMAKE_INSTALL_PREFIX=/opt/stk` - - - -## Building SuperTuxKart on Windows -To Build SuperTuxKart on Windows, follow these instructions: - -1. Download and install Visual Studio from here: [Visual Studio - Download](https://www.visualstudio.com/downloads/). The free Visual Studio Community edition works fine. -2. Download the SuperTuxKart source package from either [SuperTuxKart download area - SourceForge.net](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart/) or [SuperTuxKart.net - Source Control](https://supertuxkart.net/Source_control), and unpack it. -*Note: If you downloaded the source package from here: [SuperTuxKart.net - Source Control](https://supertuxkart.net/Source_control), then both `stk-code` and `stk-assets` **must** be in the same directory, otherwise the build can result in failure* -3. Download the Windows dependencies package from either [SuperTuxKart download area: Dependencies - SourceForge.net](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart%20Dependencies/Windows/) -or [SuperTuxKart on GitHub - Dependencies](https://github.com/supertuxkart/dependencies), and unpack it; then, copy the `dependencies` directory from either the `windows` or the `windows_64bit` directories into the `stk-code` directory, rename it to `dependencies-64bit` if you want to compile a 64bit build. -4. Download CMake from here: [CMake - download page](https://cmake.org/download/), install it; once CMake is installed, double click on the CMake icon on your desktop, and point it towards your `stk-code` directory in the 'Where is the source code' field, and point it to a directory called `build` or `bld` inside the stk-code directory. -5. Press 'Configure'; CMake will ask you if it is OK to create the aforementioned directory, press `Yes`. CMake will then ask you about your version of Visual Studio. -Confirm your selection; *Please look at the table below to avoid confusion between version numbers and releases of Visual Studio*; -CMake will begin creating the required files for the build in the directory. -6. Navigate to your build directory and open the `SuperTuxKart.sln` file; Visual Studio will now load the solution. -7. In the 'Solution Explorer', right click on the `supertuxkart` project and select "Set as StartUp project" -8. Open the 'Build' menu and select 'Build Solution'; or, press the default keyboard shortcut: `CTRL + SHIFT + B` to build the solution. - -*Note: To avoid confusion between releases and versions, refer to this table:* - -Visual Studio Release | Version -----------------------|------------ -Visual Studio 2017| 15 -Visual Studio 2015| 14 -Visual Studio 2013| 13 - -## Building SuperTuxKart on Windows (from PowerShell/Command line) -1. Download and install Visual Studio from here: [Visual Studio - Download](https://www.visualstudio.com/downloads/), the free Visual Studio Community edition works fine. - -2. Download a source package from either [SuperTuxKart 0.9.2 download area - SourceForge.net](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart/0.9.2) or [SuperTuxKart.net - Source Control](https://supertuxkart.net/Source_control) -NOTE: the `stk-code` and `stk-assets` directories **must** be in the same directory -3. Download the Windows dependencies package from either [SuperTuxKart download area - SourceForge.net](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart%20Dependencies/Windows/) -or [SuperTuxKart on GitHub - Dependencies](https://github.com/supertuxkart/dependencies) -and unpack the archive; once unpacked, copy the `dependencies` directory from either the `windows` or the `windows_64bit` directories into the `stk-code` directory -4. Download CMake from here: [CMake - download page](https://cmake.org/download/); and install it. Navigate to the `stk-code` directory; and create an directory called "build": -```cmd -mkdir build -cd build -``` -5. Once inside the build directory; run CMake to start the compilation process: -```cmd -cmake .. -``` -6. Now that CMake finished configuring and creating the necessary files for the build, run the build command in the same directory: -```cmd -msbuild.exe SuperTuxKart.sln -``` -SuperTuxKart can now be run as `bin\Debug\supertuxkart.exe` or `bin\Release\supertuxkart.exe` - -## Building SuperTuxKart on macOS - -### Getting Started - -Install the developer tools, either from the OS X Install DVD or from Apple's website. - -If you have never built anything before, you have create `/usr/local/include/` first: - -```bash -sudo mkdir -p /usr/local/include/ -``` - -Symlink the `include`-folder of OpenGL framework to `/usr/local/include/GL` (Unix programs have an easier time finding it this way): - -```bash -sudo ln -s /System/Library/Frameworks/OpenGL.framework/Versions/A/Headers/ /usr/local/include/GL -``` - -On OS X 10.9.5, you might need the following workaround: - -```bash -sudo ln -s `xcrun --show-sdk-path`/usr/include/ /usr/include -sudo ln -s `xcrun --show-sdk-path`/System/Library/Frameworks/OpenGL.framework/Headers/ /usr/local/include/OpenGL -``` -The first link is required in order to find libcurl, the second to find opengl. - -### CMake - -CMake is used to build STK. At this time CMake will not make a binary that is ready for distribution. - -You'll have to run these commands inside your stk-code directory. - - -### STK 0.9.4 or later (or latest git) - -Install homebrew ( https://brew.sh/) -Install all of the dependencies using homebrew : - -```bash -brew install libogg -brew install libvorbis -brew install openal-soft -brew install freetype -brew install curl -brew install openssl@1.1 -brew install fribidi -brew install glew -``` - -Build STK -```bash -mkdir cmake_build -cd cmake_build -CMAKE_PREFIX_PATH=/usr/local/opt/freetype/:/usr/local/opt/curl/:/usr/local/opt/libogg/:/usr/local/opt/libogg/:/usr/local/opt/libvorbis/:/usr/local/opt/openssl\@1.1/:/usr/local/opt/glew/:/usr/local/opt/fribidi/ /usr/local/opt/cmake/bin/cmake .. -DFREETYPE_INCLUDE_DIRS=/usr/local/opt/freetype/include/freetype2/ -DUSE_SYSTEM_GLEW=1 -DOPENAL_INCLUDE_DIR=/usr/local/opt/openal-soft/include/ -DOPENAL_LIBRARY=/usr/local/opt/openal-soft/lib/libopenal.dylib -make -``` - -#### (Optional) packaging for distribution - -By default, the executable that is produced is not ready for distribution. Install https://github.com/auriamg/macdylibbundler - -```bash -dylibbundler -od -b -x ./bin/SuperTuxKart.app/Contents/MacOS/supertuxkart -d ./bin/SuperTuxKart.app/Contents/libs/ -p @executable_path/../libs/ -``` - -then copy the datafiles into /SuperTuxKart.app/Contents/Resources/data - - -### STK 0.9.3 or earlier - -Download pre-built dependencies from [here](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart%20Dependencies/OSX/) and put the frameworks in [hard disk root]/Library/Frameworks - -Building with clang: - -```bash -mkdir cmake_build -cd cmake_build -cmake .. -make -``` - -Building with GCC: -```bash -mkdir cmake_build -cd cmake_build -cmake .. -DCMAKE_CXX_COMPILER=/usr/bin/g++ -DCMAKE_C_COMPILER=/usr/bin/gcc -make -``` - -Building on 10.10 with 10.9 compatibility: -```bash -cmake .. -DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk -DCMAKE_OSX_DEPLOYMENT_TARGET=10.9 -``` - -#### Xcode - -Place an additional copy of the dependencies into `Users//Library/Frameworks`. -Then cd to your cloned stk-code directory and execute the following commands: - -```bash -mkdir xcode_build && cd xcode_build -cmake .. -GXcode -``` - -Use Finder to navigate to your stk-code/xcode_build folder and open the newly generated Xcode project (`SuperTuxKart.xcodeproj`). - -You can then build the project in Xcode using Product -> Build - -Note: Xcode is much less well tested than makefiles, so there may be issues when trying to use Xcode. +Building instructions can be found in [`INSTALL.md`](/INSTALL.md) From 68f8d524df976707700866563a6b825b31d4f72b Mon Sep 17 00:00:00 2001 From: Deve Date: Fri, 8 Jun 2018 23:08:25 +0200 Subject: [PATCH 09/28] Update ubuntu dependencies. Fixed "ibogg" typo. --- INSTALL.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 1510d7f64..3443096d5 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -28,17 +28,17 @@ Ubuntu command: ```bash sudo apt-get install build-essential cmake libbluetooth-dev \ -libcurl4-gnutls-dev libfreetype6-dev libfribidi-dev libgl1-mesa-dev \ -libjpeg-dev libogg-dev libopenal-dev libpng-dev libvorbis-dev libxrandr-dev \ -mesa-common-dev pkg-config zlib1g-dev +libcurl4-openssl-dev libenet-dev libfreetype6-dev libfribidi-dev \ +libgl1-mesa-dev libglew-dev libjpeg-dev libogg-dev libopenal-dev libpng-dev \ +libssl-dev libvorbis-dev libxrandr-dev libx11-dev pkg-config zlib1g-dev ``` Fedora command: ```bash sudo dnf install @development-tools cmake bluez-libs-devel \ openssl-devel libcurl-devel freetype-devel fribidi-devel mesa-libGL-devel \ -libjpeg-turbo-devel ibogg-devel openal-soft-devel libpng-devel libvorbis-devel \ -libXrandr-devel libGLEW pkgconf zlib-devel +libjpeg-turbo-devel libogg-devel openal-soft-devel libpng-devel \ +libvorbis-devel libXrandr-devel libGLEW pkgconf zlib-devel ``` ### In-game recorder From 05572b00848b09e98ab23f501a8b68bb647f74b4 Mon Sep 17 00:00:00 2001 From: Deve Date: Sat, 9 Jun 2018 00:38:10 +0200 Subject: [PATCH 10/28] Disable addons manager for server-only build. No need to spam with downloading icons for add-ons. --- src/addons/addons_manager.cpp | 3 +++ src/addons/addons_manager.hpp | 5 ++++- src/addons/news_manager.cpp | 4 ++++ src/addons/news_manager.hpp | 4 +++- src/main.cpp | 15 ++++++++++++- src/states_screens/addons_screen.cpp | 9 +++++--- src/states_screens/addons_screen.hpp | 1 - src/states_screens/dialogs/addons_loading.cpp | 21 ++++++++++++++++--- src/states_screens/dialogs/addons_loading.hpp | 2 ++ src/states_screens/dialogs/vote_dialog.cpp | 3 ++- src/states_screens/main_menu_screen.cpp | 8 +++++++ src/states_screens/options_screen_ui.cpp | 3 ++- 12 files changed, 66 insertions(+), 12 deletions(-) diff --git a/src/addons/addons_manager.cpp b/src/addons/addons_manager.cpp index 72ec42e74..b89aa963f 100644 --- a/src/addons/addons_manager.cpp +++ b/src/addons/addons_manager.cpp @@ -18,6 +18,8 @@ \page addons Addons */ +#ifndef SERVER_ONLY + #include "addons/addons_manager.hpp" #include "addons/news_manager.hpp" @@ -614,3 +616,4 @@ void AddonsManager::saveInstalled() xml_installed.close(); } // saveInstalled +#endif diff --git a/src/addons/addons_manager.hpp b/src/addons/addons_manager.hpp index 2aa096e2d..345f2758a 100644 --- a/src/addons/addons_manager.hpp +++ b/src/addons/addons_manager.hpp @@ -19,6 +19,8 @@ #ifndef HEADER_ADDONS_MANAGER_HPP #define HEADER_ADDONS_MANAGER_HPP +#ifndef SERVER_ONLY + #include #include #include @@ -89,5 +91,6 @@ public: }; // class AddonsManager extern AddonsManager *addons_manager; -#endif +#endif +#endif diff --git a/src/addons/news_manager.cpp b/src/addons/news_manager.cpp index 9ca39aab8..78c8a262b 100644 --- a/src/addons/news_manager.cpp +++ b/src/addons/news_manager.cpp @@ -15,6 +15,8 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#ifndef SERVER_ONLY + #include "addons/news_manager.hpp" #include "config/user_config.hpp" @@ -490,3 +492,5 @@ bool NewsManager::conditionFulfilled(const std::string &cond) } // conditionFulfilled // ---------------------------------------------------------------------------- + +#endif diff --git a/src/addons/news_manager.hpp b/src/addons/news_manager.hpp index e249a83a8..3819cd8df 100644 --- a/src/addons/news_manager.hpp +++ b/src/addons/news_manager.hpp @@ -18,6 +18,8 @@ #ifndef HEADER_NEWS_MANAGER_HPP #define HEADER_NEWS_MANAGER_HPP +#ifndef SERVER_ONLY + #include #include @@ -143,4 +145,4 @@ public: extern NewsManager *news_manager; #endif - +#endif diff --git a/src/main.cpp b/src/main.cpp index 863b66ed2..1a26da448 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1574,7 +1574,9 @@ void initRest() // This only initialises the non-network part of the add-ons manager. The // online section of the add-ons manager will be initialised from a // separate thread running in network HTTP. +#ifndef SERVER_ONLY addons_manager = new AddonsManager(); +#endif Online::ProfileManager::create(); // The request manager will start the login process in case of a saved @@ -1583,7 +1585,9 @@ void initRest() // achievement managers to be created, which can only be created later). PlayerManager::create(); Online::RequestManager::get()->startNetworkThread(); +#ifndef SERVER_ONLY NewsManager::get(); // this will create the news manager +#endif music_manager = new MusicManager(); SFXManager::create(); @@ -1664,8 +1668,10 @@ void askForInternetPermission() Online::RequestManager::IPERM_ALLOWED; UserConfigParams::m_internet_status = Online::RequestManager::IPERM_ALLOWED; +#ifndef SERVER_ONLY if (need_to_start_news_manager) NewsManager::get()->init(false); +#endif GUIEngine::ModalDialog::dismiss(); } // onConfirm // -------------------------------------------------------- @@ -1806,6 +1812,7 @@ int main(int argc, char *argv[] ) //handleCmdLine() needs InitTuxkart() so it can't be called first if(!handleCmdLine()) exit(0); +#ifndef SERVER_ONLY addons_manager->checkInstalledAddons(); // Load addons.xml to get info about add-ons even when not @@ -1827,6 +1834,7 @@ int main(int argc, char *argv[] ) } } } +#endif if(UserConfigParams::m_unit_testing) { @@ -1959,6 +1967,7 @@ int main(int argc, char *argv[] ) StateManager::get()->enterGameState(); } +#ifndef SERVER_ONLY // If an important news message exists it is shown in a popup dialog. const core::stringw important_message = NewsManager::get()->getImportantMessage(); @@ -1968,7 +1977,7 @@ int main(int argc, char *argv[] ) MessageDialog::MESSAGE_DIALOG_OK, NULL, true); } // if important_message - +#endif // Replay a race // ============= @@ -2119,11 +2128,13 @@ static void cleanSuperTuxKart() // was deleted (in cleanUserConfig below), but before STK finishes and // the OS takes all threads down. +#ifndef SERVER_ONLY if(!NewsManager::get()->waitForReadyToDeleted(2.0f)) { Log::info("Thread", "News manager not stopping, exiting anyway."); } NewsManager::deallocate(); +#endif if(!Online::RequestManager::get()->waitForReadyToDeleted(5.0f)) { @@ -2144,7 +2155,9 @@ static void cleanSuperTuxKart() // The add-ons manager might still be called from a currenty running request // in the request manager, so it can not be deleted earlier. +#ifndef SERVER_ONLY if(addons_manager) delete addons_manager; +#endif ServersManager::deallocate(); cleanUserConfig(); diff --git a/src/states_screens/addons_screen.cpp b/src/states_screens/addons_screen.cpp index 4ba06209a..f50f9ec4d 100644 --- a/src/states_screens/addons_screen.cpp +++ b/src/states_screens/addons_screen.cpp @@ -234,7 +234,7 @@ void AddonsScreen::tearDown() */ void AddonsScreen::loadList() { - +#ifndef SERVER_ONLY // Get the filter by words. GUIEngine::TextBoxWidget* w_filter_name = getWidget("filter_name"); @@ -416,6 +416,7 @@ void AddonsScreen::loadList() else getWidget("category")->select("tab_update", PLAYER_ID_GAME_MASTER); +#endif } // loadList // ---------------------------------------------------------------------------- @@ -452,6 +453,7 @@ void AddonsScreen::onColumnClicked(int column_id) void AddonsScreen::eventCallback(GUIEngine::Widget* widget, const std::string& name, const int playerID) { +#ifndef SERVER_ONLY if (name == "back") { StateManager::get()->escapePressed(); @@ -509,7 +511,7 @@ void AddonsScreen::eventCallback(GUIEngine::Widget* widget, { loadList(); } - +#endif } // eventCallback // ---------------------------------------------------------------------------- @@ -534,6 +536,7 @@ void AddonsScreen::setLastSelected() void AddonsScreen::onUpdate(float dt) { +#ifndef SERVER_ONLY if (m_reloading) { if(UserConfigParams::m_internet_status!=RequestManager::IPERM_ALLOWED) @@ -579,5 +582,5 @@ void AddonsScreen::onUpdate(float dt) m_show_tips = false; } } - +#endif } // onUpdate diff --git a/src/states_screens/addons_screen.hpp b/src/states_screens/addons_screen.hpp index 3b12e3254..339ec49c6 100644 --- a/src/states_screens/addons_screen.hpp +++ b/src/states_screens/addons_screen.hpp @@ -46,7 +46,6 @@ class AddonsScreen : public GUIEngine::Screen, friend class GUIEngine::ScreenSingleton; private: AddonsScreen(); - AddonsManager *m_addons; AddonsLoading *m_load; void loadInformations(); /** Icon for installed addon, which can be updated. */ diff --git a/src/states_screens/dialogs/addons_loading.cpp b/src/states_screens/dialogs/addons_loading.cpp index 2dbe92fca..663194b99 100644 --- a/src/states_screens/dialogs/addons_loading.cpp +++ b/src/states_screens/dialogs/addons_loading.cpp @@ -45,8 +45,10 @@ using namespace irr::gui; */ AddonsLoading::AddonsLoading(const std::string &id) - : ModalDialog(0.8f, 0.8f), - m_addon(*(addons_manager->getAddon(id)) ) + : ModalDialog(0.8f, 0.8f) +#ifndef SERVER_ONLY + , m_addon(*(addons_manager->getAddon(id)) ) +#endif { m_icon_shown = false; @@ -82,6 +84,7 @@ AddonsLoading::~AddonsLoading() void AddonsLoading::beforeAddingWidgets() { +#ifndef SERVER_ONLY /* Init the icon here to be able to load a single image*/ m_icon = getWidget ("icon" ); m_progress = getWidget("progress"); @@ -180,6 +183,7 @@ void AddonsLoading::beforeAddingWidgets() unit = _LTR("%s KB", 1); core::stringw size = _("Size: %s", unit.c_str()); getWidget("size")->setText(size, false); +#endif } // AddonsLoading // ---------------------------------------------------------------------------- @@ -205,6 +209,7 @@ bool AddonsLoading::onEscapePressed() GUIEngine::EventPropagation AddonsLoading::processEvent(const std::string& event_source) { +#ifndef SERVER_ONLY GUIEngine::RibbonWidget* actions_ribbon = getWidget("actions"); @@ -252,12 +257,14 @@ GUIEngine::EventPropagation AddonsLoading::processEvent(const std::string& event voteClicked(); return GUIEngine::EVENT_BLOCK; } +#endif return GUIEngine::EVENT_LET; } // processEvent // ---------------------------------------------------------------------------- void AddonsLoading::voteClicked() { +#ifndef SERVER_ONLY if (PlayerManager::isCurrentLoggedIn()) { // We need to keep a copy of the addon id, since dismiss() will @@ -266,11 +273,13 @@ void AddonsLoading::voteClicked() dismiss(); new VoteDialog(addon_id); } +#endif } // voteClicked // ---------------------------------------------------------------------------- void AddonsLoading::onUpdate(float delta) { +#ifndef SERVER_ONLY if(m_progress->isVisible()) { float progress = m_download_request->getProgress(); @@ -308,6 +317,7 @@ void AddonsLoading::onUpdate(float delta) } m_icon_shown = true; } +#endif } // onUpdate // ---------------------------------------------------------------------------- @@ -316,13 +326,14 @@ void AddonsLoading::onUpdate(float delta) **/ void AddonsLoading::startDownload() { +#ifndef SERVER_ONLY std::string save = "tmp/" + StringUtils::getBasename(m_addon.getZipFileName()); m_download_request = new Online::HTTPRequest(save, /*manage mem*/false, /*priority*/5); m_download_request->setURL(m_addon.getZipFileName()); m_download_request->queue(); - +#endif } // startDownload // ---------------------------------------------------------------------------- @@ -353,6 +364,7 @@ void AddonsLoading::stopDownload() */ void AddonsLoading::doInstall() { +#ifndef SERVER_ONLY delete m_download_request; m_download_request = NULL; @@ -383,12 +395,14 @@ void AddonsLoading::doInstall() } track_manager->loadTrackList(); +#endif } // doInstall // ---------------------------------------------------------------------------- void AddonsLoading::doUninstall() { +#ifndef SERVER_ONLY delete m_download_request; m_download_request = NULL; bool error = !addons_manager->uninstall(m_addon); @@ -418,4 +432,5 @@ void AddonsLoading::doUninstall() AddonsScreen::getInstance()->loadList(); dismiss(); } +#endif } // doUninstall diff --git a/src/states_screens/dialogs/addons_loading.hpp b/src/states_screens/dialogs/addons_loading.hpp index cdc91c6e8..1c217641a 100644 --- a/src/states_screens/dialogs/addons_loading.hpp +++ b/src/states_screens/dialogs/addons_loading.hpp @@ -41,7 +41,9 @@ private: GUIEngine::IconButtonWidget *m_icon; /** The addon to load. */ +#ifndef SERVER_ONLY Addon m_addon; +#endif void startDownload(); void stopDownload(); void doInstall(); diff --git a/src/states_screens/dialogs/vote_dialog.cpp b/src/states_screens/dialogs/vote_dialog.cpp index 923f65120..697ea6c16 100644 --- a/src/states_screens/dialogs/vote_dialog.cpp +++ b/src/states_screens/dialogs/vote_dialog.cpp @@ -93,6 +93,7 @@ bool VoteDialog::onEscapePressed() */ void VoteDialog::sendVote() { +#ifndef SERVER_ONLY /** A vote request. The callback will update the addon manager with the * new average. The VoteDialog polls this request till it is finished * to inform the user about the new average. @@ -125,7 +126,7 @@ void VoteDialog::sendVote() m_rating_widget->setActive(false); m_cancel_widget->setActive(false); - +#endif } // sendVote // ----------------------------------------------------------------------------- diff --git a/src/states_screens/main_menu_screen.cpp b/src/states_screens/main_menu_screen.cpp index 0f41e82b2..887d6420a 100644 --- a/src/states_screens/main_menu_screen.cpp +++ b/src/states_screens/main_menu_screen.cpp @@ -140,6 +140,7 @@ void MainMenuScreen::init() // the key bindings for the first player the default again. input_manager->getDeviceManager()->clearLatestUsedDevice(); +#ifndef SERVER_ONLY if (addons_manager->isLoading()) { IconButtonWidget* w = getWidget("addons"); @@ -152,6 +153,7 @@ void MainMenuScreen::init() const core::stringw &news_text = NewsManager::get()->getNextNewsMessage(); w->setText(news_text, true); w->update(0.01f); +#endif RibbonWidget* r = getWidget("menu_bottomrow"); // FIXME: why do I need to do this manually @@ -174,6 +176,7 @@ void MainMenuScreen::init() void MainMenuScreen::onUpdate(float delta) { +#ifndef SERVER_ONLY PlayerProfile *player = PlayerManager::getCurrentPlayer(); if(PlayerManager::getCurrentOnlineState() == PlayerProfile::OS_GUEST || PlayerManager::getCurrentOnlineState() == PlayerProfile::OS_SIGNED_IN) @@ -222,6 +225,7 @@ void MainMenuScreen::onUpdate(float delta) const core::stringw &news_text = NewsManager::get()->getNextNewsMessage(); w->setText(news_text, true); } +#endif } // onUpdate // ---------------------------------------------------------------------------- @@ -229,6 +233,7 @@ void MainMenuScreen::onUpdate(float delta) void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, const int playerID) { +#ifndef SERVER_ONLY if(name=="user-id") { UserScreen::getInstance()->push(); @@ -534,6 +539,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, { OnlineProfileAchievements::getInstance()->push(); } +#endif } // eventCallback // ---------------------------------------------------------------------------- @@ -546,6 +552,7 @@ void MainMenuScreen::tearDown() void MainMenuScreen::onDisabledItemClicked(const std::string& item) { +#ifndef SERVER_ONLY if (item == "addons") { if (UserConfigParams::m_internet_status != RequestManager::IPERM_ALLOWED) @@ -565,4 +572,5 @@ void MainMenuScreen::onDisabledItemClicked(const std::string& item) new MessageDialog( _("Please wait while the add-ons are loading")); } } +#endif } // onDisabledItemClicked diff --git a/src/states_screens/options_screen_ui.cpp b/src/states_screens/options_screen_ui.cpp index 3179d64c1..a0635763d 100644 --- a/src/states_screens/options_screen_ui.cpp +++ b/src/states_screens/options_screen_ui.cpp @@ -239,6 +239,7 @@ void OptionsScreenUI::init() void OptionsScreenUI::eventCallback(Widget* widget, const std::string& name, const int playerID) { +#ifndef SERVER_ONLY if (name == "options_choice") { std::string selection = ((RibbonWidget*)widget)->getSelectionIDString(PLAYER_ID_GAME_MASTER); @@ -387,7 +388,7 @@ void OptionsScreenUI::eventCallback(Widget* widget, const std::string& name, con OptionsScreenUI::getInstance()->push(); } - +#endif } // eventCallback // ----------------------------------------------------------------------------- From 7c14fd28ba8f901b9846f90a101dcf2d27be1385 Mon Sep 17 00:00:00 2001 From: Alayan-stk-2 Date: Sat, 9 Jun 2018 10:43:19 +0200 Subject: [PATCH 11/28] Ranking formula refinements (#3288) * Reduce the maximum scaling time from 600s to 500s 8m20s is already much longer than nearly all ranked races will be, so it matters most for eliminated players. It would be too punishing if kept to 600 with the new time scaling method. * New helper function for ranking computations * Make short races less important for ranking points And long races more important --- src/network/protocols/server_lobby.cpp | 20 +++++++++++++++----- src/network/protocols/server_lobby.hpp | 3 ++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 54a12fe85..1699d82e4 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -970,13 +970,13 @@ void ServerLobby::computeNewRankings() { result = 0.0; ranking_importance = mode_factor * - MAX_SCALING_TIME * MAX_POINTS_PER_SECOND * player_factors; + scalingValueForTime(MAX_SCALING_TIME) * player_factors; } else if (!players[j]) { result = 1.0; ranking_importance = mode_factor * - MAX_SCALING_TIME * MAX_POINTS_PER_SECOND * player_factors; + scalingValueForTime(MAX_SCALING_TIME) * player_factors; } else { @@ -994,10 +994,11 @@ void ServerLobby::computeNewRankings() (player1_time - player2_time) / (player2_time / 20.0); result = std::max(0.0, 0.5 - result); } + + float max_time = std::min(std::max(player1_time, player2_time), + MAX_SCALING_TIME); ranking_importance = mode_factor * - std::min( - std::max(player1_time, player2_time), MAX_SCALING_TIME) * - MAX_POINTS_PER_SECOND * player_factors; + scalingValueForTime(max_time) * player_factors; } // Compute the ranking change scores_change[i] += @@ -1070,6 +1071,15 @@ double ServerLobby::getModeSpread() return 1.4; } // getModeSpread +//----------------------------------------------------------------------------- +/** Compute the scaling value of a given time + * Short races are more random, so we don't use strict proportionality + */ +double ServerLobby::scalingValueForTime(float time) +{ + return time * sqrt(time/120.0) * MAX_POINTS_PER_SECOND; +} // scalingValueForTime + //----------------------------------------------------------------------------- /** Manages the distribution of the base points. * Gives half of the points progressively diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 4a5823fd4..c4e39b21c 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -106,7 +106,7 @@ private: /* Ranking related variables */ // If updating the base points, update the base points distribution in DB const double BASE_RANKING_POINTS = 4000.0; - const double MAX_SCALING_TIME = 600.0; + const double MAX_SCALING_TIME = 500.0; const double MAX_POINTS_PER_SECOND = 0.125; /** Online id to profile map, handling disconnection in ranked server */ @@ -197,6 +197,7 @@ private: double distributeBasePoints(uint32_t online_id); double getModeFactor(); double getModeSpread(); + double scalingValueForTime(float time); void checkRaceFinished(); public: From 9133275677ffc7db7dff4e327ef2fc28f697ecc3 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 9 Jun 2018 18:53:44 +0800 Subject: [PATCH 12/28] Use double for calculation --- src/network/protocols/server_lobby.cpp | 10 +++++----- src/network/protocols/server_lobby.hpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 1699d82e4..3a1c06740 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -995,10 +995,10 @@ void ServerLobby::computeNewRankings() result = std::max(0.0, 0.5 - result); } - float max_time = std::min(std::max(player1_time, player2_time), - MAX_SCALING_TIME); + double max_time = std::min(std::max(player1_time, player2_time), + MAX_SCALING_TIME); ranking_importance = mode_factor * - scalingValueForTime(max_time) * player_factors; + scalingValueForTime(max_time) * player_factors; } // Compute the ranking change scores_change[i] += @@ -1075,9 +1075,9 @@ double ServerLobby::getModeSpread() /** Compute the scaling value of a given time * Short races are more random, so we don't use strict proportionality */ -double ServerLobby::scalingValueForTime(float time) +double ServerLobby::scalingValueForTime(double time) { - return time * sqrt(time/120.0) * MAX_POINTS_PER_SECOND; + return time * sqrt(time / 120.0) * MAX_POINTS_PER_SECOND; } // scalingValueForTime //----------------------------------------------------------------------------- diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index c4e39b21c..df4de3457 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -197,7 +197,7 @@ private: double distributeBasePoints(uint32_t online_id); double getModeFactor(); double getModeSpread(); - double scalingValueForTime(float time); + double scalingValueForTime(double time); void checkRaceFinished(); public: From 6433e1eae06fc02d431615b16aa6fcade727b192 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 9 Jun 2018 18:57:26 +0800 Subject: [PATCH 13/28] Fix as alayan suggested --- src/network/protocols/server_lobby.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 3a1c06740..4dfaf8492 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -1092,9 +1092,7 @@ double ServerLobby::distributeBasePoints(uint32_t online_id) unsigned num_races = m_num_ranked_races.at(online_id); if (num_races < 45) { - return - (BASE_RANKING_POINTS / 2000.0 * std::max((45u - num_races), 4u) * - 2.0); + return BASE_RANKING_POINTS / 2000.0 * std::max((45u - num_races), 4u); } else return 0.0; From f329314bf614a9cee070f1761ddd5363ad002b07 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 9 Jun 2018 19:04:15 +0800 Subject: [PATCH 14/28] Fix possible empty race event manager instance --- src/network/protocols/server_lobby.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 4dfaf8492..4282fec81 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -434,7 +434,8 @@ void ServerLobby::update(int ticks) // Reset server to initial state if no more connected players if (m_waiting_for_reset) { - if (!RaceEventManager::getInstance()->protocolStopped() || + if ((RaceEventManager::getInstance() && + !RaceEventManager::getInstance()->protocolStopped()) || !GameProtocol::emptyInstance()) return; From e823babb909cb7972c86674afd99441b61892192 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 9 Jun 2018 20:16:56 +0800 Subject: [PATCH 15/28] Show remote player in the past --- src/karts/kart.cpp | 15 +++++++++++++++ src/karts/kart.hpp | 6 ++++++ src/karts/kart_rewinder.cpp | 30 +++++++++++++++++++++++++++--- src/karts/moveable.cpp | 2 +- src/modes/world.cpp | 10 ++++++++++ src/network/rewind_queue.cpp | 4 ++-- 6 files changed, 61 insertions(+), 6 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index feaf322bd..b919884a7 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1687,6 +1687,21 @@ void Kart::update(int ticks) } // update +//----------------------------------------------------------------------------- +void Kart::handleRewoundTransform() +{ + if (!m_controller->isLocalPlayerController()) + { + if (RewindManager::get()->isRewinding()) + m_rewound_transforms.push_back(getTrans()); + else if (!m_rewound_transforms.empty()) + { + setTrans(m_rewound_transforms.front()); + m_rewound_transforms.pop_front(); + } + } +} // handleRewoundTransform + //----------------------------------------------------------------------------- /** Updates the local speed based on the current physical velocity. The value * is smoothed exponentially to avoid camera stuttering (camera distance diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 7a3fb0983..5133d3305 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -32,6 +32,7 @@ #include "karts/abstract_kart.hpp" #include "utils/no_copy.hpp" +#include #include class AbstractKartAnimation; @@ -250,6 +251,8 @@ protected: int m_ticks_last_crash; RaceManager::KartType m_type; + std::deque m_rewound_transforms; + /** To prevent using nitro in too short bursts */ int m_min_nitro_ticks; @@ -547,6 +550,9 @@ public: virtual void playSound(SFXBuffer* buffer) OVERRIDE; // ------------------------------------------------------------------------ virtual bool isVisible() OVERRIDE; + // ------------------------------------------------------------------------ + void handleRewoundTransform(); + }; // Kart diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index a1c52b235..275b64fb2 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -60,15 +60,39 @@ void KartRewinder::reset() void KartRewinder::saveTransform() { m_saved_transform = getTrans(); + m_rewound_transforms.clear(); } // saveTransform // ---------------------------------------------------------------------------- void KartRewinder::computeError() { + // Local player kart doesn't need showing in the past + if (m_rewound_transforms.empty()) + return; + + std::deque copied = m_rewound_transforms; + // Find the closest position that matches previous rewound one + Vec3 saved_position = m_saved_transform.getOrigin(); + while (!copied.empty()) + { + Vec3 cur_position = copied.front().getOrigin(); + if ((cur_position - saved_position).length() < 0.25f) + { + setTrans(copied.front()); + copied.pop_front(); + std::swap(m_rewound_transforms, copied); + return; + } + copied.pop_front(); + } + + // Use newly rewound one if no matching transformation + setTrans(m_rewound_transforms.front()); + m_rewound_transforms.pop_front(); //btTransform error = getTrans() - m_saved_transform; - Vec3 pos_error = getTrans().getOrigin() - m_saved_transform.getOrigin(); - btQuaternion rot_error(0, 0, 0, 1); - Kart::addError(pos_error, rot_error); + //Vec3 pos_error = getTrans().getOrigin() - m_saved_transform.getOrigin(); + //btQuaternion rot_error(0, 0, 0, 1); + //Kart::addError(pos_error, rot_error); } // computeError // ---------------------------------------------------------------------------- diff --git a/src/karts/moveable.cpp b/src/karts/moveable.cpp index 0ca87bd8a..0f195933c 100644 --- a/src/karts/moveable.cpp +++ b/src/karts/moveable.cpp @@ -106,7 +106,7 @@ void Moveable::updateGraphics(float dt, const Vec3& offset_xyz, #endif } #ifndef SERVER_ONLY - Vec3 xyz=getXYZ()+offset_xyz - m_positional_error; + Vec3 xyz=getXYZ()+offset_xyz; m_node->setPosition(xyz.toIrrVector()); btQuaternion r_all = getRotation()*rotation; if(btFuzzyZero(r_all.getX()) && btFuzzyZero(r_all.getY()-0.70710677f) && diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 64785700a..94979b67d 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -1056,6 +1056,16 @@ void World::update(int ticks) Physics::getInstance()->update(ticks); + if (NetworkConfig::get()->isNetworking() && + NetworkConfig::get()->isClient()) + { + for (int i = 0 ; i < kart_amount; i++) + { + if (!m_karts[i]->isEliminated()) + static_cast(m_karts[i])->handleRewoundTransform(); + } + } + PROFILER_PUSH_CPU_MARKER("World::update (projectiles)", 0xa0, 0x7F, 0x00); projectile_manager->update(ticks); PROFILER_POP_CPU_MARKER(); diff --git a/src/network/rewind_queue.cpp b/src/network/rewind_queue.cpp index 81aa39ee8..ea13f1ce7 100644 --- a/src/network/rewind_queue.cpp +++ b/src/network/rewind_queue.cpp @@ -210,7 +210,6 @@ void RewindQueue::mergeNetworkData(int world_ticks, bool *needs_rewind, // Only a client ever rewinds. So the rewind time should be the latest // received state before current world time (if any) *rewind_ticks = -9999; - bool adjust_next = false; // FIXME: making m_network_events sorted would prevent the need to // go through the whole list of events @@ -261,7 +260,8 @@ void RewindQueue::mergeNetworkData(int world_ticks, bool *needs_rewind, // any server message should be in the client's past - but it can // happen during debugging) we need to rewind to getTicks (in order // to get the latest state). - if (NetworkConfig::get()->isClient() && (*i)->getTicks() <= world_ticks) + if (NetworkConfig::get()->isClient() && (*i)->getTicks() <= world_ticks + && (*i)->isState()) { // We need rewind if we receive an event in the past. This will // then trigger a rewind later. Note that we only rewind to the From 90f3a2389e01f7633e1300f531ac75d08018029d Mon Sep 17 00:00:00 2001 From: Deve Date: Sun, 10 Jun 2018 23:25:51 +0200 Subject: [PATCH 16/28] Allow to set commandline arguments on android --- src/config/user_config.hpp | 4 ++++ src/main.cpp | 2 ++ src/utils/command_line.cpp | 18 +++++++++++++++++- src/utils/command_line.hpp | 1 + 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/config/user_config.hpp b/src/config/user_config.hpp index f5c08f0de..040509811 100644 --- a/src/config/user_config.hpp +++ b/src/config/user_config.hpp @@ -994,6 +994,10 @@ namespace UserConfigParams PARAM_PREFIX BoolUserConfigParam m_everything_unlocked PARAM_DEFAULT( BoolUserConfigParam(false, "everything_unlocked", "Enable all karts and tracks") ); + + PARAM_PREFIX StringUserConfigParam m_commandline + PARAM_DEFAULT( StringUserConfigParam("", "commandline", + "Allows to set commandline args in config file") ); // TODO? implement blacklist for new irrlicht device and GUI PARAM_PREFIX std::vector m_blacklist_res; diff --git a/src/main.cpp b/src/main.cpp index 1a26da448..048439e28 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1737,6 +1737,8 @@ int main(int argc, char *argv[] ) // handle all command line options that do not need (or must // not have) other managers initialised: initUserConfig(); + + CommandLine::addArgsFromUserConfig(); handleCmdLinePreliminary(); diff --git a/src/utils/command_line.cpp b/src/utils/command_line.cpp index c68332326..df62a93ec 100644 --- a/src/utils/command_line.cpp +++ b/src/utils/command_line.cpp @@ -32,12 +32,28 @@ std::string CommandLine::m_exec_name=""; void CommandLine::init(unsigned int argc, char *argv[]) { if (argc > 0) + { m_exec_name = argv[0]; + } - for(unsigned int i=1; i config_args; + config_args = StringUtils::split(UserConfigParams::m_commandline, ' '); + + for (std::string config_arg : config_args) + { + m_argv.push_back(config_arg); + } +} + // ---------------------------------------------------------------------------- bool CommandLine::has(const std::string &option) { diff --git a/src/utils/command_line.hpp b/src/utils/command_line.hpp index 84abc5de8..aae6d2400 100644 --- a/src/utils/command_line.hpp +++ b/src/utils/command_line.hpp @@ -79,6 +79,7 @@ private: public: static void init(unsigned int argc, char *argv[]); + static void addArgsFromUserConfig(); static void reportInvalidParameters(); static bool has(const std::string &option); // ------------------------------------------------------------------------ From fc89a1c24443b0243aa6bb975bfbf34513e67e4b Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Sun, 10 Jun 2018 18:43:46 -0400 Subject: [PATCH 17/28] Fix #3242 --- src/guiengine/widgets/icon_button_widget.cpp | 8 ++++++++ src/states_screens/register_screen.cpp | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/src/guiengine/widgets/icon_button_widget.cpp b/src/guiengine/widgets/icon_button_widget.cpp index de608928a..7503ec42f 100644 --- a/src/guiengine/widgets/icon_button_widget.cpp +++ b/src/guiengine/widgets/icon_button_widget.cpp @@ -160,6 +160,9 @@ void IconButtonWidget::add() m_element = btn; m_id = m_element->getID(); + if (!m_is_visible) + m_element->setVisible(false); + // ---- label if any const stringw& message = getText(); if (message.size() > 0) @@ -222,6 +225,11 @@ void IconButtonWidget::add() m_label->setVisible(false); } + if (!m_is_visible) + { + m_label->setVisible(false); + } + setLabelFont(); m_label->setRightToLeft(translations->isRTLText(message)); diff --git a/src/states_screens/register_screen.cpp b/src/states_screens/register_screen.cpp index fa253d2eb..06cf2f360 100644 --- a/src/states_screens/register_screen.cpp +++ b/src/states_screens/register_screen.cpp @@ -107,6 +107,11 @@ void RegisterScreen::init() makeEntryFieldsVisible(); local_username->setFocusForPlayer(PLAYER_ID_GAME_MASTER); + + if (PlayerManager::get()->getNumPlayers() == 0) + { + getWidget("back")->setVisible(false); + } } // init // ----------------------------------------------------------------------------- From e9becd283b73827d64e585cdde07f530501d7fd5 Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Sun, 10 Jun 2018 18:57:25 -0400 Subject: [PATCH 18/28] More on #3242 --- src/states_screens/register_screen.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/states_screens/register_screen.cpp b/src/states_screens/register_screen.cpp index 06cf2f360..bf0805663 100644 --- a/src/states_screens/register_screen.cpp +++ b/src/states_screens/register_screen.cpp @@ -108,10 +108,14 @@ void RegisterScreen::init() local_username->setFocusForPlayer(PLAYER_ID_GAME_MASTER); - if (PlayerManager::get()->getNumPlayers() == 0) - { - getWidget("back")->setVisible(false); - } + // The behaviour of the screen is slightly different at startup, i.e. + // when it is the first screen: cancel will exit the game, and in + // this case no 'back' error should be shown. + bool has_player_profile = (PlayerManager::get()->getNumPlayers() > 0); + getWidget("back")->setVisible(has_player_profile); + getWidget("cancel")->setLabel(has_player_profile + ? _("Cancel") + : _("Exit game")); } // init // ----------------------------------------------------------------------------- From 81aedd6582d0c6d05efd2dfb10e4ae6a191723e7 Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Sun, 10 Jun 2018 19:09:36 -0400 Subject: [PATCH 19/28] FIx #2912 --- src/online/profile_manager.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/online/profile_manager.cpp b/src/online/profile_manager.cpp index 07583ba1f..2768fb2f1 100644 --- a/src/online/profile_manager.cpp +++ b/src/online/profile_manager.cpp @@ -255,16 +255,18 @@ bool ProfileManager::inPersistent(const uint32_t id) */ OnlineProfile* ProfileManager::addPersistent(OnlineProfile * profile) { - if (inPersistent(profile->getID())) + uint32_t profile_id = profile->getID(); + + if (inPersistent(profile_id)) { - m_profiles_persistent[profile->getID()]->merge(profile); + m_profiles_persistent[profile_id]->merge(profile); } else { - m_profiles_persistent[profile->getID()] = profile; + m_profiles_persistent[profile_id] = profile; } - return m_profiles_persistent[profile->getID()]; + return m_profiles_persistent[profile_id]; } // addPersistent // ------------------------------------------------------------------------ From be5b389d94d46c9088fa02dee6c5ab8ee6f9bf97 Mon Sep 17 00:00:00 2001 From: Alayan-stk-2 Date: Mon, 11 Jun 2018 22:02:46 +0200 Subject: [PATCH 20/28] Better checkboxes (#3291) * Better checkboxes for forest skin * Better checkboxes for ocean skin * Better checkboxes for peach skin * Better checkboxes for ruby skin --- data/skins/forest/glasscheckbox_checked.png | Bin 10774 -> 3306 bytes .../glasscheckbox_checked_deactivated.png | Bin 2404 -> 2637 bytes .../forest/glasscheckbox_checked_focus.png | Bin 13168 -> 4101 bytes data/skins/ocean/glasscheckbox_checked.png | Bin 2838 -> 3157 bytes .../glasscheckbox_checked_deactivated.png | Bin 2404 -> 2637 bytes .../ocean/glasscheckbox_checked_focus.png | Bin 3328 -> 3921 bytes data/skins/peach/glasscheckbox_checked.png | Bin 3496 -> 3710 bytes .../glasscheckbox_checked_deactivated.png | Bin 2404 -> 2637 bytes .../peach/glasscheckbox_checked_focus.png | Bin 4652 -> 5010 bytes data/skins/ruby/glasscheckbox_checked.png | Bin 2676 -> 3055 bytes .../glasscheckbox_checked_deactivated.png | Bin 2404 -> 2637 bytes .../ruby/glasscheckbox_checked_focus.png | Bin 3389 -> 3911 bytes 12 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/skins/forest/glasscheckbox_checked.png b/data/skins/forest/glasscheckbox_checked.png index 7a381159cc5fe55a2aea6dc7f6f1e23151bab05b..7b5fd70f53877eb4c1374f570958c76612c0af55 100644 GIT binary patch delta 3268 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 G2LBh#HIBgm diff --git a/data/skins/forest/glasscheckbox_checked_deactivated.png b/data/skins/forest/glasscheckbox_checked_deactivated.png index a08213dc14c08947ae0a7a67e902461c462b02d5..a7af601ec279c64a381043cdd40a3b41dd713086 100644 GIT binary patch delta 2608 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 Xz_d@hZx5Cv00000NkvXXu0mjfYxK=N literal 13168 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 Q{*MA=C6y$q#f*dh7pvH1&Hw-a diff --git a/data/skins/ocean/glasscheckbox_checked.png b/data/skins/ocean/glasscheckbox_checked.png index d9b404a7f69e35949f25a60ab1df6cdc8dfeedd0..4da054d198f8bf59f85ed9ca1602d30053b03368 100644 GIT binary patch delta 3117 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 Hu0mjfj>Z4y delta 2793 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 vI=n7VBcoFHy)Hk*5JL|&CF`; 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 V@>KOa?WO<#002ovPDHLkV1htS9Mb>* diff --git a/data/skins/peach/glasscheckbox_checked.png b/data/skins/peach/glasscheckbox_checked.png index 906c87a0a36982f89d282a83a85f94d4e2ad5e0c..09cc593e62c3c6b721e060890a28fa1bbfb87220 100644 GIT binary patch delta 3674 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 delta 3456 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 iIEG_5hGY0&;J*N|KMV_@E01IV0000|&CF`; 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 D9;oO< diff --git a/data/skins/ruby/glasscheckbox_checked.png b/data/skins/ruby/glasscheckbox_checked.png index e69bd416bb6324b09d38795ba79c47534222034d..5ad4ff025262acc99d88e0927fd3abbee0610e35 100644 GIT binary patch delta 3011 zcmV;!3q17n6z><1N`DKFNklhhnO(2#U4zZTU?>m=5RZsd5j0g) zqDUl)syrxaB$FnAsI;PKM>2oFEzqky;`ZsR<#dg3#h1 z$V)Eu5(BoeH(vbM-I;q&f6QL*?93P&cJq+pBaL=v=FWLP?ti)W0uwL+6EFc2FaZ!n2AqnR zVNa^DhNe!B+Cr#IG-~FK{;NwJJeYLKu7EXb8qNCYwUy`2<6H5>%pVcV8pAN7%p74h zEoLTW5Ww^~*nf-xCL+fn5?Q633?NhKbmucO>hV^0PikoI-u7`R!5q(^@7y)tT(-2X zBAKkdF_D8Lb-TuBO0k`RlUB1aY}c3|5!F7~D{IHLi$?>=DJGN7_DD6m&ZqT=VwfMFDbA_Al= z7L>VIXzz19ZfFQ-Ys27+FCyN15!LZY5JEr*5`UQtPtRmjvSbAe4?E(+f zl#vktLnSG|5QN?VyK$D1Cc)4IfSMX05^)*S))T_Llqy)tE2y?K5Uzl*Q1IhYs`yzX z;(zi|5FTKroXs^-L6lqth)5}xRm@C6P%&dt0=8YuU|8IHy+GeZYb5T2N|~5CDWAX_-)^D}eJ?Si;gFpnn|) z77-rXvkI(<6mJ@Ghx(%cMmb{3T8rL9!R{DlqM z9$P+fWk&ZUCfjzeT+R&zDS>1X*MH2Kg$LKJ4aM=hmKHc(%jZ3zv#Se#-nldMn=Q@F zsEx;6I#Rl{a=F6y9=f=HFe1AL7~kplFkYqL&Rot+!>T?+u{LWV86wzI}k>;CxpXo_+1LkR&GXcylu0?1J9Q(T!?$`z8gp;3$d;}z)vqU2M`QF(lX270e{Rik^@3#f`033 zCiC~C6zc2h@X!Mf1W@F1!A><6jpDI~9)jVucnIDint^!$lOx&6+FZ6W$f1iG-iF1S zk^R{@nZzBp+=3O$mW@fMdHs6KoI2ID{#bauO_7gZA;`8&Dnl703SaRGYhJu%O8?gu zN+z*o!v<7WR*p()<$sD5xP9eHS9tAVUR+_N&)+!di4e14J7t~-l?t|$kZN?Jr26|I zJ37ik%i(Ni*HEquWsvhZfI~bZq`lRPBKm)RwMa7K$)}&jaAx!d?c|3aV%O251zd(v zj7$FyqwSlwNU1Cu$9ny^JU}tWc(lbzf4l3y^6y&SeHW)b{(pE(S}ptc({Quwbxt|cz>*|4KKg>rYpE*73g_A!m{|y)b*`Onm?T`+@FmSbxzd9^bSHmKM?jFB~|4_s^a! zxV#qHvRs>DJ8b;+J%8!t_XKg9?;?DGa3<`@_2x|H!w`(%LZYkFCUW%z%N@{F*p?5lW1DA z28$Oj27kJ{3*UEl`0cB&2FqbsNgQEKs94mvST+R^AOo>LRP2t? zsB4u-1g%Gp;=Pcit+{jN;K6n4fS#U0Z0Bci_aZ*qRbg*w$_q5C6rOnQImnP*pYJs_VZp+MKyUAer;Nm~ zwrp=NZoTzZ+`MvSaB6GY*!Ijb$S74@eZq3U6~LJL0t^N-jKEY_GZBrBY;45p>+xbs zOMiHt09@g<%Z|1&l1}3n+qQ-880i+dx3^FMY&%c|*qsR`#xoNfQvr^{B6K}KGvHnR z?(YxZ8uO0#Dn@_n7c`Z+{MMMpOo9ejajs+q2!k=4bsU&BE5)!Z3sdSQqw0Jo26ybZ{2?i?z@sT7Y>Cak?&ud@ zhlV~RH9UxEGwNYkR=(?G6^&ANWeiR^%Al&M6xj>C-5v47jO!AK1f-Nu3``r&f-hYH zE?vSG1r&*3N=+Pznpz|h37DpdbbtT3H2n;i`JQBKX;(D=I!}H@+F4 zH2bf%9jWc??8L>37hxF2m*SwLloC^>OhH3K157iM>g!21&2LnPw*T?n(ho=g!22Ji zoa?WzJC;oLtrcR@GmVXN=Pq10|4VmNHkVCzK&Cd{aDzB<;zVj(XNAA=hdiY}i{tX* za=WbK--j7>tjd>vRS`3?r5=}`fC-p@37CKhn1HV?{2x~dw*pF5K(_z@002ovPDHLk FV1l=oy6pe} delta 2629 zcmV-L3cB_07xWa6N`C+cbV*G`2j2@01~e`Gc&kCo!Ao+~hDeK7`~f^{-MVCNL@ACPMvrvw7N1_B{CkUzj@8w|Dmu6ox(_QuDQ2NrmixwEH}ua?ePH!~+R{K(x1SEA{%zb6J+5*=(ZuY!mA%E7+@z z0jEy`UG%P?Ml>9wVW$742g zZ2)#40XR#jRSk70A;%fgG{ySH7WU3h1Mj^D+`0vvJlV}~$RttmIWwSj&J+<~ZIMyt zBGch>nYX_W+`o^#4?jeGV;$r5NhqbD6q#0w>wm45+a6c|2L~DbdtvX?E0^pEdd`G% z4BWiwnLl^VGgH>85UEuDKe?B`WsMT$e`UVhmo0!9Xf*uqyLaI}`%Ewa0F=#Kb-!gb zfOH@MXhqoAO<%g`>=;4I2Y`tQpi=P!iuRQ9mvgzMLPkZ?KwJPZS17OLT=7|@;%Uj5 z2Y;ANC*_)TM}w2;17+E%Z0M}vy383Ip zA##iML%?G8gwDFTNvW29XJ>&{tG{oRXk-AkkAk>A0r2x8ylerY zIcxJvG%EMT0GBQmL2+XPSX;~AUt9CfLIGd8~yBR>AM=?M`lq5FJ<&1~E+UWq7FBciz-St<{o$}DaAV-@T zVZjn2axM;9iC(41CWeNP2vNKiI#NSn4IeVRxR?W4 z8BZ1$ee4f2z*s}17ad_D#H`Y6$cfOv!&+c@xp$jvc{yGtzraW{&VQCx_wC>UV1AAP z9I=c@c`KQm{FZP?1gx(1UPH#}YW`d@$nv_eZ0Ooco^G*s>f#x!HC z*KCLd^|~)~0O09U&wsQqwYut)A*uUAKQD*k()XDeL<~@W`xQr8K+ko`4G$q+DW9Q* zX1lUd)DjVJ{W@@P;IHe~yBS+qDVn`8`F*+77I6XR*z~hu@#4i0;H94=xv$Fw_~*v> zuojxMBs=oH%rCpb&W?ZA7{69HU$7xb^7lg;39L0`Jqr0_3V)!N3`6s;R6H>v3}f;f zetEg5wMGLtdsg~w{sMe^yC+ydk~l}gbKyFl=3uz)TX5C%fch*q=)o;>N! z^Jiy$S}X6$m1Vy6FBe=-p2XJ{p@_Q`l@{+~zGzEKE9wxDjbcCTQKPB?FxsXDQt_Ei<4e$@la5q#>;tsxQ`fas((sbJca?s7)O`GU@${<9eoi{ zs#c3#+E5rVoGANzd1EBjb1ZL+h!C~*E~d*#D(hC;oft|cm}N}Set-~QOt_5{-x}*x zJIMUwY>kC@5Y`%njy)&swrjwr?{+{8pg5Qc10kp#wK*EwF&=btH0lCO3|9T*0ey6Y z7>ZPzh<|LM77*!|1#r~%c=&#q`4Axc-5xK2ATxtNj)*eSLjXbR7^lXQr&{T~f?fLhJ)#p$uF4(XP)nYRc< zz=H?C{ri5B9Fd7>I_Cc`azrL3oaaYm@ZbUD?0_RjagnvNnWc zat!9NlPX);Si4tmoPV>?Xuw(v$H2=6ZSc-cUdw+AgDMqFP1MntIE_Z50b>lB&%XNC z#eWMY_TPNtG=BGol^3e@3gg$mdfj~SNWW5_y!7WZt=wK(TEhDJI<(f`g@cl?)?#XE z3e(fmFsAjl&8G`LyLQEW`TIZq^T-1deF@;5JJ;0Q+!?d5F#f~TP5w4GDoX&V@5)ix zPP_TB!q)pUGy9M4-P`EvsO+!~OIMhg{yrZMOMe~5<>|0p*8cZlsP}H25C43krn5nh n%a6urjK*k;#%PSj_jmjk)VgzgRc_@$00000NkvXXu0mjfT~ie7 diff --git a/data/skins/ruby/glasscheckbox_checked_deactivated.png b/data/skins/ruby/glasscheckbox_checked_deactivated.png index a08213dc14c08947ae0a7a67e902461c462b02d5..a7af601ec279c64a381043cdd40a3b41dd713086 100644 GIT binary patch delta 2608 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 e_d|Jo{{L6bM!L8Cv<=<>0000 Date: Mon, 11 Jun 2018 22:22:43 +0200 Subject: [PATCH 21/28] Try to create external/internal directory if it's not available. Typically it always exists on new phones, but I didn't find any information if it's guaranteed to exist and it's missing on my old phone. --- src/io/assets_android.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/io/assets_android.cpp b/src/io/assets_android.cpp index 17877f10c..7977dcc43 100644 --- a/src/io/assets_android.cpp +++ b/src/io/assets_android.cpp @@ -60,10 +60,18 @@ void AssetsAndroid::init() paths.push_back(getenv("SUPERTUXKART_DATADIR")); if (global_android_app->activity->externalDataPath) + { + m_file_manager->checkAndCreateDirectoryP( + global_android_app->activity->externalDataPath); paths.push_back(global_android_app->activity->externalDataPath); + } if (global_android_app->activity->internalDataPath) + { + m_file_manager->checkAndCreateDirectoryP( + global_android_app->activity->internalDataPath); paths.push_back(global_android_app->activity->internalDataPath); + } if (getenv("EXTERNAL_STORAGE")) paths.push_back(getenv("EXTERNAL_STORAGE")); From e44266b526e4103063ea1933a7465ce87878114e Mon Sep 17 00:00:00 2001 From: Deve Date: Tue, 12 Jun 2018 22:25:46 +0200 Subject: [PATCH 22/28] Always set blend func in GLES 2.0. It's often changed in STK engine and the value stored in irrlicht variable may be invalid. Fixes #3296 --- lib/irrlicht/source/Irrlicht/COGLES2Driver.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/irrlicht/source/Irrlicht/COGLES2Driver.cpp b/lib/irrlicht/source/Irrlicht/COGLES2Driver.cpp index e8bbb3eb2..aad4b0867 100644 --- a/lib/irrlicht/source/Irrlicht/COGLES2Driver.cpp +++ b/lib/irrlicht/source/Irrlicht/COGLES2Driver.cpp @@ -2753,26 +2753,26 @@ namespace video void COGLES2CallBridge::setBlendFunc(GLenum source, GLenum destination) { - if(BlendSource != source || BlendDestination != destination) - { + //if(BlendSource != source || BlendDestination != destination) + //{ glBlendFunc(source, destination); BlendSource = source; BlendDestination = destination; - } + //} } void COGLES2CallBridge::setBlend(bool enable) { - if(Blend != enable) - { + //if(Blend != enable) + //{ if (enable) glEnable(GL_BLEND); else glDisable(GL_BLEND); Blend = enable; - } + //} } void COGLES2CallBridge::setCullFaceFunc(GLenum mode) From 7d9adf5b935feee810cd9474fe4e72b4796a291f Mon Sep 17 00:00:00 2001 From: Deve Date: Tue, 12 Jun 2018 23:01:13 +0200 Subject: [PATCH 23/28] Avoid a crash when user name is empty --- src/states_screens/register_screen.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/states_screens/register_screen.cpp b/src/states_screens/register_screen.cpp index bf0805663..dcad9e46e 100644 --- a/src/states_screens/register_screen.cpp +++ b/src/states_screens/register_screen.cpp @@ -245,6 +245,13 @@ void RegisterScreen::doRegister() { stringw local_name = getWidget("local_username") ->getText().trim(); + + if (local_name.empty()) + { + m_info_widget->setErrorColor(); + m_info_widget->setText(_("User name cannot be empty."), false); + return; + } handleLocalName(local_name); From aee9e7ffa1a15f31fc6c2cead090af86ca6dd9c8 Mon Sep 17 00:00:00 2001 From: Alayan-stk-2 Date: Wed, 13 Jun 2018 02:36:33 +0200 Subject: [PATCH 24/28] Unlock cutscene improvements (#3298) * Don't display unlocked features out of screen when there are 4 to 6-7 And more regular spacing between them * Remove outdated workaround * Store the previous number of story mode point * Allow to get the previous points from player profile * Fix points estimation for finding unlocked tracks - Updated to reflect that the player's points are now updated before this function - Use the real previous number of points rather than trying to estimate the point change (otherwise, the points computation method would have to be duplicated here to not have a bug with GPs who give more points than single race challenges). --- src/challenges/story_mode_status.cpp | 5 +++ src/challenges/story_mode_status.hpp | 4 ++ src/config/player_profile.hpp | 2 + src/states_screens/feature_unlocked.cpp | 54 ++++++------------------- src/states_screens/feature_unlocked.hpp | 4 -- 5 files changed, 24 insertions(+), 45 deletions(-) diff --git a/src/challenges/story_mode_status.cpp b/src/challenges/story_mode_status.cpp index 266f6548d..65ab04976 100644 --- a/src/challenges/story_mode_status.cpp +++ b/src/challenges/story_mode_status.cpp @@ -31,6 +31,7 @@ StoryModeStatus::StoryModeStatus(const XMLNode *node) { m_points = 0; + m_points_before = 0; m_next_unlock_points = 0; m_first_time = true; m_easy_challenges = 0; @@ -79,6 +80,7 @@ bool StoryModeStatus::isLocked(const std::string& feature) //----------------------------------------------------------------------------- void StoryModeStatus::computeActive() { + int old_points = m_points; m_points = 0; m_next_unlock_points = 0; m_easy_challenges = 0; @@ -182,6 +184,9 @@ void StoryModeStatus::computeActive() // now we have the number of points. + if (old_points != m_points) + m_points_before = old_points; + unlockFeatureByList(); //Actually lock the tracks diff --git a/src/challenges/story_mode_status.hpp b/src/challenges/story_mode_status.hpp index 94a7ee1c2..5f1da7423 100644 --- a/src/challenges/story_mode_status.hpp +++ b/src/challenges/story_mode_status.hpp @@ -62,6 +62,7 @@ private: const ChallengeStatus *m_current_challenge; int m_points; + int m_points_before; // used for unlocks int m_next_unlock_points; /** Set to false after the initial stuff (intro, select kart, etc.) */ @@ -101,6 +102,9 @@ public: /** Returns the number of points accumulated. */ int getPoints () const { return m_points; } // ------------------------------------------------------------------------ + /** Returns the number of points before the previous point increase */ + int getPointsBefore () const { return m_points_before; } + // ------------------------------------------------------------------------ /** Returns the number of points needed by the next unlockable. 0 if none. */ int getNextUnlockPoints () const { return m_next_unlock_points; } // ------------------------------------------------------------------------ diff --git a/src/config/player_profile.hpp b/src/config/player_profile.hpp index 519f5f5c3..5f20c9dda 100644 --- a/src/config/player_profile.hpp +++ b/src/config/player_profile.hpp @@ -230,6 +230,8 @@ public: // ------------------------------------------------------------------------ unsigned int getPoints() const { return m_story_mode_status->getPoints(); } // ------------------------------------------------------------------------ + unsigned int getPointsBefore() const { return m_story_mode_status->getPointsBefore(); } + // ------------------------------------------------------------------------ unsigned int getNextUnlockPoints() const { return m_story_mode_status->getNextUnlockPoints(); } // ------------------------------------------------------------------------ void setFirstTime(bool b) { m_story_mode_status->setFirstTime(b); } diff --git a/src/states_screens/feature_unlocked.cpp b/src/states_screens/feature_unlocked.cpp index 06664f7da..591a5f5f9 100644 --- a/src/states_screens/feature_unlocked.cpp +++ b/src/states_screens/feature_unlocked.cpp @@ -165,10 +165,6 @@ FeatureUnlockedCutScene::FeatureUnlockedCutScene() : CutsceneScreen("feature_unlocked.stkgui") { m_key_angle = 0; - -#ifdef USE_IRRLICHT_BUG_WORKAROUND - m_avoid_irrlicht_bug = NULL; -#endif } // FeatureUnlockedCutScene // ---------------------------------------------------------------------------- @@ -181,12 +177,6 @@ void FeatureUnlockedCutScene::loadedFromFile() void FeatureUnlockedCutScene::onCutsceneEnd() { -#ifdef USE_IRRLICHT_BUG_WORKAROUND - if (m_avoid_irrlicht_bug) - irr_driver->removeNode(m_avoid_irrlicht_bug); - m_avoid_irrlicht_bug = NULL; -#endif - m_unlocked_stuff.clearAndDeleteAll(); #ifndef SERVER_ONLY if (CVS->isGLSL()) @@ -205,8 +195,10 @@ void FeatureUnlockedCutScene::onCutsceneEnd() void FeatureUnlockedCutScene::findWhatWasUnlocked(RaceManager::Difficulty difficulty,std::vector& unlocked) { PlayerProfile *player = PlayerManager::getCurrentPlayer(); - int points_before = player->getPoints(); - int points_now = points_before + CHALLENGE_POINTS[difficulty]; + + // The number of points is updated before this function is called + int points_before = player->getPointsBefore(); + int points_now = player->getPoints(); std::vector tracks; std::vector gps; @@ -362,18 +354,6 @@ void FeatureUnlockedCutScene::init() #ifdef DEBUG m_unlocked_stuff[n].m_root_gift_node->setName("unlocked kart"); -#endif -#ifdef USE_IRRLICHT_BUG_WORKAROUND - // If a mesh with this material is added, irrlicht will - // display the 'continue' text (otherwise the text is - // not visible). This is a terrible work around, but allows - // stk to be released without waiting for the next - // irrlicht version. - video::SMaterial m; - m.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - scene::IMesh* mesh = - irr_driver->createTexturedQuadMesh(&m, 0, 0); - m_avoid_irrlicht_bug = irr_driver->addMesh(mesh); #endif } #ifndef SERVER_ONLY @@ -475,6 +455,7 @@ void FeatureUnlockedCutScene::onUpdate(float dt) m_global_time += dt; const int unlockedStuffCount = m_unlocked_stuff.size(); + // When the chest has opened but the items are not yet at their final position if (m_global_time > GIFT_EXIT_FROM && m_global_time < GIFT_EXIT_TO) { float progress_factor = (m_global_time - GIFT_EXIT_FROM) / (GIFT_EXIT_TO - GIFT_EXIT_FROM); @@ -489,30 +470,21 @@ void FeatureUnlockedCutScene::onUpdate(float dt) // when there are more than 1 unlocked items, make sure they each // have their own path when they move - if (unlockedStuffCount > 1) - { - if (n == 1) pos.X -= 1.0f*dt*float( int((n + 1)/2) ); - else if (n > 1) pos.X += 1.0f*dt*(n - 0.3f); + // and that they won't end offscreen in usual situations - //else pos.X += 6.2f*dt*float( int((n + 1)/2) ); - //Log::info("FeatureUnlockedCutScene", "Object %d moving by %f", n, - // (n % 2 == 0 ? -4.0f : 4.0f)*float( n/2 + 1 )); - } - else - { - //pos.X -= 2.0f*dt; - } - - //if (m_global_time > GIFT_EXIT_FROM + 2.0f) pos.Z -= 2.0f*dt; + // Put the trophy in center + float pos_value = (n == 0) ? unlockedStuffCount/2 : + (n == unlockedStuffCount/2) ? 0 : n; + float offset = (float) pos_value - ((float) unlockedStuffCount)/2.0f + 0.5f; + offset *= (unlockedStuffCount <= 3) ? 1.4f : + (unlockedStuffCount <= 5) ? 1.2f : 1.0f; + pos.X += offset*dt; pos.Z = smoothed_progress_factor * -4.0f; m_unlocked_stuff[n].m_root_gift_node->setPosition(pos); } } - else if (m_global_time < GIFT_EXIT_FROM) - { - } for (int n=0; n Date: Thu, 14 Jun 2018 01:43:18 +0200 Subject: [PATCH 25/28] Fix #3100 (#3299) * Fix #3100 * Fix server only compilation --- src/states_screens/user_screen.cpp | 45 ++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/src/states_screens/user_screen.cpp b/src/states_screens/user_screen.cpp index 4332ce8c0..d1300e207 100644 --- a/src/states_screens/user_screen.cpp +++ b/src/states_screens/user_screen.cpp @@ -17,6 +17,7 @@ #include "states_screens/user_screen.hpp" +#include "addons/news_manager.hpp" #include "audio/sfx_manager.hpp" #include "challenges/unlock_manager.hpp" #include "config/player_manager.hpp" @@ -26,6 +27,7 @@ #include "guiengine/widgets/label_widget.hpp" #include "guiengine/widgets/list_widget.hpp" #include "guiengine/widgets/text_box_widget.hpp" +#include "online/request_manager.hpp" #include "states_screens/dialogs/message_dialog.hpp" #include "states_screens/dialogs/kart_color_slider_dialog.hpp" #include "states_screens/dialogs/recovery_dialog.hpp" @@ -326,18 +328,49 @@ void BaseUserScreen::eventCallback(Widget* widget, } else if (name == "online") { - // If online access is not allowed, do not accept an online account - // but advice the user where to enable this option. + // If online access is not allowed, + // give the player the choice to enable this option. if (m_online_cb->getState()) { if (UserConfigParams::m_internet_status == Online::RequestManager::IPERM_NOT_ALLOWED) { - m_info_widget->setText( - _("Internet access is disabled, please enable it in the options"), - true); + irr::core::stringw message = + _("Internet access is disabled. Do you want to enable it ?"); + + class ConfirmInternet : public MessageDialog::IConfirmDialogListener + { + BaseUserScreen *m_parent_screen; + private: + GUIEngine::CheckBoxWidget *m_cb; + public: + virtual void onConfirm() + { + UserConfigParams::m_internet_status = + Online::RequestManager::IPERM_ALLOWED; +#ifndef SERVER_ONLY + NewsManager::get()->init(false); +#endif + m_parent_screen->makeEntryFieldsVisible(); + ModalDialog::dismiss(); + } // onConfirm + virtual void onCancel() + { + m_cb->setState(false); + m_parent_screen->makeEntryFieldsVisible(); + ModalDialog::dismiss(); + } // onCancel + // ------------------------------------------------------------ + ConfirmInternet(BaseUserScreen *parent, GUIEngine::CheckBoxWidget *online_cb) + { + m_parent_screen = parent; + m_cb = online_cb; + } + }; // ConfirmInternet + SFXManager::get()->quickSound( "anvil" ); - m_online_cb->setState(false); + new MessageDialog(message, MessageDialog::MESSAGE_DIALOG_CONFIRM, + new ConfirmInternet(this, m_online_cb), true); } } makeEntryFieldsVisible(); From 96a07be6bc3acb3fe8b31c9b71386a49ebc4c778 Mon Sep 17 00:00:00 2001 From: Alayan-stk-2 Date: Thu, 14 Jun 2018 01:46:20 +0200 Subject: [PATCH 26/28] Fix stupidly long line full of whitespace (#3303) --- 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 996cab11d..c1ebe765c 100644 --- a/src/guiengine/skin.cpp +++ b/src/guiengine/skin.cpp @@ -2498,4 +2498,4 @@ void Skin::setSize (EGUI_DEFAULT_SIZE which, s32 texture_size) void Skin::setSpriteBank (IGUISpriteBank *bank) { m_fallback_skin->setSpriteBank(bank); -} // setSpriteBank +} // setSpriteBank From e14ea99acdba9637a09b68cda4a29d22c48a190a Mon Sep 17 00:00:00 2001 From: Deve Date: Fri, 15 Jun 2018 21:06:29 +0200 Subject: [PATCH 27/28] Don't use pkg-config on macOS Fixes #3305 --- cmake/FindFribidi.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/FindFribidi.cmake b/cmake/FindFribidi.cmake index 4ef0405e8..70fa2cd8c 100644 --- a/cmake/FindFribidi.cmake +++ b/cmake/FindFribidi.cmake @@ -9,7 +9,7 @@ # FRIBIDI_LIBRARIES # Fribidi library list -if(UNIX) +if(UNIX AND NOT APPLE) include(FindPkgConfig) pkg_check_modules(FRIBIDI fribidi) else() From bd6492f5cfba2473205e00afc17f825a8fb65fa1 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 16 Jun 2018 13:55:20 +0800 Subject: [PATCH 28/28] Fix #3309 --- src/karts/kart.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index b919884a7..a65f076f4 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -2780,7 +2780,8 @@ void Kart::loadData(RaceManager::KartType type, bool is_animated_model) m_skidmarks = new SkidMarks(*this); } - if (CVS->isGLSL() && !CVS->isShadowEnabled()) + if (CVS->isGLSL() && !CVS->isShadowEnabled() && m_kart_properties + ->getShadowMaterial()->getSamplerPath(0) != "unicolor_white") { m_shadow = new Shadow(m_kart_properties->getShadowMaterial(), *this); }