diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index d014aaa06..e96d49b29 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -526,6 +526,7 @@ void Powerup::hitBonusBox(const ItemState &item_state) // (1) The item id // (2) The time // (3) The position of the kart + // (4) An extra random 64bit integer // Using (1) means that not all boxes at a certain time for a kart // will give the same box. Using (2) means that the item will // change over time - even if the next item is displayed, it @@ -533,7 +534,8 @@ void Powerup::hitBonusBox(const ItemState &item_state) // of the time component it will also be difficult to get the // item at the right time. Using (3) adds another cheat-prevention // layer: even if a cheater is waiting for the right sequence - // of items, if he is overtaken the sequence will change. + // of items, if he is overtaken the sequence will change, using (4) + // to avoid same item sequence when starting // // In order to increase the probability of correct client prediction // in networking (where there might be 1 or 2 frames difference @@ -544,7 +546,8 @@ void Powerup::hitBonusBox(const ItemState &item_state) // number to spread the random values across the (typically 200) // weights used in the PowerupManager - same for the position. uint64_t random_number = item_state.getItemId() * 31 + - world->getTicksSinceStart() / 10 + position * 23; + world->getTicksSinceStart() / 10 + position * 23 + + powerup_manager->getRandomSeed(); // Use this random number as a seed of a PRNG (based on the one in // bullet's btSequentialImpulseConstraintSolver) to avoid getting diff --git a/src/items/powerup_manager.cpp b/src/items/powerup_manager.cpp index f0d4f2ecb..596288044 100644 --- a/src/items/powerup_manager.cpp +++ b/src/items/powerup_manager.cpp @@ -43,6 +43,7 @@ PowerupManager* powerup_manager=0; /** The constructor initialises everything to zero. */ PowerupManager::PowerupManager() { + m_random_seed.store(0); for(int i=0; i #include #include #include @@ -151,6 +153,11 @@ private: WeightsData m_current_item_weights; PowerupType getPowerupType(const std::string &name) const; + + /** Seed for random powerup, for local game it will use a random number, + * for network games it will use the start time from server. */ + std::atomic m_random_seed; + public: static void unitTesting(); @@ -171,6 +178,11 @@ public: /** Returns the mesh for a certain powerup. * \param type Mesh type for which the model is returned. */ irr::scene::IMesh *getMesh(int type) const {return m_all_meshes[type];} + // ------------------------------------------------------------------------ + uint64_t getRandomSeed() const { return m_random_seed.load(); } + // ------------------------------------------------------------------------ + void setRandomSeed(uint64_t seed) { m_random_seed.store(seed); } + }; // class PowerupManager extern PowerupManager* powerup_manager; diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index a7be5c338..a84938ba7 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -26,6 +26,7 @@ #include "guiengine/screen_keyboard.hpp" #include "input/device_manager.hpp" #include "items/item_manager.hpp" +#include "items/powerup_manager.hpp" #include "karts/kart_properties_manager.hpp" #include "modes/linear_world.hpp" #include "network/crypto.hpp" @@ -816,6 +817,7 @@ void ClientLobby::startGame(Event* event) { World::getWorld()->setPhase(WorldStatus::SERVER_READY_PHASE); uint64_t start_time = event->data().getUInt64(); + powerup_manager->setRandomSeed(start_time); joinStartGameThread(); m_start_game_thread = std::thread([start_time, this]() { diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index ac3dbc330..55b02bc6a 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -20,6 +20,7 @@ #include "config/user_config.hpp" #include "items/item_manager.hpp" +#include "items/powerup_manager.hpp" #include "karts/abstract_kart.hpp" #include "karts/controller/player_controller.hpp" #include "karts/kart_properties_manager.hpp" @@ -2369,6 +2370,7 @@ void ServerLobby::configPeersStartTime() // Start up time will be after 2500ms, so even if this packet is sent late // (due to packet loss), the start time will still ahead of current time uint64_t start_time = STKHost::get()->getNetworkTimer() + (uint64_t)2500; + powerup_manager->setRandomSeed(start_time); NetworkString* ns = getNetworkString(10); ns->addUInt8(LE_START_RACE).addUInt64(start_time); sendMessageToPeers(ns, /*reliable*/true); diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 951a8c2e7..0cd358200 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -53,6 +53,7 @@ #include "items/item.hpp" #include "items/item_manager.hpp" #include "items/network_item_manager.hpp" +#include "items/powerup_manager.hpp" #include "karts/abstract_kart.hpp" #include "karts/kart_properties.hpp" #include "modes/linear_world.hpp" @@ -1855,8 +1856,10 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) else { // Seed random engine locally - ItemManager::updateRandomSeed((uint32_t)StkTime::getTimeSinceEpoch()); + uint32_t seed = (uint32_t)StkTime::getTimeSinceEpoch(); + ItemManager::updateRandomSeed(seed); ItemManager::create(); + powerup_manager->setRandomSeed(seed); } // Set the default start positions. Node that later the default