diff --git a/CMakeLists.txt b/CMakeLists.txt index e3dc736ee..5a028c745 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,6 +113,7 @@ if(WIN32) set(ENV{OPENALDIR} ${PROJECT_SOURCE_DIR}/${DEPENDENCIES}) add_definitions(-D_IRR_STATIC_LIB_) add_definitions(-DNO_IRR_COMPILE_WITH_X11_) + include_directories(${PROJECT_SOURCE_DIR}/${DEPENDENCIES}/include) endif() if(USE_GLES2) diff --git a/data/stk_config.xml b/data/stk_config.xml index 5ea4a4178..434fdf33a 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -203,8 +203,11 @@ <!-- Networking state-frequency: how many states the server will send per second. + steering-reduction: Reduce a remote kart's steering by this factor + each frame. This helps reduces oversteering by high latency + clients when they only do minor steering adjustments. --> - <networking state-frequency="10"/> + <networking state-frequency="10" steering-reduction="1.0"/> <!-- The field od views for 1-4 player split screen. fov-3 is actually not used (since 3 player split screen uses the diff --git a/lib/irrlicht/CMakeLists.txt b/lib/irrlicht/CMakeLists.txt index 3c5a3907b..419caf9a6 100644 --- a/lib/irrlicht/CMakeLists.txt +++ b/lib/irrlicht/CMakeLists.txt @@ -1,18 +1,21 @@ # CMakeLists.txt for Irrlicht in STK -find_package(PNG REQUIRED) -find_package(JPEG REQUIRED) - -include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include/" - "${JPEG_INCLUDE_DIR}" - "${PNG_INCLUDE_DIRS}" - "${ZLIB_INCLUDE_DIR}") - -if(MSVC OR APPLE) - include_directories("${CMAKE_CURRENT_BINARY_DIR}/../zlib/" # For zconf.h on WIN32 - "${CMAKE_CURRENT_BINARY_DIR}/../libpng/") -endif() - if(NOT SERVER_ONLY) + find_package(PNG REQUIRED) + find_package(JPEG REQUIRED) + + include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include/" + "${JPEG_INCLUDE_DIR}" + "${PNG_INCLUDE_DIRS}" + "${ZLIB_INCLUDE_DIR}") + + if(MSVC) + include_directories("${CMAKE_CURRENT_BINARY_DIR}/../zlib/") # For zconf.h on WIN32 + endif() + + if(MSVC OR APPLE) + include_directories("${CMAKE_CURRENT_BINARY_DIR}/../libpng/") + endif() + if(NOT USE_GLES2) find_package(OpenGL REQUIRED) include_directories(${OPENGL_INCLUDE_DIR}) @@ -58,6 +61,13 @@ if(NOT SERVER_ONLY) endif() endif() else() + include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include/") + if(MSVC) + include_directories("${CMAKE_CURRENT_BINARY_DIR}/../zlib/") + else() + find_package(ZLIB REQUIRED) + include_directories("${ZLIB_INCLUDE_DIR}") + endif() add_definitions(-DNO_IRR_COMPILE_WITH_LIBPNG_) add_definitions(-DNO_IRR_COMPILE_WITH_LIBJPEG_) add_definitions(-DNO_IRR_COMPILE_WITH_BMP_LOADER_) diff --git a/sources.cmake b/sources.cmake index d4f28ae4d..ba4868d71 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,5 +1,5 @@ # Modify this file to change the last-modified date when you add/remove a file. -# This will then trigger a new cmake run automatically. +# This will then trigger a new cmake run automatically. file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp") file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp") file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") diff --git a/src/animations/animation_base.hpp b/src/animations/animation_base.hpp index 8de3102b7..a462e3823 100644 --- a/src/animations/animation_base.hpp +++ b/src/animations/animation_base.hpp @@ -47,9 +47,6 @@ private: * one time only (which might get triggered more than once). */ enum AnimTimeType { ATT_CYCLIC, ATT_CYCLIC_ONCE } m_anim_type; - /** The current time used in the IPOs. */ - float m_current_time; - /** The inital position of this object. */ Vec3 m_initial_xyz; @@ -67,6 +64,9 @@ protected: float m_animation_duration; + /** The current time used in the IPOs. */ + float m_current_time; + public: AnimationBase(const XMLNode &node); AnimationBase(Ipo *ipo); diff --git a/src/animations/three_d_animation.cpp b/src/animations/three_d_animation.cpp index 5627d0dc0..a1ab67294 100644 --- a/src/animations/three_d_animation.cpp +++ b/src/animations/three_d_animation.cpp @@ -75,15 +75,13 @@ void ThreeDAnimation::updateWithWorldTicks() Vec3 xyz = m_object->getPosition(); Vec3 scale = m_object->getScale(); - float position = 0.0f; if (!m_is_paused) { int cur_ticks = World::getWorld()->getTicksSinceStart(); - float cur_time = stk_config->ticks2Time(cur_ticks); - position = fmodf(cur_time, m_animation_duration); + m_current_time = stk_config->ticks2Time(cur_ticks); } - AnimationBase::getAt(position, &xyz, &m_hpr, &scale); //updates all IPOs + AnimationBase::getAt(m_current_time, &xyz, &m_hpr, &scale); //updates all IPOs //m_node->setPosition(xyz.toIrrVector()); //m_node->setScale(scale.toIrrVector()); diff --git a/src/config/stk_config.cpp b/src/config/stk_config.cpp old mode 100644 new mode 100755 index 2828d4d86..d1d3be65c --- a/src/config/stk_config.cpp +++ b/src/config/stk_config.cpp @@ -149,8 +149,10 @@ void STKConfig::load(const std::string &filename) CHECK_NEG(m_minimap_ai_icon, "minimap ai_icon" ); CHECK_NEG(m_minimap_player_icon, "minimap player_icon" ); CHECK_NEG(m_smooth_angle_limit, "physics smooth-angle-limit" ); - CHECK_NEG(m_default_track_friction, "physics default-track-friction" ); - CHECK_NEG(m_physics_fps, "physics fps" ); + CHECK_NEG(m_default_track_friction, "physics default-track-friction"); + CHECK_NEG(m_physics_fps, "physics fps" ); + CHECK_NEG(m_network_state_frequeny, "network state-frequency" ); + CHECK_NEG(m_network_steering_reduction,"network steering-reduction" ); CHECK_NEG(m_default_moveable_friction, "physics default-moveable-friction"); CHECK_NEG(m_solver_iterations, "physics: solver-iterations" ); CHECK_NEG(m_network_state_frequeny, "network solver-state-frequency" ); @@ -200,6 +202,7 @@ void STKConfig::init_defaults() m_solver_iterations = -100; m_solver_set_flags = 0; m_solver_reset_flags = 0; + m_network_steering_reduction = 1.0f; m_title_music = NULL; m_solver_split_impulse = false; m_smooth_normals = false; @@ -433,6 +436,7 @@ void STKConfig::getAllData(const XMLNode * root) if (const XMLNode *networking_node = root->getNode("networking")) { networking_node->get("state-frequency", &m_network_state_frequeny); + networking_node->get("steering-reduction", &m_network_steering_reduction); } if(const XMLNode *replay_node = root->getNode("replay")) diff --git a/src/config/stk_config.hpp b/src/config/stk_config.hpp index 786d186ab..922800c9d 100644 --- a/src/config/stk_config.hpp +++ b/src/config/stk_config.hpp @@ -89,6 +89,11 @@ public: /** How many state updates per second the server will send. */ int m_network_state_frequeny; + /** In case of a network race, remote karts will get their steering somewhat + * reduced each frame. This reduces stutter when a kart only does small + * steering adjustments. */ + float m_network_steering_reduction; + /** If the angle between a normal on a vertex and the normal of the * triangle are more than this value, the physics will use the normal * of the triangle in smoothing normal. */ diff --git a/src/items/bowling.hpp b/src/items/bowling.hpp index a75d82414..0309b0081 100644 --- a/src/items/bowling.hpp +++ b/src/items/bowling.hpp @@ -53,9 +53,9 @@ public: Bowling(AbstractKart* kart); virtual ~Bowling(); static void init(const XMLNode &node, scene::IMesh *bowling); - virtual bool updateAndDelete(int ticks); - virtual bool hit(AbstractKart* kart, PhysicalObject* obj=NULL); - virtual HitEffect *getHitEffect() const; + virtual bool updateAndDelete(int ticks) OVERRIDE; + virtual bool hit(AbstractKart* kart, PhysicalObject* obj=NULL) OVERRIDE; + virtual HitEffect *getHitEffect() const OVERRIDE; }; // Bowling diff --git a/src/items/cake.cpp b/src/items/cake.cpp index 5b302a753..a7f7c0128 100644 --- a/src/items/cake.cpp +++ b/src/items/cake.cpp @@ -119,11 +119,7 @@ Cake::Cake (AbstractKart *kart) : Flyable(kart, PowerupManager::POWERUP_CAKE) //do not adjust height according to terrain setAdjustUpVelocity(false); - - m_body->setActivationState(DISABLE_DEACTIVATION); - - m_body->applyTorque( btVector3(5,-3,7) ); - + additionalPhysicsProperties(); } // Cake // ----------------------------------------------------------------------------- diff --git a/src/items/cake.hpp b/src/items/cake.hpp index d22b71679..4d6184248 100644 --- a/src/items/cake.hpp +++ b/src/items/cake.hpp @@ -46,24 +46,36 @@ private: /** Which kart is targeted by this projectile (NULL if none). */ Moveable* m_target; + + // ------------------------------------------------------------------------ + virtual void additionalPhysicsProperties() OVERRIDE + { + m_body->setActivationState(DISABLE_DEACTIVATION); + m_body->clearForces(); + m_body->applyTorque(btVector3(5.0f, -3.0f, 7.0f)); + } + public: Cake (AbstractKart *kart); static void init (const XMLNode &node, scene::IMesh *cake_model); - virtual bool hit(AbstractKart* kart, PhysicalObject* obj=NULL); + virtual bool hit(AbstractKart* kart, PhysicalObject* obj=NULL) OVERRIDE; // ------------------------------------------------------------------------ - virtual void hitTrack () { hit(NULL); } + virtual void hitTrack () OVERRIDE { hit(NULL); } // ------------------------------------------------------------------------ /** Kinematic objects are not allowed to have a velocity (assertion in * bullet), so we have to do our own velocity handling here. This * function returns the velocity of this object. */ - virtual const btVector3 &getVelocity() const {return m_initial_velocity;} + virtual const btVector3 &getVelocity() const OVERRIDE + { return m_initial_velocity; } // ------------------------------------------------------------------------ /** Kinematic objects are not allowed to have a velocity (assertion in * bullet), so we have to do our own velocity handling here. This * function sets the velocity of this object. * \param v Linear velocity of this object. */ - virtual void setVelocity(const btVector3& v) {m_initial_velocity=v; } + virtual void setVelocity(const btVector3& v) OVERRIDE + { m_initial_velocity = v; } + }; // Cake #endif diff --git a/src/items/flyable.cpp b/src/items/flyable.cpp index 63e2d2127..1037fd974 100644 --- a/src/items/flyable.cpp +++ b/src/items/flyable.cpp @@ -37,6 +37,11 @@ #include "karts/explosion_animation.hpp" #include "modes/linear_world.hpp" #include "modes/soccer_world.hpp" +#include "network/compress_network_body.hpp" +#include "network/network_config.hpp" +#include "network/network_string.hpp" +#include "network/rewind_info.hpp" +#include "network/rewind_manager.hpp" #include "physics/physics.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" @@ -75,6 +80,10 @@ Flyable::Flyable(AbstractKart *kart, PowerupManager::PowerupType type, m_owner_has_temporary_immunity = true; m_do_terrain_info = true; m_max_lifespan = -1; + m_undo_creation = false; + m_has_undone_destruction = false; + m_has_server_state = false; + m_check_created_ticks = -1; // Add the graphical model #ifndef SERVER_ONLY @@ -160,6 +169,10 @@ void Flyable::createPhysics(float forw_offset, const Vec3 &velocity, m_body->setCollisionFlags(m_body->getCollisionFlags() | btCollisionObject::CF_NO_CONTACT_RESPONSE); + m_saved_transform = getTrans(); + m_saved_lv = m_body->getLinearVelocity(); + m_saved_av = m_body->getAngularVelocity(); + m_saved_gravity = gravity; } // createPhysics // ----------------------------------------------------------------------------- @@ -378,7 +391,7 @@ void Flyable::setAnimation(AbstractKartAnimation *animation) */ void Flyable::updateGraphics(float dt) { - updateSmoothedGraphics(dt); + Moveable::updateSmoothedGraphics(dt); Moveable::updateGraphics(); } // updateGraphics @@ -390,6 +403,9 @@ void Flyable::updateGraphics(float dt) */ bool Flyable::updateAndDelete(int ticks) { + if (m_undo_creation) + return false; + if (hasAnimation()) { m_animation->update(stk_config->ticks2Time(ticks)); @@ -502,6 +518,8 @@ bool Flyable::isOwnerImmunity(const AbstractKart* kart_hit) const */ bool Flyable::hit(AbstractKart *kart_hit, PhysicalObject* object) { + if (m_undo_creation) + return false; // the owner of this flyable should not be hit by his own flyable if(isOwnerImmunity(kart_hit)) return false; m_has_hit_something=true; @@ -573,5 +591,140 @@ HitEffect* Flyable::getHitEffect() const unsigned int Flyable::getOwnerId() { return m_owner->getWorldKartId(); -} +} // getOwnerId + +// ---------------------------------------------------------------------------- +BareNetworkString* Flyable::saveState(std::vector<std::string>* ru) +{ + ru->push_back(getUniqueIdentity()); + BareNetworkString *buffer = new BareNetworkString(); + CompressNetworkBody::compress(m_body->getWorldTransform(), + m_body->getLinearVelocity(), m_body->getAngularVelocity(), buffer); + uint16_t hit_and_ticks = (m_has_hit_something ? 1 << 15 : 0) | + m_ticks_since_thrown; + buffer->addUInt16(hit_and_ticks); + return buffer; +} // saveState + +// ---------------------------------------------------------------------------- +void Flyable::restoreState(BareNetworkString *buffer, int count) +{ + btTransform t; + Vec3 lv, av; + CompressNetworkBody::decompress(buffer, &t, &lv, &av); + + m_body->setWorldTransform(t); + m_motion_state->setWorldTransform(t); + m_body->setInterpolationWorldTransform(t); + m_body->setLinearVelocity(lv); + m_body->setAngularVelocity(av); + m_body->setInterpolationLinearVelocity(lv); + m_body->setInterpolationAngularVelocity(av); + uint16_t hit_and_ticks = buffer->getUInt16(); + m_has_hit_something = (hit_and_ticks >> 15) == 1; + m_ticks_since_thrown = hit_and_ticks & ~(1 << 15); + if (!m_has_server_state) + m_has_server_state = true; +} // restoreState + +// ---------------------------------------------------------------------------- +void Flyable::addForRewind(const std::string& uid) +{ + SmoothNetworkBody::setEnable(true); + SmoothNetworkBody::setSmoothRotation(false); + SmoothNetworkBody::setAdjustVerticalOffset(false); + Rewinder::setUniqueIdentity(uid); + Rewinder::rewinderAdd(); +} // addForRewind + +// ---------------------------------------------------------------------------- +void Flyable::addRewindInfoEventFunctionAfterFiring() +{ + if (!NetworkConfig::get()->isNetworking() || + NetworkConfig::get()->isServer()) + return; + + std::shared_ptr<Flyable> f = getShared<Flyable>(); + RewindManager::get()->addRewindInfoEventFunction(new + RewindInfoEventFunction(World::getWorld()->getTicksSinceStart(), + /*undo_function*/[f]() + { + f->m_undo_creation = true; + const Vec3 *min, *max; + Track::getCurrentTrack()->getAABB(&min, &max); + btTransform t = f->m_body->getWorldTransform(); + // Move it to (almost infinity), avoiding affecting current + // rewinding + t.setOrigin(*max * 2.0f); + f->m_body->setWorldTransform(t); + f->m_motion_state->setWorldTransform(t); + f->m_body->setInterpolationWorldTransform(t); + f->m_body->setGravity(Vec3(0.0f)); + }, + /*replay_function*/[f]() + { + f->m_undo_creation = false; + f->m_body->setWorldTransform(f->m_saved_transform); + f->m_motion_state->setWorldTransform(f->m_saved_transform); + f->m_body->setInterpolationWorldTransform(f->m_saved_transform); + f->m_body->setLinearVelocity(f->m_saved_lv); + f->m_body->setAngularVelocity(f->m_saved_av); + f->m_body->setInterpolationLinearVelocity(f->m_saved_lv); + f->m_body->setInterpolationAngularVelocity(f->m_saved_av); + f->m_body->setGravity(f->m_saved_gravity); + f->m_ticks_since_thrown = 0; + f->m_has_hit_something = false; + f->additionalPhysicsProperties(); + }, + /*delete_function*/[f]() + { + f->m_check_created_ticks = World::getWorld()->getTicksSinceStart(); + })); +} // addRewindInfoEventFunctionAfterFiring + +// ---------------------------------------------------------------------------- +void Flyable::handleUndoDestruction() +{ + if (!NetworkConfig::get()->isNetworking() || + NetworkConfig::get()->isServer() || + m_has_undone_destruction) + return; + + m_has_undone_destruction = true; + + // If destroyed during rewind, than in theroy it should be safe to delete + // without undo + if (RewindManager::get()->isRewinding()) + return; + + // We don't bother seeing the mesh during rewinding + m_node->setVisible(false); + std::shared_ptr<Flyable> f = getShared<Flyable>(); + std::string uid = f->getUniqueIdentity(); + RewindManager::get()->addRewindInfoEventFunction(new + RewindInfoEventFunction(World::getWorld()->getTicksSinceStart(), + /*undo_function*/[f, uid]() + { + projectile_manager->addByUID(uid, f); + }, + /*replay_function*/[uid]() + { + projectile_manager->removeByUID(uid); + })); +} // handleUndoDestruction + +// ---------------------------------------------------------------------------- +void Flyable::computeError() +{ + Moveable::checkSmoothing(); + if (!m_has_server_state && m_check_created_ticks != -1 && + World::getWorld()->getTicksSinceStart() > m_check_created_ticks) + { + const std::string& uid = getUniqueIdentity(); + Log::warn("Flyable", "Item %s failed to be created on server, " + "remove it locally", uid.c_str()); + projectile_manager->removeByUID(uid); + } +} // computeError + /* EOF */ diff --git a/src/items/flyable.hpp b/src/items/flyable.hpp index 8eba2bc6e..7d19159ba 100644 --- a/src/items/flyable.hpp +++ b/src/items/flyable.hpp @@ -24,6 +24,7 @@ #include "items/powerup_manager.hpp" #include "karts/moveable.hpp" +#include "network/rewinder.hpp" #include "tracks/terrain_info.hpp" #include "utils/cpp2011.hpp" @@ -43,7 +44,8 @@ class XMLNode; /** * \ingroup items */ -class Flyable : public Moveable, public TerrainInfo +class Flyable : public Moveable, public TerrainInfo, + public Rewinder { public: private: @@ -103,6 +105,11 @@ protected: /** Size of this flyable. */ Vec3 m_extend; + bool m_undo_creation; + bool m_has_undone_destruction; + bool m_has_server_state; + int m_check_created_ticks; + // The flyable class stores the values for each flyable type, e.g. // speed, min_height, max_height. These variables must be static, // so we need arrays of these variables to have different values @@ -128,11 +135,11 @@ protected: /** Time since thrown. used so a kart can't hit himself when trying * something, and also to put some time limit to some collectibles */ - int m_ticks_since_thrown; + int16_t m_ticks_since_thrown; /** Set to something > -1 if this flyable should auto-destrcut after * that may ticks. */ - int m_max_lifespan; + int m_max_lifespan; /** If set to true, the kart that throwns this flyable can't collide * with it for a short time. */ @@ -160,6 +167,13 @@ protected: const bool rotates=false, const bool turn_around=false, const btTransform* customDirection=NULL); + + /** Used when undoing creation or destruction. */ + btTransform m_saved_transform; + Vec3 m_saved_lv, m_saved_av, m_saved_gravity; + + virtual void additionalPhysicsProperties() {} + public: Flyable (AbstractKart* kart, @@ -217,6 +231,31 @@ public: /** Returns the size (extend) of the mesh. */ const Vec3 &getExtend() const { return m_extend; } // ------------------------------------------------------------------------ + void addForRewind(const std::string& uid); + // ------------------------------------------------------------------------ + virtual void undoEvent(BareNetworkString *buffer) OVERRIDE {} + // ------------------------------------------------------------------------ + virtual void rewindToEvent(BareNetworkString *buffer) OVERRIDE {} + // ------------------------------------------------------------------------ + virtual void undoState(BareNetworkString *buffer) OVERRIDE {} + // ------------------------------------------------------------------------ + virtual void saveTransform() OVERRIDE { Moveable::prepareSmoothing(); } + // ------------------------------------------------------------------------ + virtual void computeError() OVERRIDE; + // ------------------------------------------------------------------------ + virtual BareNetworkString* saveState(std::vector<std::string>* ru) + OVERRIDE; + // ------------------------------------------------------------------------ + virtual void restoreState(BareNetworkString *buffer, int count) OVERRIDE; + // ------------------------------------------------------------------------ + virtual void addRewindInfoEventFunctionAfterFiring(); + // ------------------------------------------------------------------------ + bool isUndoCreation() const { return m_undo_creation; } + // ------------------------------------------------------------------------ + bool hasUndoneDestruction() const { return m_has_undone_destruction; } + // ------------------------------------------------------------------------ + void handleUndoDestruction(); + }; // Flyable #endif diff --git a/src/items/item_manager.cpp b/src/items/item_manager.cpp index 4fcf76c8e..49842321a 100644 --- a/src/items/item_manager.cpp +++ b/src/items/item_manager.cpp @@ -43,19 +43,20 @@ #include <string> -std::vector<scene::IMesh *> ItemManager::m_item_mesh; -std::vector<scene::IMesh *> ItemManager::m_item_lowres_mesh; -std::vector<video::SColorf> ItemManager::m_glow_color; -bool ItemManager::m_disable_item_collection = false; -ItemManager * ItemManager::m_item_manager = NULL; -std::mt19937 ItemManager::m_random_engine; +std::vector<scene::IMesh *> ItemManager::m_item_mesh; +std::vector<scene::IMesh *> ItemManager::m_item_lowres_mesh; +std::vector<video::SColorf> ItemManager::m_glow_color; +bool ItemManager::m_disable_item_collection = false; +std::shared_ptr<ItemManager> ItemManager::m_item_manager; +std::mt19937 ItemManager::m_random_engine; //----------------------------------------------------------------------------- /** Creates one instance of the item manager. */ void ItemManager::create() { assert(!m_item_manager); - m_item_manager = new ItemManager(); + // Due to protected constructor use new instead of make_shared + m_item_manager = std::shared_ptr<ItemManager>(new ItemManager()); } // create //----------------------------------------------------------------------------- @@ -63,8 +64,7 @@ void ItemManager::create() void ItemManager::destroy() { assert(m_item_manager); - delete m_item_manager; - m_item_manager = NULL; + m_item_manager = nullptr; } // destroy //----------------------------------------------------------------------------- diff --git a/src/items/item_manager.hpp b/src/items/item_manager.hpp index 53924b5bc..83f5f9a9e 100644 --- a/src/items/item_manager.hpp +++ b/src/items/item_manager.hpp @@ -60,7 +60,7 @@ private: static std::mt19937 m_random_engine; protected: /** The instance of ItemManager while a race is on. */ - static ItemManager *m_item_manager; + static std::shared_ptr<ItemManager> m_item_manager; public: static void loadDefaultItemMeshes(); static void removeTextures(); @@ -90,9 +90,10 @@ public: // ------------------------------------------------------------------------ /** Return an instance of the item manager (it does not automatically * create one, call create for that). */ - static ItemManager *get() { + static ItemManager *get() + { assert(m_item_manager); - return m_item_manager; + return m_item_manager.get(); } // get // ======================================================================== @@ -119,9 +120,9 @@ protected: virtual unsigned int insertItem(Item *item); void setSwitchItems(const std::vector<int> &switch_items); ItemManager(); +public: virtual ~ItemManager(); -public: virtual Item* placeItem (ItemState::ItemType type, const Vec3& xyz, const Vec3 &normal); virtual Item* dropNewItem (ItemState::ItemType type, diff --git a/src/items/network_item_manager.cpp b/src/items/network_item_manager.cpp index f07235b28..b1070f228 100644 --- a/src/items/network_item_manager.cpp +++ b/src/items/network_item_manager.cpp @@ -31,16 +31,16 @@ void NetworkItemManager::create() { assert(!m_item_manager); - m_item_manager = new NetworkItemManager(); + auto nim = std::shared_ptr<NetworkItemManager>(new NetworkItemManager()); + nim->rewinderAdd(); + m_item_manager = nim; } // create - // ============================================================================ /** Creates a new instance of the item manager. This is done at startup * of each race. */ NetworkItemManager::NetworkItemManager() - : Rewinder("N", /*can be deleted*/false), - ItemManager() + : Rewinder("N"), ItemManager() { m_last_confirmed_item_ticks.clear(); diff --git a/src/items/network_item_manager.hpp b/src/items/network_item_manager.hpp index cfeec8e3d..1e1d93ff9 100644 --- a/src/items/network_item_manager.hpp +++ b/src/items/network_item_manager.hpp @@ -61,10 +61,10 @@ private: void forwardTime(int ticks); NetworkItemManager(); - virtual ~NetworkItemManager(); public: static void create(); + virtual ~NetworkItemManager(); void setSwitchItems(const std::vector<int> &switch_items); void sendItemUpdate(); diff --git a/src/items/plunger.cpp b/src/items/plunger.cpp index 65501a6d8..9cbafa6c3 100644 --- a/src/items/plunger.cpp +++ b/src/items/plunger.cpp @@ -100,7 +100,7 @@ Plunger::Plunger(AbstractKart *kart) { m_rubber_band = new RubberBand(this, kart); } - m_keep_alive = -1; + additionalPhysicsProperties(); } // Plunger // ---------------------------------------------------------------------------- diff --git a/src/items/plunger.hpp b/src/items/plunger.hpp index 91283a3c2..a74e9ed09 100644 --- a/src/items/plunger.hpp +++ b/src/items/plunger.hpp @@ -47,13 +47,17 @@ private: btVector3 m_initial_velocity; bool m_reverse_mode; + + virtual void additionalPhysicsProperties() OVERRIDE { m_keep_alive = -1; } + public: Plunger(AbstractKart *kart); ~Plunger(); static void init(const XMLNode &node, scene::IMesh* missile); - virtual bool updateAndDelete(int ticks); - virtual void hitTrack (); - virtual bool hit (AbstractKart *kart, PhysicalObject *obj=NULL); + virtual bool updateAndDelete(int ticks) OVERRIDE; + virtual void hitTrack () OVERRIDE; + virtual bool hit (AbstractKart *kart, PhysicalObject *obj=NULL) + OVERRIDE; // ------------------------------------------------------------------------ /** Sets the keep-alive value. Setting it to 0 will remove the plunger @@ -62,7 +66,7 @@ public: void setKeepAlive(int ticks) {m_keep_alive = ticks;} // ------------------------------------------------------------------------ /** No hit effect when it ends. */ - virtual HitEffect *getHitEffect() const {return NULL; } + virtual HitEffect *getHitEffect() const OVERRIDE { return NULL; } // ------------------------------------------------------------------------ }; // Plunger diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index fc3f0895c..70f08dff3 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -32,6 +32,7 @@ #include "karts/controller/controller.hpp" #include "karts/kart_properties.hpp" #include "modes/world.hpp" +#include "network/rewind_manager.hpp" #include "physics/triangle_mesh.hpp" #include "tracks/track.hpp" #include "utils/string_utils.hpp" @@ -211,6 +212,13 @@ void Powerup::adjustSound() } } // adjustSound +//----------------------------------------------------------------------------- +void Powerup::playSound() +{ + if (!RewindManager::get()->isRewinding()) + m_sound_use->play(); +} // playSound + //----------------------------------------------------------------------------- /** Use (fire) this powerup. */ @@ -248,7 +256,7 @@ void Powerup::use() { ItemManager::get()->switchItems(); m_sound_use->setPosition(m_kart->getXYZ()); - m_sound_use->play(); + playSound(); break; } case PowerupManager::POWERUP_CAKE: @@ -258,7 +266,7 @@ void Powerup::use() if(stk_config->m_shield_restrict_weapons) m_kart->setShieldTime(0.0f); // make weapon usage destroy the shield Powerup::adjustSound(); - m_sound_use->play(); + playSound(); projectile_manager->newProjectile(m_kart, m_type); break ; @@ -280,7 +288,7 @@ void Powerup::use() if(!new_item) return; Powerup::adjustSound(); - m_sound_use->play(); + playSound(); } else // if the kart is looking forward, use the bubblegum as a shield { @@ -330,7 +338,7 @@ void Powerup::use() //In this case this is a workaround, since the bubblegum item has two different sounds. Powerup::adjustSound(); - m_sound_use->play(); + playSound(); } // end of PowerupManager::POWERUP_BUBBLEGUM break; @@ -359,7 +367,7 @@ void Powerup::use() else m_sound_use->setPosition(m_kart->getXYZ()); - m_sound_use->play(); + playSound(); break; } } @@ -418,7 +426,7 @@ void Powerup::use() m_sound_use->setPosition(m_kart->getXYZ()); else if(player_kart) m_sound_use->setPosition(player_kart->getXYZ()); - m_sound_use->play(); + playSound(); } break; diff --git a/src/items/powerup.hpp b/src/items/powerup.hpp index 9077495da..4e4965f80 100644 --- a/src/items/powerup.hpp +++ b/src/items/powerup.hpp @@ -36,9 +36,6 @@ class SFXBase; class Powerup : public NoCopy { private: - /** A synchronised random number generator for network games. */ - RandomGenerator m_random; - /** Sound effect that is being played. */ SFXBase *m_sound_use; @@ -51,6 +48,7 @@ private: /** The owner (kart) of this powerup. */ AbstractKart* m_kart; + void playSound(); public: Powerup (AbstractKart* kart_); ~Powerup (); diff --git a/src/items/powerup_manager.cpp b/src/items/powerup_manager.cpp index d2f1d3b53..07aeb0f15 100644 --- a/src/items/powerup_manager.cpp +++ b/src/items/powerup_manager.cpp @@ -298,7 +298,7 @@ void PowerupManager::WeightsData::convertRankToSection(int rank, int *prev, } // The last kart always uses the data for the last section - if (rank == m_num_karts) + if (rank == (int)m_num_karts) { *prev = *next = m_weights_for_section.size() - 1; *weight = 1.0f; @@ -600,9 +600,11 @@ void PowerupManager::unitTesting() int num_weights = wd.m_summed_weights_for_rank[0].back(); for(int i=0; i<num_weights; i++) { +#ifdef DEBUG unsigned int n; assert( powerup_manager->getRandomPowerup(1, &n, i)==POWERUP_BOWLING ); assert(n==3); +#endif } // Test 2: Test all possible random numbers for 5 karts and rank 5 diff --git a/src/items/powerup_manager.hpp b/src/items/powerup_manager.hpp index f68b68dc2..bb2ef0cf1 100644 --- a/src/items/powerup_manager.hpp +++ b/src/items/powerup_manager.hpp @@ -139,7 +139,6 @@ public: }; private: - const int RAND_CLASS_RANGE = 1000; /** The icon for each powerup. */ Material* m_all_icons [POWERUP_MAX]; diff --git a/src/items/projectile_manager.cpp b/src/items/projectile_manager.cpp index 907c77ca4..b72724cd2 100644 --- a/src/items/projectile_manager.cpp +++ b/src/items/projectile_manager.cpp @@ -27,6 +27,8 @@ #include "items/powerup.hpp" #include "items/rubber_ball.hpp" #include "karts/abstract_kart.hpp" +#include "modes/world.hpp" +#include "network/rewind_manager.hpp" ProjectileManager *projectile_manager=0; @@ -45,12 +47,6 @@ void ProjectileManager::removeTextures() //----------------------------------------------------------------------------- void ProjectileManager::cleanup() { - for(Projectiles::iterator i = m_active_projectiles.begin(); - i != m_active_projectiles.end(); ++i) - { - delete *i; - } - m_active_projectiles.clear(); for(HitEffects::iterator i = m_active_hit_effects.begin(); i != m_active_hit_effects.end(); ++i) @@ -68,12 +64,8 @@ void ProjectileManager::cleanup() */ void ProjectileManager::updateGraphics(float dt) { - for (auto p = m_active_projectiles.begin(); - p != m_active_projectiles.end(); ++p) - { - (*p)->updateGraphics(dt); - } - + for (auto& p : m_active_projectiles) + p.second->updateGraphics(dt); } // updateGraphics // ----------------------------------------------------------------------------- @@ -82,6 +74,8 @@ void ProjectileManager::update(int ticks) { updateServer(ticks); + if (RewindManager::get()->isRewinding()) + return; HitEffects::iterator he = m_active_hit_effects.begin(); while(he!=m_active_hit_effects.end()) { @@ -107,24 +101,30 @@ void ProjectileManager::update(int ticks) /** Updates all rockets on the server (or no networking). */ void ProjectileManager::updateServer(int ticks) { - Projectiles::iterator p = m_active_projectiles.begin(); - while(p!=m_active_projectiles.end()) + auto p = m_active_projectiles.begin(); + while (p != m_active_projectiles.end()) { - bool can_be_deleted = (*p)->updateAndDelete(ticks); - if(can_be_deleted) + if (p->second->isUndoCreation()) { - HitEffect *he = (*p)->getHitEffect(); - if(he) - addHitEffect(he); - Flyable *f=*p; - Projectiles::iterator p_next=m_active_projectiles.erase(p); - delete f; - p=p_next; + p++; + continue; + } + bool can_be_deleted = p->second->updateAndDelete(ticks); + if (can_be_deleted) + { + if (!p->second->hasUndoneDestruction()) + { + HitEffect *he = p->second->getHitEffect(); + if (he) + addHitEffect(he); + } + p->second->handleUndoDestruction(); + p = m_active_projectiles.erase(p); } else p++; } // while p!=m_active_projectiles.end() - + } // updateServer // ----------------------------------------------------------------------------- @@ -132,20 +132,40 @@ void ProjectileManager::updateServer(int ticks) * \param kart The kart which shoots the projectile. * \param type Type of projectile. */ -Flyable *ProjectileManager::newProjectile(AbstractKart *kart, - PowerupManager::PowerupType type) +std::shared_ptr<Flyable> + ProjectileManager::newProjectile(AbstractKart *kart, + PowerupManager::PowerupType type) { - Flyable *f; + const std::string& uid = getUniqueIdentity(kart, type); + auto it = m_active_projectiles.find(uid); + // Flyable already created during rewind + if (it != m_active_projectiles.end()) + return it->second; + + std::shared_ptr<Flyable> f; switch(type) { - case PowerupManager::POWERUP_BOWLING: f = new Bowling(kart); break; - case PowerupManager::POWERUP_PLUNGER: f = new Plunger(kart); break; - case PowerupManager::POWERUP_CAKE: f = new Cake(kart); break; - case PowerupManager::POWERUP_RUBBERBALL: f = new RubberBall(kart); - break; - default: return NULL; + case PowerupManager::POWERUP_BOWLING: + f = std::make_shared<Bowling>(kart); + break; + case PowerupManager::POWERUP_PLUNGER: + f = std::make_shared<Plunger>(kart); + break; + case PowerupManager::POWERUP_CAKE: + f = std::make_shared<Cake>(kart); + break; + case PowerupManager::POWERUP_RUBBERBALL: + f = std::make_shared<RubberBall>(kart); + break; + default: + return nullptr; + } + m_active_projectiles[uid] = f; + if (RewindManager::get()->isEnabled()) + { + f->addForRewind(uid); + f->addRewindInfoEventFunctionAfterFiring(); } - m_active_projectiles.push_back(f); return f; } // newProjectile @@ -158,13 +178,15 @@ Flyable *ProjectileManager::newProjectile(AbstractKart *kart, bool ProjectileManager::projectileIsClose(const AbstractKart * const kart, float radius) { - float r2 = radius*radius; - - for(Projectiles::iterator i = m_active_projectiles.begin(); - i != m_active_projectiles.end(); i++) + float r2 = radius * radius; + for (auto i = m_active_projectiles.begin(); i != m_active_projectiles.end(); + i++) { - float dist2 = (*i)->getXYZ().distance2(kart->getXYZ()); - if(dist2<r2) return true; + if (i->second->isUndoCreation()) + continue; + float dist2 = i->second->getXYZ().distance2(kart->getXYZ()); + if (dist2 < r2) + return true; } return false; } // projectileIsClose @@ -179,21 +201,101 @@ bool ProjectileManager::projectileIsClose(const AbstractKart * const kart, int ProjectileManager::getNearbyProjectileCount(const AbstractKart * const kart, float radius, PowerupManager::PowerupType type) { - float r2 = radius*radius; - int projectileCount = 0; - - for(Projectiles::iterator i = m_active_projectiles.begin(); - i != m_active_projectiles.end(); i++) + float r2 = radius * radius; + int projectile_count = 0; + for (auto i = m_active_projectiles.begin(); i != m_active_projectiles.end(); + i++) { - if ((*i)->getType() == type) + if (i->second->isUndoCreation()) + continue; + if (i->second->getType() == type) { - float dist2 = (*i)->getXYZ().distance2(kart->getXYZ()); - if(dist2<r2) + float dist2 = i->second->getXYZ().distance2(kart->getXYZ()); + if (dist2 < r2) { - - projectileCount++; + projectile_count++; } } } - return projectileCount; + return projectile_count; } // getNearbyProjectileCount + +// ----------------------------------------------------------------------------- +std::string ProjectileManager::getUniqueIdentity(AbstractKart* kart, + PowerupManager::PowerupType t) +{ + switch (t) + { + case PowerupManager::POWERUP_BOWLING: + return std::string("B_") + + StringUtils::toString(kart->getWorldKartId()) + "_" + + StringUtils::toString(World::getWorld()->getTicksSinceStart()); + case PowerupManager::POWERUP_PLUNGER: + return std::string("P_") + + StringUtils::toString(kart->getWorldKartId()) + "_" + + StringUtils::toString(World::getWorld()->getTicksSinceStart()); + case PowerupManager::POWERUP_CAKE: + return std::string("C_") + + StringUtils::toString(kart->getWorldKartId()) + "_" + + StringUtils::toString(World::getWorld()->getTicksSinceStart()); + case PowerupManager::POWERUP_RUBBERBALL: + return std::string("R_") + + StringUtils::toString(kart->getWorldKartId()) + "_" + + StringUtils::toString(World::getWorld()->getTicksSinceStart()); + default: + assert(false); + return ""; + } +} // getUniqueIdentity + +// ----------------------------------------------------------------------------- +std::shared_ptr<Rewinder> + ProjectileManager::addRewinderFromNetworkState(const std::string& uid) +{ + std::vector<std::string> id = StringUtils::split(uid, '_'); + if (id.size() != 3) + return nullptr; + if (!(id[0] == "B" || id[0] == "P" || id[0] == "C" || id[0] == "R")) + return nullptr; + int world_id = -1; + if (!StringUtils::fromString(id[1], world_id)) + return nullptr; + AbstractKart* kart = World::getWorld()->getKart(world_id); + char first_id = id[0][0]; + Log::debug("ProjectileManager", "Missed a firing event or locally deleted," + " add the flyable %s manually", uid.c_str()); + switch (first_id) + { + case 'B': + { + auto f = std::make_shared<Bowling>(kart); + f->addForRewind(uid); + m_active_projectiles[uid] = f; + return f; + } + case 'P': + { + auto f = std::make_shared<Plunger>(kart); + f->addForRewind(uid); + m_active_projectiles[uid] = f; + return f; + } + case 'C': + { + auto f = std::make_shared<Cake>(kart); + f->addForRewind(uid); + m_active_projectiles[uid] = f; + return f; + } + case 'R': + { + auto f = std::make_shared<RubberBall>(kart); + f->addForRewind(uid); + m_active_projectiles[uid] = f; + return f; + } + default: + assert(false); + return nullptr; + } +} // addProjectileFromNetworkState diff --git a/src/items/projectile_manager.hpp b/src/items/projectile_manager.hpp index 9518ccec2..bfae1cdd6 100644 --- a/src/items/projectile_manager.hpp +++ b/src/items/projectile_manager.hpp @@ -19,6 +19,8 @@ #ifndef HEADER_PROJECTILEMANAGER_HPP #define HEADER_PROJECTILEMANAGER_HPP +#include <map> +#include <memory> #include <vector> namespace irr @@ -32,6 +34,7 @@ namespace irr class AbstractKart; class Flyable; class HitEffect; +class Rewinder; class Track; class Vec3; @@ -41,17 +44,18 @@ class Vec3; class ProjectileManager : public NoCopy { private: - typedef std::vector<Flyable*> Projectiles; typedef std::vector<HitEffect*> HitEffects; /** The list of all active projectiles, i.e. projectiles which are * currently moving on the track. */ - Projectiles m_active_projectiles; + std::map<std::string, std::shared_ptr<Flyable> > m_active_projectiles; /** All active hit effects, i.e. hit effects which are currently * being shown or have a sfx playing. */ HitEffects m_active_hit_effects; + std::string getUniqueIdentity(AbstractKart* kart, + PowerupManager::PowerupType type); void updateServer(int ticks); public: ProjectileManager() {} @@ -60,9 +64,6 @@ public: void cleanup (); void update (int ticks); void updateGraphics (float dt); - Flyable* newProjectile (AbstractKart *kart, - PowerupManager::PowerupType type); - void Deactivate (Flyable *p) {} void removeTextures (); bool projectileIsClose(const AbstractKart * const kart, float radius); @@ -74,6 +75,19 @@ public: * \param hit_effect The hit effect to be added. */ void addHitEffect(HitEffect *hit_effect) { m_active_hit_effects.push_back(hit_effect); } + // ------------------------------------------------------------------------ + std::shared_ptr<Rewinder> + addRewinderFromNetworkState(const std::string& uid); + // ------------------------------------------------------------------------ + std::shared_ptr<Flyable> newProjectile(AbstractKart *kart, + PowerupManager::PowerupType type); + // ------------------------------------------------------------------------ + void addByUID(const std::string& uid, std::shared_ptr<Flyable> f) + { m_active_projectiles[uid] = f; } + // ------------------------------------------------------------------------ + void removeByUID(const std::string& uid) + { m_active_projectiles.erase(uid); } + }; extern ProjectileManager *projectile_manager; diff --git a/src/karts/abstract_kart_animation.cpp b/src/karts/abstract_kart_animation.cpp index d4424a656..539ac7412 100644 --- a/src/karts/abstract_kart_animation.cpp +++ b/src/karts/abstract_kart_animation.cpp @@ -89,7 +89,7 @@ AbstractKartAnimation::~AbstractKartAnimation() Vec3 linear_velocity = kart->getBody()->getLinearVelocity(); Vec3 angular_velocity = kart->getBody()->getAngularVelocity(); btTransform transform = kart->getBody()->getWorldTransform(); - RewindManager::get()->getRewindQueue().insertRewindInfo(new + RewindManager::get()->addRewindInfoEventFunction(new RewindInfoEventFunction( World::getWorld()->getTicksSinceStart(), [kart]() diff --git a/src/karts/controller/controller.cpp b/src/karts/controller/controller.cpp index 26d8311d2..fe04be88e 100644 --- a/src/karts/controller/controller.cpp +++ b/src/karts/controller/controller.cpp @@ -35,7 +35,8 @@ Controller::Controller(AbstractKart *kart) setControllerName("Controller"); } // Controller +// ---------------------------------------------------------------------------- core::stringw Controller::getName() const { return translations->fribidize(m_kart->getName()); -} +} // getName diff --git a/src/karts/controller/network_player_controller.hpp b/src/karts/controller/network_player_controller.hpp old mode 100644 new mode 100755 index 0bff1464d..0aca2962c --- a/src/karts/controller/network_player_controller.hpp +++ b/src/karts/controller/network_player_controller.hpp @@ -18,6 +18,7 @@ #define HEADER_NETWORK_PLAYER_CONTROLLER_HPP #include "karts/controller/player_controller.hpp" +#include "network/network_config.hpp" class AbstractKart; class Player; @@ -42,6 +43,23 @@ public: return false; } // isLocal // ------------------------------------------------------------------------ + /** Update for network controller. For player with a high ping it is + * useful to reduce shaking by reducing the steering somewhat in each + * frame: If the player does a quick correction only, because of the high + * latency the predicted path will curve way too much. By automatically + * reducing it, this error is reduced. And even if the player steers more + * the error is hopefully acceptable. */ + virtual void update(int ticks) + { + PlayerController::update(ticks); + if (NetworkConfig::get()->isClient()) + { + m_controls->setSteer( m_controls->getSteer() + * stk_config->m_network_steering_reduction); + } + } // update + + // ------------------------------------------------------------------------ }; // NetworkPlayerController #endif // NETWORK_PLAYER_CONTROLLER_HPP diff --git a/src/karts/ghost_kart.cpp b/src/karts/ghost_kart.cpp index 6c7c0c343..058991b7e 100644 --- a/src/karts/ghost_kart.cpp +++ b/src/karts/ghost_kart.cpp @@ -47,6 +47,7 @@ void GhostKart::reset() Kart::reset(); // This will set the correct start position update(0); + updateGraphics(0); m_last_egg_idx = 0; } // reset diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index b23094a20..2563d96cf 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -91,6 +91,7 @@ #include <algorithm> // for min and max #include <iostream> +#include <limits> #include <cmath> @@ -129,7 +130,7 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id, m_bubblegum_ticks = 0; m_bubblegum_torque = 0.0f; m_invulnerable_ticks = 0; - m_squash_ticks = 0; + m_squash_time = std::numeric_limits<float>::max(); m_shadow = NULL; m_wheel_box = NULL; @@ -366,7 +367,7 @@ void Kart::reset() m_bubblegum_ticks = 0; m_bubblegum_torque = 0.0f; m_invulnerable_ticks = 0; - m_squash_ticks = 0; + m_squash_time = std::numeric_limits<float>::max(); m_node->setScale(core::vector3df(1.0f, 1.0f, 1.0f)); m_collected_energy = 0; m_has_started = false; @@ -858,6 +859,8 @@ void Kart::adjustSpeed(float f) */ void Kart::updateWeight() { + if (!m_body) + return; float mass = m_kart_properties->getMass() + m_attachment->weightAdjust(); if (m_weight != mass) { @@ -1280,34 +1283,12 @@ void Kart::update(int ticks) // Reset any instand speed increase in the bullet kart m_vehicle->setMinSpeed(0); - if(m_squash_ticks>=0) - { - m_squash_ticks-=ticks; - // If squasing time ends, reset the model - if(m_squash_ticks<=0) - { - m_node->setScale(core::vector3df(1.0f, 1.0f, 1.0f)); - scene::ISceneNode* node = - m_kart_model->getAnimatedNode() ? - m_kart_model->getAnimatedNode() : m_node; - if (m_vehicle->getNumWheels() > 0) - { - scene::ISceneNode **wheels = m_kart_model->getWheelNodes(); - for (int i = 0; i < 4 && i < m_vehicle->getNumWheels(); ++i) - { - if (wheels[i]) - wheels[i]->setParent(node); - } - } - } - } // if squashed - - if (m_bubblegum_ticks > 0.0f) + if (m_bubblegum_ticks > 0) { m_bubblegum_ticks -= ticks; - if (m_bubblegum_ticks <= 0.0f) + if (m_bubblegum_ticks <= 0) { - m_bubblegum_torque = 0.0f; + m_bubblegum_torque = 0; } } @@ -1757,23 +1738,42 @@ void Kart::setSquash(float time, float slowdown) ExplosionAnimation::create(this); return; } - m_node->setScale(core::vector3df(1.0f, 0.5f, 1.0f)); + m_max_speed->setSlowdown(MaxSpeed::MS_DECREASE_SQUASH, slowdown, stk_config->time2Ticks(0.1f), stk_config->time2Ticks(time)); - if (m_vehicle->getNumWheels() > 0) + +#ifndef SERVER_ONLY + if (m_squash_time == std::numeric_limits<float>::max()) { - if (!m_wheel_box) - m_wheel_box = irr_driver->getSceneManager()->addDummyTransformationSceneNode(m_node); - scene::ISceneNode **wheels = m_kart_model->getWheelNodes(); - for (int i = 0; i < 4 && i < m_vehicle->getNumWheels(); ++i) + m_node->setScale(core::vector3df(1.0f, 0.5f, 1.0f)); + if (m_vehicle->getNumWheels() > 0) { - if (wheels[i]) - wheels[i]->setParent(m_wheel_box); + if (!m_wheel_box) + { + m_wheel_box = irr_driver->getSceneManager() + ->addDummyTransformationSceneNode(m_node); + } + scene::ISceneNode **wheels = m_kart_model->getWheelNodes(); + for (int i = 0; i < 4 && i < m_vehicle->getNumWheels(); i++) + { + if (wheels[i]) + wheels[i]->setParent(m_wheel_box); + } + m_wheel_box->getRelativeTransformationMatrix() + .setScale(core::vector3df(1.0f, 2.0f, 1.0f)); } - m_wheel_box->getRelativeTransformationMatrix().setScale(core::vector3df(1.0f, 2.0f, 1.0f)); + m_squash_time = time; } - m_squash_ticks = stk_config->time2Ticks(time); +#endif +} // setSquash + +//----------------------------------------------------------------------------- +/** Returns if the kart is currently being squashed + */ +bool Kart::isSquashed() const +{ + return m_max_speed->isSpeedDecreaseActive(MaxSpeed::MS_DECREASE_SQUASH); } // setSquash //----------------------------------------------------------------------------- @@ -2946,6 +2946,30 @@ void Kart::updateGraphics(float dt) if (m_custom_sounds[n] != NULL) m_custom_sounds[n]->position(getXYZ()); } */ + + if (m_squash_time != std::numeric_limits<float>::max()) + { + m_squash_time -= dt; + // If squasing time ends, reset the model + if (m_squash_time <= 0.0f) + { + m_squash_time = std::numeric_limits<float>::max(); + m_node->setScale(core::vector3df(1.0f, 1.0f, 1.0f)); + scene::ISceneNode* node = + m_kart_model->getAnimatedNode() ? + m_kart_model->getAnimatedNode() : m_node; + if (m_vehicle->getNumWheels() > 0) + { + scene::ISceneNode **wheels = m_kart_model->getWheelNodes(); + for (int i = 0; i < 4 && i < m_vehicle->getNumWheels(); ++i) + { + if (wheels[i]) + wheels[i]->setParent(node); + } + } + } + } // if squashed + for (int i = 0; i < EMITTER_COUNT; i++) m_emitters[i]->setPosition(getXYZ()); m_skid_sound->setPosition(getXYZ()); diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 9e78fe8d0..1fb864f82 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -160,17 +160,17 @@ protected: int m_bounce_back_ticks; /** Time a kart is invulnerable. */ - int m_invulnerable_ticks; + int16_t m_invulnerable_ticks; /** How long a kart is being squashed. If this is >0 * the kart is squashed. */ - int m_squash_ticks; + float m_squash_time; /** Current leaning of the kart. */ float m_current_lean; /** If > 0 then bubble gum effect is on. This is the sliding when hitting a gum on the floor, not the shield. */ - int m_bubblegum_ticks; + int16_t m_bubblegum_ticks; /** The torque to apply after hitting a bubble gum. */ float m_bubblegum_torque; @@ -218,13 +218,10 @@ protected: /** When a kart has its view blocked by the plunger, this variable will be * > 0 the number it contains is the time left before removing plunger. */ - int m_view_blocked_by_plunger; + int16_t m_view_blocked_by_plunger; /** The current speed (i.e. length of velocity vector) of this kart. */ float m_speed; - /** For camera handling an exponentially smoothened value is used, which - * reduces stuttering of the camera. */ - float m_smoothed_speed; - + /** For smoothing engine sound**/ float m_last_factor_engine_sound; @@ -499,7 +496,7 @@ public: virtual bool isOnMinNitroTime() const OVERRIDE { return m_min_nitro_ticks > 0; } // ------------------------------------------------------------------------ /** Returns if the kart is currently being squashed. */ - virtual bool isSquashed() const OVERRIDE { return m_squash_ticks >0; } + virtual bool isSquashed() const OVERRIDE; // ------------------------------------------------------------------------ /** Shows the star effect for a certain time. */ virtual void showStarEffect(float t) OVERRIDE; diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index 83e8edf0c..c0dfc0491 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -38,8 +38,7 @@ KartRewinder::KartRewinder(const std::string& ident, const btTransform& init_transform, PerPlayerDifficulty difficulty, std::shared_ptr<RenderInfo> ri) - : Rewinder(std::string("K") + StringUtils::toString(world_kart_id), - /*can_be_destroyed*/ false) + : Rewinder(std::string("K") + StringUtils::toString(world_kart_id)) , Kart(ident, world_kart_id, position, init_transform, difficulty, ri) { @@ -144,6 +143,15 @@ BareNetworkString* KartRewinder::saveState(std::vector<std::string>* ru) // ----------- m_skidding->saveState(buffer); + // 6) Firing and related handling + // ----------- + buffer->addUInt16(m_bubblegum_ticks); + buffer->addUInt16(m_view_blocked_by_plunger); + // m_invulnerable_ticks will not be negative + uint16_t fire_and_invulnerable = (m_fire_clicked ? 1 << 15 : 0) | + m_invulnerable_ticks; + buffer->addUInt16(fire_and_invulnerable); + return buffer; } // saveState @@ -203,15 +211,21 @@ void KartRewinder::restoreState(BareNetworkString *buffer, int count) float nitro = buffer->getFloat(); setEnergy(nitro); - // 5) Max speed info + // 4) Max speed info // ------------------ m_max_speed->rewindTo(buffer); - // 6) Skidding + // 5) Skidding // ----------- m_skidding->rewindTo(buffer); - return; + // 6) Firing and related handling + // ----------- + m_bubblegum_ticks = buffer->getUInt16(); + m_view_blocked_by_plunger = buffer->getUInt16(); + uint16_t fire_and_invulnerable = buffer->getUInt16(); + m_fire_clicked = (fire_and_invulnerable >> 15) == 1; + m_invulnerable_ticks = fire_and_invulnerable & ~(1 << 15); } // restoreState // ---------------------------------------------------------------------------- @@ -223,25 +237,16 @@ void KartRewinder::update(int ticks) Kart::update(ticks); } // update -// ---------------------------------------------------------------------------- -void KartRewinder::rewindToEvent(BareNetworkString *buffer) -{ -} // rewindToEvent - // ---------------------------------------------------------------------------- std::function<void()> KartRewinder::getLocalStateRestoreFunction() { if (m_eliminated) return nullptr; - // In theory all ticks / boolean related stuff can be saved locally + // Variable can be saved locally if its adjustment only depends on the kart + // itself bool has_started = m_has_started; - int bubblegum_ticks = m_bubblegum_ticks; int bounce_back_ticks = m_bounce_back_ticks; - int invulnerable_ticks = m_invulnerable_ticks; - int squash_ticks = m_squash_ticks; - bool fire_clicked = m_fire_clicked; - int view_blocked_by_plunger = m_view_blocked_by_plunger; int brake_ticks = m_brake_ticks; int8_t min_nitro_ticks = m_min_nitro_ticks; @@ -259,18 +264,11 @@ std::function<void()> KartRewinder::getLocalStateRestoreFunction() steer_val_r = pc->m_steer_val_r; } - return [has_started, bubblegum_ticks, bounce_back_ticks, - invulnerable_ticks, squash_ticks, fire_clicked, - view_blocked_by_plunger, brake_ticks, min_nitro_ticks, initial_speed, - node_scale, steer_val_l, steer_val_r, this]() + return [has_started, bounce_back_ticks, brake_ticks, min_nitro_ticks, + initial_speed, node_scale, steer_val_l, steer_val_r, this]() { m_has_started = has_started; - m_bubblegum_ticks = bubblegum_ticks; m_bounce_back_ticks = bounce_back_ticks; - m_invulnerable_ticks = invulnerable_ticks; - m_squash_ticks = squash_ticks; - m_fire_clicked = fire_clicked; - m_view_blocked_by_plunger = view_blocked_by_plunger; m_brake_ticks = brake_ticks; m_min_nitro_ticks = min_nitro_ticks; getAttachment()->setInitialSpeed(initial_speed); diff --git a/src/karts/kart_rewinder.hpp b/src/karts/kart_rewinder.hpp index ebdd39b35..1c9beb254 100644 --- a/src/karts/kart_rewinder.hpp +++ b/src/karts/kart_rewinder.hpp @@ -48,7 +48,7 @@ public: OVERRIDE; void reset() OVERRIDE; virtual void restoreState(BareNetworkString *p, int count) OVERRIDE; - virtual void rewindToEvent(BareNetworkString *p) OVERRIDE; + virtual void rewindToEvent(BareNetworkString *p) OVERRIDE {} virtual void update(int ticks) OVERRIDE; // ------------------------------------------------------------------------- virtual float getSteerPercent() const OVERRIDE diff --git a/src/karts/max_speed.cpp b/src/karts/max_speed.cpp index ad707c74e..fde8c6121 100644 --- a/src/karts/max_speed.cpp +++ b/src/karts/max_speed.cpp @@ -280,6 +280,15 @@ int MaxSpeed::getSpeedIncreaseTicksLeft(unsigned int category) return m_speed_increase[category].getTimeLeft(); } // getSpeedIncreaseTimeLeft +// ---------------------------------------------------------------------------- +/** Returns if decreased speed is active in the given category. + * \param category Which category to report on. + */ +int MaxSpeed::isSpeedDecreaseActive(unsigned int category) +{ + return m_speed_decrease[category].isActive(); +} // isSpeedDecreaseActive + // ---------------------------------------------------------------------------- /** Updates all speed increase and decrease objects, and determines the * current maximum speed. Note that the function can be called with diff --git a/src/karts/max_speed.hpp b/src/karts/max_speed.hpp index f22c17890..d3ec211a8 100644 --- a/src/karts/max_speed.hpp +++ b/src/karts/max_speed.hpp @@ -158,11 +158,13 @@ private: // -------------------------------------------------------------------- /** Returns the current slowdown fracftion, taking a 'fade in' * into account. */ - float getSlowdownFraction() const {return m_current_fraction;} + float getSlowdownFraction() const { return m_current_fraction; } + // -------------------------------------------------------------------- + int getTimeLeft() const { return m_duration; } // -------------------------------------------------------------------- /** Returns if this speed decrease is active atm. A duration of * -1 indicates an ongoing effect. */ - bool isActive() const { return m_duration > 0 || m_duration <= -1.0f; } + bool isActive() const { return m_duration > 0 || m_duration <= -1; } }; // SpeedDecrease // ------------------------------------------------------------------------ @@ -188,6 +190,7 @@ public: void setSlowdown(unsigned int category, float max_speed_fraction, int fade_in_time, int duration=-1); int getSpeedIncreaseTicksLeft(unsigned int category); + int isSpeedDecreaseActive(unsigned int category); void update(int ticks); void reset(); void saveState(BareNetworkString *buffer) const; diff --git a/src/karts/moveable.cpp b/src/karts/moveable.cpp index de92b5d21..cf16491fe 100644 --- a/src/karts/moveable.cpp +++ b/src/karts/moveable.cpp @@ -62,7 +62,10 @@ void Moveable::setNode(scene::ISceneNode *n) //----------------------------------------------------------------------------- void Moveable::updateSmoothedGraphics(float dt) { - SmoothNetworkBody::updateSmoothedGraphics(m_transform, getVelocity(), dt); + Vec3 velocity; + if (m_body) + velocity = m_body->getLinearVelocity(); + SmoothNetworkBody::updateSmoothedGraphics(m_transform, velocity, dt); #undef DEBUG_SMOOTHING #ifdef DEBUG_SMOOTHING // Gnuplot compare command diff --git a/src/main.cpp b/src/main.cpp index 08cef4d72..0a303d139 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -603,11 +603,13 @@ void cmdLineHelp() " --disable-lan Disable LAN detection (connect using WAN).\n" " --auto-connect Automatically connect to fist server and start race\n" " --max-players=n Maximum number of clients (server only).\n" + " --min-players=n Minimum number of clients (server only).\n" " --motd Message showing in all lobby of clients, can specify a .txt file.\n" " --auto-end Automatically end network game after 1st player finished\n" " for some time (currently his finished time * 0.25 + 15.0). \n" " --team-choosing Allow choosing team in lobby, implicitly allowed in lan or\n" - " password protected server.\n" + " password protected server. This function cannot be used in\n" + " owner-less server.\n" " --soccer-timed Use time limit mode in network soccer game.\n" " --soccer-goals Use goals limit mode in network soccer game.\n" " --network-gp=n Specify number of tracks used in network grand prix.\n" @@ -1139,12 +1141,28 @@ int handleCmdLine() NetworkConfig::get()->setServerIdFile( file_manager->getUserConfigFile(s)); } - if(CommandLine::has("--disable-polling")) + if (CommandLine::has("--disable-polling")) + { Online::RequestManager::m_disable_polling = true; - if(CommandLine::has("--max-players", &n)) - UserConfigParams::m_server_max_players=n; - NetworkConfig::get()-> - setMaxPlayers(UserConfigParams::m_server_max_players); + } + if (CommandLine::has("--max-players", &n)) + { + UserConfigParams::m_server_max_players = n; + } + + if (UserConfigParams::m_server_max_players < 1) + { + UserConfigParams::m_server_max_players = 1; + } + NetworkConfig::get()->setMaxPlayers(UserConfigParams::m_server_max_players); + + if (CommandLine::has("--min-players", &n)) + { + float threshold = ((float)(n) - 0.5f) / + UserConfigParams::m_server_max_players; + threshold = std::max(std::min(threshold, 1.0f), 0.0f); + UserConfigParams::m_start_game_threshold = threshold; + } if (CommandLine::has("--port", &n)) { // We don't know if this instance is going to be a client @@ -1158,7 +1176,8 @@ int handleCmdLine() } if (CommandLine::has("--team-choosing")) { - NetworkConfig::get()->setTeamChoosing(true); + if (!NetworkConfig::get()->isOwnerLess()) + NetworkConfig::get()->setTeamChoosing(true); } if (CommandLine::has("--connect-now", &s)) { diff --git a/src/modes/cutscene_world.cpp b/src/modes/cutscene_world.cpp index e8a018b01..32a2e6de7 100644 --- a/src/modes/cutscene_world.cpp +++ b/src/modes/cutscene_world.cpp @@ -56,7 +56,8 @@ CutsceneWorld::CutsceneWorld() : World() { m_time_at_second_reset = 0.0f; m_aborted = false; - WorldStatus::setClockMode(CLOCK_NONE); + WorldStatus::setClockMode(CLOCK_CHRONO); + m_phase = RACE_PHASE; m_use_highscores = false; m_play_track_intro_sound = false; m_play_ready_set_go_sounds = false; @@ -170,7 +171,12 @@ void CutsceneWorld::init() CutsceneWorld::~CutsceneWorld() { } // ~CutsceneWorld - +//----------------------------------------------------------------------------- +void CutsceneWorld::reset() +{ + World::reset(); + m_phase = RACE_PHASE; +} //----------------------------------------------------------------------------- /** Returns the internal identifier for this race. */ @@ -231,7 +237,6 @@ void CutsceneWorld::update(int ticks) { // this way of calculating time and dt is more in line with what // irrlicht does and provides better synchronisation - double prev_time = m_time; double now = StkTime::getRealTime(); m_time = now - m_time_at_second_reset; } diff --git a/src/modes/cutscene_world.hpp b/src/modes/cutscene_world.hpp index 5b8fd84bc..cc30a7672 100644 --- a/src/modes/cutscene_world.hpp +++ b/src/modes/cutscene_world.hpp @@ -65,6 +65,8 @@ public: virtual void init() OVERRIDE; + virtual void reset() OVERRIDE; + // clock events virtual bool isRaceOver() OVERRIDE; diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index e03eab9f7..c5f7f7059 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -176,7 +176,7 @@ void LinearWorld::update(int ticks) for(unsigned int n=0; n<kart_amount; n++) { KartInfo& kart_info = m_kart_info[n]; - AbstractKart* kart = m_karts[n]; + AbstractKart* kart = m_karts[n].get(); // Nothing to do for karts that are currently being // rescued or eliminated @@ -218,7 +218,7 @@ void LinearWorld::update(int ticks) // Update the estimated finish time. // This is used by the AI m_kart_info[i].m_estimated_finish = - estimateFinishTimeForKart(m_karts[i]); + estimateFinishTimeForKart(m_karts[i].get()); } // If one player and a ghost, or two compared ghosts, // compute the live time difference @@ -328,7 +328,7 @@ void LinearWorld::updateLiveDifference() void LinearWorld::newLap(unsigned int kart_index) { KartInfo &kart_info = m_kart_info[kart_index]; - AbstractKart *kart = m_karts[kart_index]; + AbstractKart *kart = m_karts[kart_index].get(); // Reset reset-after-lap achievements PlayerProfile *p = PlayerManager::getCurrentPlayer(); @@ -556,7 +556,7 @@ void LinearWorld::getKartsDisplayInfo( for(unsigned int i = 0; i < kart_amount ; i++) { RaceGUIBase::KartIconDisplayInfo& rank_info = (*info)[i]; - AbstractKart* kart = m_karts[i]; + AbstractKart* kart = m_karts[i].get(); // reset color rank_info.m_color = video::SColor(255, 255, 255, 255); @@ -585,7 +585,7 @@ void LinearWorld::getKartsDisplayInfo( { RaceGUIBase::KartIconDisplayInfo& rank_info = (*info)[i]; KartInfo& kart_info = m_kart_info[i]; - AbstractKart* kart = m_karts[i]; + AbstractKart* kart = m_karts[i].get(); const int position = kart->getPosition(); @@ -799,7 +799,7 @@ void LinearWorld::updateRacePosition() // so that debug output is still correct!!!!!!!!!!! for (unsigned int i=0; i<kart_amount; i++) { - AbstractKart* kart = m_karts[i]; + AbstractKart* kart = m_karts[i].get(); // Karts that are either eliminated or have finished the // race already have their (final) position assigned. If // these karts would get their rank updated, it could happen @@ -898,7 +898,7 @@ void LinearWorld::updateRacePosition() Log::debug("[LinearWorld]", "Counting laps at %u seconds.", getTime()); for (unsigned int i=0; i<kart_amount; i++) { - AbstractKart* kart = m_karts[i]; + AbstractKart* kart = m_karts[i].get(); Log::debug("[LinearWorld]", "counting karts ahead of %s (laps %u," " progress %u, finished %d, eliminated %d, initial position %u.", kart->getIdent().c_str(), @@ -973,7 +973,7 @@ void LinearWorld::checkForWrongDirection(unsigned int i, float dt) KartInfo &ki = m_kart_info[i]; - const AbstractKart *kart=m_karts[i]; + const AbstractKart *kart=m_karts[i].get(); // If the kart can go in more than one directions from the current track // don't do any reverse message handling, since it is likely that there // will be one direction in which it isn't going backwards anyway. diff --git a/src/modes/profile_world.cpp b/src/modes/profile_world.cpp index 6f735b92e..147577aa0 100644 --- a/src/modes/profile_world.cpp +++ b/src/modes/profile_world.cpp @@ -103,19 +103,18 @@ void ProfileWorld::setProfileModeLaps(int laps) * this player globally (i.e. including network players). * \param init_pos The start XYZ coordinates. */ -AbstractKart *ProfileWorld::createKart(const std::string &kart_ident, int index, - int local_player_id, int global_player_id, - RaceManager::KartType type, - PerPlayerDifficulty difficulty) +std::shared_ptr<AbstractKart> ProfileWorld::createKart + (const std::string &kart_ident, int index, int local_player_id, + int global_player_id, RaceManager::KartType kart_type, + PerPlayerDifficulty difficulty) { btTransform init_pos = getStartTransform(index); - Kart *new_kart = new KartWithStats(kart_ident, - /*world kart id*/ index, - /*position*/ index+1, - init_pos, difficulty); + std::shared_ptr<KartWithStats> new_kart = + std::make_shared<KartWithStats>(kart_ident, /*world kart id*/ index, + /*position*/ index + 1, init_pos, difficulty); new_kart->init(RaceManager::KT_AI); - Controller *controller = loadAIController(new_kart); + Controller *controller = loadAIController(new_kart.get()); new_kart->setController(controller); // Create a camera for the last kart (since this way more of the @@ -123,7 +122,7 @@ AbstractKart *ProfileWorld::createKart(const std::string &kart_ident, int index, if (index == (int)race_manager->getNumberOfKarts()-1) { // The camera keeps track of all cameras and will free them - Camera::createCamera(new_kart, local_player_id); + Camera::createCamera(new_kart.get(), local_player_id); } return new_kart; } // createKart @@ -197,7 +196,7 @@ void ProfileWorld::enterRaceOverState() // ---------- update rank ------ if (m_karts[i]->hasFinishedRace() || m_karts[i]->isEliminated()) continue; - m_karts[i]->finishedRace(estimateFinishTimeForKart(m_karts[i])); + m_karts[i]->finishedRace(estimateFinishTimeForKart(m_karts[i].get())); } // Print framerate statistics @@ -229,9 +228,9 @@ void ProfileWorld::enterRaceOverState() std::set<std::string> all_groups; - for ( KartList::size_type i = 0; i < m_karts.size(); ++i) + for (KartList::size_type i = 0; i < m_karts.size(); ++i) { - KartWithStats* kart = dynamic_cast<KartWithStats*>(m_karts[i]); + auto kart = std::dynamic_pointer_cast<KartWithStats>(m_karts[i]); max_t = std::max(max_t, kart->getFinishTime()); min_t = std::min(min_t, kart->getFinishTime()); @@ -285,7 +284,7 @@ void ProfileWorld::enterRaceOverState() float av_time = 0.0f, energy = 0; for ( unsigned int i = 0; i < (unsigned int)m_karts.size(); ++i) { - KartWithStats* kart = dynamic_cast<KartWithStats*>(m_karts[i]); + auto kart = std::dynamic_pointer_cast<KartWithStats>(m_karts[i]); const std::string &name=kart->getController()->getControllerName(); if(name!=*it) continue; diff --git a/src/modes/profile_world.hpp b/src/modes/profile_world.hpp index c4c1e0e00..255e9a4ba 100644 --- a/src/modes/profile_world.hpp +++ b/src/modes/profile_world.hpp @@ -72,10 +72,10 @@ protected: * used by DemoWorld. */ static int m_num_laps; - virtual AbstractKart *createKart(const std::string &kart_ident, int index, - int local_player_id, int global_player_id, - RaceManager::KartType type, - PerPlayerDifficulty difficulty); + virtual std::shared_ptr<AbstractKart> createKart + (const std::string &kart_ident, int index, int local_player_id, + int global_player_id, RaceManager::KartType type, + PerPlayerDifficulty difficulty); public: ProfileWorld(); diff --git a/src/modes/soccer_world.cpp b/src/modes/soccer_world.cpp index b1e11448b..fbb4db5e7 100644 --- a/src/modes/soccer_world.cpp +++ b/src/modes/soccer_world.cpp @@ -38,6 +38,8 @@ #include "network/stk_host.hpp" #include "physics/physics.hpp" #include "states_screens/race_gui_base.hpp" +#include "tracks/graph.hpp" +#include "tracks/quad.hpp" #include "tracks/track.hpp" #include "tracks/track_object_manager.hpp" #include "tracks/track_sector.hpp" @@ -120,6 +122,7 @@ void SoccerWorld::init() Log::fatal("SoccerWorld","Ball is missing in soccer field, abort."); m_bgd.init(m_ball->getPhysicalObject()->getRadius()); + m_ball_body->setActivationState(DISABLE_DEACTIVATION); } // init @@ -211,10 +214,10 @@ const std::string& SoccerWorld::getIdent() const void SoccerWorld::update(int ticks) { updateBallPosition(ticks); + updateSectorForKarts(); if (Track::getCurrentTrack()->hasNavMesh() && !NetworkConfig::get()->isNetworking()) { - updateSectorForKarts(); updateAIData(); } @@ -225,7 +228,7 @@ void SoccerWorld::update(int ticks) { for (unsigned int i = 0; i < m_karts.size(); i++) { - AbstractKart* kart = m_karts[i]; + auto& kart = m_karts[i]; if (kart->isEliminated()) continue; kart->getBody()->setLinearVelocity(Vec3(0.0f)); @@ -332,7 +335,7 @@ void SoccerWorld::onCheckGoalTriggered(bool first_goal) } for (unsigned i = 0; i < m_karts.size(); i++) { - AbstractKart* kart = m_karts[i]; + auto& kart = m_karts[i]; if (kart->isEliminated()) continue; kart->getBody()->setLinearVelocity(Vec3(0.0f)); @@ -353,7 +356,7 @@ void SoccerWorld::handleResetBallFromServer(const NetworkString& ns) "%d when reset player", ticks_back_to_own_goal, ticks_now); return; } - RewindManager::get()->getRewindQueue().insertRewindInfo(new + RewindManager::get()->addRewindInfoEventFunction(new RewindInfoEventFunction(ticks_back_to_own_goal, [](){}, std::bind(&SoccerWorld::resetKartsToSelfGoals, this))); @@ -403,7 +406,7 @@ void SoccerWorld::handlePlayerGoalFromServer(const NetworkString& ns) m_ball->setEnabled(false); for (unsigned i = 0; i < m_karts.size(); i++) { - AbstractKart* kart = m_karts[i]; + auto& kart = m_karts[i]; if (kart->isEliminated()) continue; btTransform transform_now = kart->getBody()->getWorldTransform(); @@ -413,7 +416,7 @@ void SoccerWorld::handlePlayerGoalFromServer(const NetworkString& ns) kart->setTrans(transform_now); m_goal_transforms[i] = transform_now; } - RewindManager::get()->getRewindQueue().insertRewindInfo(new + RewindManager::get()->addRewindInfoEventFunction(new RewindInfoEventFunction(ticks_back_to_own_goal, [](){}, std::bind(&SoccerWorld::resetKartsToSelfGoals, this))); @@ -428,7 +431,7 @@ void SoccerWorld::resetKartsToSelfGoals() setPhase(WorldStatus::RACE_PHASE); for (unsigned i = 0; i < m_karts.size(); i++) { - AbstractKart* kart = m_karts[i]; + auto& kart = m_karts[i]; if (kart->isEliminated()) continue; @@ -436,7 +439,7 @@ void SoccerWorld::resetKartsToSelfGoals() kart->getBody()->setAngularVelocity(Vec3(0.0f)); unsigned index = m_kart_position_map.at(kart->getWorldKartId()); btTransform t = Track::getCurrentTrack()->getStartTransform(index); - moveKartTo(kart, t); + moveKartTo(kart.get(), t); } } // resetKartsToSelfGoals @@ -529,10 +532,10 @@ bool SoccerWorld::getKartSoccerResult(unsigned int kart_id) const } // getKartSoccerResult //----------------------------------------------------------------------------- -AbstractKart *SoccerWorld::createKart(const std::string &kart_ident, int index, - int local_player_id, int global_player_id, - RaceManager::KartType kart_type, - PerPlayerDifficulty difficulty) +std::shared_ptr<AbstractKart> SoccerWorld::createKart + (const std::string &kart_ident, int index, int local_player_id, + int global_player_id, RaceManager::KartType kart_type, + PerPlayerDifficulty difficulty) { int cur_red = getTeamNum(SOCCER_TEAM_RED); int cur_blue = getTeamNum(SOCCER_TEAM_BLUE); @@ -587,16 +590,19 @@ AbstractKart *SoccerWorld::createKart(const std::string &kart_ident, int index, std::shared_ptr<RenderInfo> ri = std::make_shared<RenderInfo>(); ri = (team == SOCCER_TEAM_BLUE ? std::make_shared<RenderInfo>(0.66f) : std::make_shared<RenderInfo>(1.0f)); - AbstractKart* new_kart; + + std::shared_ptr<AbstractKart> new_kart; if (RewindManager::get()->isEnabled()) { - new_kart = new KartRewinder(kart_ident, index, position, init_pos, - difficulty, ri); + auto kr = std::make_shared<KartRewinder>(kart_ident, index, position, + init_pos, difficulty, ri); + kr->rewinderAdd(); + new_kart = kr; } else { - new_kart = new Kart(kart_ident, index, position, init_pos, difficulty, - ri); + new_kart = std::make_shared<Kart>(kart_ident, index, position, + init_pos, difficulty, ri); } new_kart->init(race_manager->getKartType(index)); @@ -605,18 +611,18 @@ AbstractKart *SoccerWorld::createKart(const std::string &kart_ident, int index, switch(kart_type) { case RaceManager::KT_PLAYER: - controller = new LocalPlayerController(new_kart, local_player_id, + controller = new LocalPlayerController(new_kart.get(), local_player_id, difficulty); m_num_players ++; break; case RaceManager::KT_NETWORK_PLAYER: - controller = new NetworkPlayerController(new_kart); + controller = new NetworkPlayerController(new_kart.get()); if (!online_name.empty()) new_kart->setOnScreenText(online_name.c_str()); m_num_players++; break; case RaceManager::KT_AI: - controller = loadAIController(new_kart); + controller = loadAIController(new_kart.get()); break; case RaceManager::KT_GHOST: break; @@ -808,13 +814,26 @@ int SoccerWorld::getTeamNum(SoccerTeam team) const //----------------------------------------------------------------------------- unsigned int SoccerWorld::getRescuePositionIndex(AbstractKart *kart) { - std::map<int, unsigned int>::const_iterator n = - m_kart_position_map.find(kart->getWorldKartId()); - - assert (n != m_kart_position_map.end()); - return n->second; + int last_valid_node = + getTrackSector(kart->getWorldKartId())->getLastValidGraphNode(); + if (last_valid_node >= 0) + return last_valid_node; + Log::warn("SoccerWorld", "Missing last valid node for rescuing"); + return 0; } // getRescuePositionIndex +//----------------------------------------------------------------------------- +btTransform SoccerWorld::getRescueTransform(unsigned int rescue_pos) const +{ + const Vec3 &xyz = Graph::get()->getQuad(rescue_pos)->getCenter(); + const Vec3 &normal = Graph::get()->getQuad(rescue_pos)->getNormal(); + btTransform pos; + pos.setOrigin(xyz); + btQuaternion q1 = shortestArcQuat(Vec3(0.0f, 1.0f, 0.0f), normal); + pos.setRotation(q1); + return pos; +} // getRescueTransform + //----------------------------------------------------------------------------- void SoccerWorld::enterRaceOverState() { diff --git a/src/modes/soccer_world.hpp b/src/modes/soccer_world.hpp index 3fe790aa5..190f76b3a 100644 --- a/src/modes/soccer_world.hpp +++ b/src/modes/soccer_world.hpp @@ -51,10 +51,10 @@ public: }; // ScorerData protected: - virtual AbstractKart *createKart(const std::string &kart_ident, int index, - int local_player_id, int global_player_id, - RaceManager::KartType type, - PerPlayerDifficulty difficulty) OVERRIDE; + virtual std::shared_ptr<AbstractKart> createKart + (const std::string &kart_ident, int index, int local_player_id, + int global_player_id, RaceManager::KartType type, + PerPlayerDifficulty difficulty) OVERRIDE; private: class KartDistanceMap @@ -329,7 +329,8 @@ public: virtual void reset() OVERRIDE; virtual unsigned int getRescuePositionIndex(AbstractKart *kart) OVERRIDE; - + virtual btTransform getRescueTransform(unsigned int rescue_pos) const + OVERRIDE; virtual bool useFastMusicNearEnd() const OVERRIDE { return false; } virtual void getKartsDisplayInfo( std::vector<RaceGUIBase::KartIconDisplayInfo> *info) OVERRIDE {} diff --git a/src/modes/standard_race.cpp b/src/modes/standard_race.cpp index e701c6e6b..b90152369 100644 --- a/src/modes/standard_race.cpp +++ b/src/modes/standard_race.cpp @@ -103,7 +103,7 @@ void StandardRace::endRaceEarly() for (unsigned int i = 1; i <= kart_amount; i++) { int kartid = m_position_index[i-1]; - AbstractKart* kart = m_karts[kartid]; + AbstractKart* kart = m_karts[kartid].get(); if (kart->hasFinishedRace()) { @@ -140,7 +140,7 @@ void StandardRace::endRaceEarly() int kartid = active_players[i]; int position = getNumKarts() - (int) active_players.size() + 1 + i; setKartPosition(kartid, position); - float punished_time = estimateFinishTimeForKart(m_karts[kartid]) + float punished_time = estimateFinishTimeForKart(m_karts[kartid].get()) + worse_finish_time - WorldStatus::getTime(); m_karts[kartid]->finishedRace(punished_time); diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index b21d504f8..14c930854 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -722,18 +722,18 @@ void ThreeStrikesBattle::loadCustomModels() // Now add them for (unsigned int i = 0; i < pos.size(); i++) { - AbstractKart* sta = new Kart(sta_list[i], (int)m_karts.size(), + auto sta = std::make_shared<Kart>(sta_list[i], (int)m_karts.size(), (int)m_karts.size() + 1, pos[i], PLAYER_DIFFICULTY_NORMAL, std::make_shared<RenderInfo>(1.0f)); sta->init(RaceManager::KartType::KT_SPARE_TIRE); - sta->setController(new SpareTireAI(sta)); + sta->setController(new SpareTireAI(sta.get())); m_karts.push_back(sta); race_manager->addSpareTireKart(sta_list[i]); // Copy STA pointer to m_spare_tire_karts array, allowing them // to respawn easily - m_spare_tire_karts.push_back(sta); + m_spare_tire_karts.push_back(sta.get()); } unsigned int sta_num = race_manager->getNumSpareTireKarts(); assert(m_spare_tire_karts.size() == sta_num); diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 2d434bf7b..bb2b0c54c 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -120,7 +120,7 @@ World* World::m_world = NULL; */ World::World() : WorldStatus() { - + RewindManager::setEnable(NetworkConfig::get()->isNetworking()); #ifdef DEBUG m_magic_number = 0xB01D6543; #endif @@ -212,10 +212,9 @@ void World::init() : race_manager->getKartIdent(i); int local_player_id = race_manager->getKartLocalPlayerId(i); int global_player_id = race_manager->getKartGlobalPlayerId(i); - AbstractKart* newkart = createKart(kart_ident, i, local_player_id, - global_player_id, - race_manager->getKartType(i), - race_manager->getPlayerDifficulty(i)); + auto newkart = createKart(kart_ident, i, local_player_id, + global_player_id, race_manager->getKartType(i), + race_manager->getPlayerDifficulty(i)); m_karts.push_back(newkart); } // for i @@ -301,6 +300,7 @@ void World::reset() SFXManager::get()->resumeAll(); projectile_manager->cleanup(); + RewindManager::get()->reset(); race_manager->reset(); // Make sure to overwrite the data from the previous race. if(!history->replayHistory()) history->initRecording(); @@ -334,10 +334,10 @@ void World::createRaceGUI() * \param global_player_id If the kart is a player kart this is the index of * this player globally (i.e. including network players). */ -AbstractKart *World::createKart(const std::string &kart_ident, int index, - int local_player_id, int global_player_id, - RaceManager::KartType kart_type, - PerPlayerDifficulty difficulty) +std::shared_ptr<AbstractKart> World::createKart + (const std::string &kart_ident, int index, int local_player_id, + int global_player_id, RaceManager::KartType kart_type, + PerPlayerDifficulty difficulty) { unsigned int gk = 0; if (race_manager->hasGhostKarts()) @@ -355,13 +355,19 @@ AbstractKart *World::createKart(const std::string &kart_ident, int index, int position = index+1; btTransform init_pos = getStartTransform(index - gk); - AbstractKart *new_kart; + std::shared_ptr<AbstractKart> new_kart; if (RewindManager::get()->isEnabled()) - new_kart = new KartRewinder(kart_ident, index, position, init_pos, - difficulty, ri); + { + auto kr = std::make_shared<KartRewinder>(kart_ident, index, position, + init_pos, difficulty, ri); + kr->rewinderAdd(); + new_kart = kr; + } else - new_kart = new Kart(kart_ident, index, position, init_pos, difficulty, - ri); + { + new_kart = std::make_shared<Kart>(kart_ident, index, position, + init_pos, difficulty, ri); + } new_kart->init(race_manager->getKartType(index)); Controller *controller = NULL; @@ -369,7 +375,7 @@ AbstractKart *World::createKart(const std::string &kart_ident, int index, { case RaceManager::KT_PLAYER: { - controller = new LocalPlayerController(new_kart, local_player_id, + controller = new LocalPlayerController(new_kart.get(), local_player_id, difficulty); const PlayerProfile* p = StateManager::get() ->getActivePlayer(local_player_id)->getConstProfile(); @@ -383,7 +389,7 @@ AbstractKart *World::createKart(const std::string &kart_ident, int index, } case RaceManager::KT_NETWORK_PLAYER: { - controller = new NetworkPlayerController(new_kart); + controller = new NetworkPlayerController(new_kart.get()); if (!online_name.empty()) new_kart->setOnScreenText(online_name.c_str()); m_num_players++; @@ -391,7 +397,7 @@ AbstractKart *World::createKart(const std::string &kart_ident, int index, } case RaceManager::KT_AI: { - controller = loadAIController(new_kart); + controller = loadAIController(new_kart.get()); break; } case RaceManager::KT_GHOST: @@ -417,7 +423,7 @@ const btTransform &World::getStartTransform(int index) /** Creates an AI controller for the kart. * \param kart The kart to be controlled by an AI. */ -Controller* World::loadAIController(AbstractKart *kart) +Controller* World::loadAIController(AbstractKart* kart) { Controller *controller; int turn=0; @@ -485,13 +491,7 @@ World::~World() Weather::kill(); - for ( unsigned int i = 0 ; i < m_karts.size() ; i++ ) - { - // Let ReplayPlay destroy the ghost karts - if (m_karts[i]->isGhostKart()) continue; - delete m_karts[i]; - } - + m_karts.clear(); if(race_manager->hasGhostKarts() || race_manager->isRecordingRace()) { // Destroy the old replay object, which also stored the ghost @@ -502,7 +502,6 @@ World::~World() ReplayPlay::destroy(); ReplayPlay::create(); } - m_karts.clear(); if(race_manager->isRecordingRace()) ReplayRecorder::get()->reset(); race_manager->setRaceGhostKarts(false); @@ -567,7 +566,8 @@ void World::terminateRace() { if(!m_karts[i]->hasFinishedRace() && !m_karts[i]->isEliminated()) { - m_karts[i]->finishedRace(estimateFinishTimeForKart(m_karts[i])); + m_karts[i]->finishedRace( + estimateFinishTimeForKart(m_karts[i].get())); } } // i<kart_amount @@ -699,7 +699,7 @@ void World::resetAllKarts() btTransform t = getRescueTransform(rescue_pos); // This will print out warnings if there is no terrain under // the kart, or the kart is being dropped on a reset texture - moveKartTo(m_karts[kart_id], t); + moveKartTo(m_karts[kart_id].get(), t); } // rescue_pos<getNumberOfRescuePositions @@ -725,7 +725,7 @@ void World::resetAllKarts() Vec3 up_offset = (*i)->getNormal() * (0.5f * ((*i)->getKartHeight())); (*i)->setXYZ(xyz+up_offset); - bool kart_over_ground = Track::getCurrentTrack()->findGround(*i); + bool kart_over_ground = Track::getCurrentTrack()->findGround(i->get()); if (!kart_over_ground) { @@ -1055,14 +1055,16 @@ void World::update(int ticks) m_karts[i]->update(ticks); } PROFILER_POP_CPU_MARKER(); - if(race_manager->isRecordingRace()) ReplayRecorder::get()->update(ticks); - Physics::getInstance()->update(ticks); PROFILER_PUSH_CPU_MARKER("World::update (projectiles)", 0xa0, 0x7F, 0x00); projectile_manager->update(ticks); PROFILER_POP_CPU_MARKER(); + PROFILER_PUSH_CPU_MARKER("World::update (physics)", 0xa0, 0x7F, 0x00); + Physics::getInstance()->update(ticks); + PROFILER_POP_CPU_MARKER(); + PROFILER_POP_CPU_MARKER(); #ifdef DEBUG @@ -1165,7 +1167,7 @@ void World::updateHighscores(int* best_highscore_rank) if (!m_karts[index[pos]]->hasFinishedRace()) continue; assert(index[pos] < m_karts.size()); - Kart *k = (Kart*)m_karts[index[pos]]; + Kart *k = (Kart*)m_karts[index[pos]].get(); Highscores* highscores = getHighscores(); @@ -1197,14 +1199,17 @@ void World::updateHighscores(int* best_highscore_rank) */ AbstractKart *World::getPlayerKart(unsigned int n) const { - unsigned int count=-1; + unsigned int count = -1; - for(unsigned int i=0; i<m_karts.size(); i++) - if(m_karts[i]->getController()->isPlayerController()) + for(unsigned int i = 0; i < m_karts.size(); i++) + { + if (m_karts[i]->getController()->isPlayerController()) { count++; - if(count==n) return m_karts[i]; + if (count == n) + return m_karts[i].get(); } + } return NULL; } // getPlayerKart @@ -1225,7 +1230,7 @@ AbstractKart *World::getLocalPlayerKart(unsigned int n) const void World::eliminateKart(int kart_id, bool notify_of_elimination) { assert(kart_id < (int)m_karts.size()); - AbstractKart *kart = m_karts[kart_id]; + AbstractKart *kart = m_karts[kart_id].get(); if (kart->isGhostKart()) return; // Display a message about the eliminated kart in the race guia diff --git a/src/modes/world.hpp b/src/modes/world.hpp index 4be4522e6..34ba7a37d 100644 --- a/src/modes/world.hpp +++ b/src/modes/world.hpp @@ -25,6 +25,7 @@ * battle, etc.) */ +#include <memory> #include <vector> #include <stdexcept> @@ -80,7 +81,7 @@ public: class World : public WorldStatus { public: - typedef std::vector<AbstractKart*> KartList; + typedef std::vector<std::shared_ptr<AbstractKart> > KartList; private: /** A pointer to the global world object for a race. */ static World *m_world; @@ -117,10 +118,10 @@ protected: Controller* loadAIController (AbstractKart *kart); - virtual AbstractKart *createKart(const std::string &kart_ident, int index, - int local_player_id, int global_player_id, - RaceManager::KartType type, - PerPlayerDifficulty difficulty); + virtual std::shared_ptr<AbstractKart> createKart + (const std::string &kart_ident, int index, int local_player_id, + int global_player_id, RaceManager::KartType type, + PerPlayerDifficulty difficulty); /** Pointer to the race GUI. The race GUI is handled by world. */ RaceGUIBase *m_race_gui; @@ -293,7 +294,7 @@ public: /** Returns the kart with a given world id. */ AbstractKart *getKart(int kartId) const { assert(kartId >= 0 && kartId < int(m_karts.size())); - return m_karts[kartId]; } + return m_karts[kartId].get(); } // ------------------------------------------------------------------------ /** Returns all karts. */ const KartList & getKarts() const { return m_karts; } diff --git a/src/modes/world_status.hpp b/src/modes/world_status.hpp index 66afdacda..4da315508 100644 --- a/src/modes/world_status.hpp +++ b/src/modes/world_status.hpp @@ -112,9 +112,9 @@ private: protected: bool m_play_track_intro_sound; bool m_play_ready_set_go_sounds; + Phase m_phase; private: - Phase m_phase; /** * Remember previous phase e.g. on pause diff --git a/src/modes/world_with_rank.cpp b/src/modes/world_with_rank.cpp index 6d23e20f2..862b3182d 100644 --- a/src/modes/world_with_rank.cpp +++ b/src/modes/world_with_rank.cpp @@ -82,7 +82,7 @@ AbstractKart* WorldWithRank::getKartAtPosition(unsigned int p) const if(p<1 || p>m_position_index.size()) return NULL; - return m_karts[m_position_index[p-1]]; + return m_karts[m_position_index[p-1]].get(); } // getKartAtPosition //----------------------------------------------------------------------------- diff --git a/src/network/network_string.hpp b/src/network/network_string.hpp index 760f0b4bd..ea9563558 100644 --- a/src/network/network_string.hpp +++ b/src/network/network_string.hpp @@ -74,6 +74,10 @@ protected: */ std::string getString(int len) const { + if (m_current_offset > (int)m_buffer.size() || + m_current_offset + len > (int)m_buffer.size()) + throw std::out_of_range("getString out of range."); + std::string a(m_buffer.begin() + (m_current_offset ), m_buffer.begin() + (m_current_offset + len)); m_current_offset += len; @@ -101,7 +105,7 @@ protected: { result <<= 8; // offset one byte // add the data to result - result += m_buffer[offset - a]; + result += m_buffer.at(offset - a); } return result; } // get(int pos) @@ -110,7 +114,7 @@ protected: template<typename T> T get() const { - return m_buffer[m_current_offset++]; + return m_buffer.at(m_current_offset++); } // get public: @@ -195,7 +199,8 @@ public: { return (char*)(m_buffer.data()+m_current_offset); } // getCurrentData - + // ------------------------------------------------------------------------ + int getCurrentOffset() const { return m_current_offset; } // ------------------------------------------------------------------------ /** Returns the remaining length of the network string. */ unsigned int size() const { return (int)m_buffer.size()-m_current_offset; } @@ -424,8 +429,7 @@ public: /** Returns the protocol type of this message. */ ProtocolType getProtocolType() const { - assert(!m_buffer.empty()); - return (ProtocolType)(m_buffer[0] & ~PROTOCOL_SYNCHRONOUS); + return (ProtocolType)(m_buffer.at(0) & ~PROTOCOL_SYNCHRONOUS); } // getProtocolType // ------------------------------------------------------------------------ diff --git a/src/network/protocol_manager.cpp b/src/network/protocol_manager.cpp index f0b740efc..4fe3cb5a2 100644 --- a/src/network/protocol_manager.cpp +++ b/src/network/protocol_manager.cpp @@ -378,14 +378,15 @@ bool ProtocolManager::sendEvent(Event* event) bool can_be_deleted = false; if (event->getType() == EVENT_TYPE_MESSAGE) { - OneProtocolType &opt = m_all_protocols[event->data().getProtocolType()]; + OneProtocolType &opt = + m_all_protocols.at(event->data().getProtocolType()); can_be_deleted = opt.notifyEvent(event); } else // connect or disconnect event --> test all protocols { for (unsigned int i = 0; i < m_all_protocols.size(); i++) { - can_be_deleted |= m_all_protocols[i].notifyEvent(event); + can_be_deleted |= m_all_protocols.at(i).notifyEvent(event); } } return can_be_deleted || StkTime::getTimeSinceEpoch() - event->getArrivalTime() @@ -432,7 +433,16 @@ void ProtocolManager::update(int ticks) while (i != m_sync_events_to_process.getData().end()) { m_sync_events_to_process.unlock(); - bool can_be_deleted = sendEvent(*i); + bool can_be_deleted = true; + try + { + can_be_deleted = sendEvent(*i); + } + catch (std::exception& e) + { + Log::error("ProtocolManager", "Synchronous event error: %s", + e.what()); + } m_sync_events_to_process.lock(); if (can_be_deleted) { @@ -478,7 +488,16 @@ void ProtocolManager::asynchronousUpdate() m_async_events_to_process.unlock(); m_all_protocols[(*i)->getType()].lock(); - bool result = sendEvent(*i); + bool result = true; + try + { + result = sendEvent(*i); + } + catch (std::exception& e) + { + Log::error("ProtocolManager", "Asynchronous event error: %s", + e.what()); + } m_all_protocols[(*i)->getType()].unlock(); m_async_events_to_process.lock(); diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index 2b5753b02..8415e4218 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -166,6 +166,7 @@ bool ClientLobby::notifyEventAsynchronous(Event* event) case LE_CONNECTION_REFUSED: connectionRefused(event); break; case LE_VOTE: displayPlayerVote(event); break; case LE_SERVER_OWNERSHIP: becomingServerOwner(); break; + case LE_BAD_TEAM: handleBadTeam(); break; default: break; } // switch @@ -610,10 +611,21 @@ void ClientLobby::updatePlayerList(Event* event) NetworkingLobby::getInstance()->updatePlayers(players); } // updatePlayerList +//----------------------------------------------------------------------------- +void ClientLobby::handleBadTeam() +{ + SFXManager::get()->quickSound("anvil"); + //I18N: Display when all players are in red or blue team, which the race + //will not be allowed to start + core::stringw msg = _("All players joined red or blue team."); + MessageQueue::add(MessageQueue::MT_ERROR, msg); +} // handleBadTeam + //----------------------------------------------------------------------------- void ClientLobby::becomingServerOwner() { SFXManager::get()->quickSound("wee"); + //I18N: Display when a player is allow to control the server core::stringw msg = _("You are now the owner of server."); MessageQueue::add(MessageQueue::MT_GENERIC, msg); STKHost::get()->setAuthorisedToControl(true); diff --git a/src/network/protocols/client_lobby.hpp b/src/network/protocols/client_lobby.hpp index 9efc4c801..389d24675 100644 --- a/src/network/protocols/client_lobby.hpp +++ b/src/network/protocols/client_lobby.hpp @@ -45,6 +45,7 @@ private: void updatePlayerList(Event* event); void handleChat(Event* event); void handleServerInfo(Event* event); + void handleBadTeam(); void becomingServerOwner(); void clearPlayers(); diff --git a/src/network/protocols/game_events_protocol.cpp b/src/network/protocols/game_events_protocol.cpp index f7f8f7ebc..21ef36afd 100644 --- a/src/network/protocols/game_events_protocol.cpp +++ b/src/network/protocols/game_events_protocol.cpp @@ -54,14 +54,14 @@ bool GameEventsProtocol::notifyEvent(Event* event) case GE_RESET_BALL: { if (!sw) - throw("No soccer world"); + throw std::invalid_argument("No soccer world"); sw->handleResetBallFromServer(data); break; } case GE_PLAYER_GOAL: { if (!sw) - throw("No soccer world"); + throw std::invalid_argument("No soccer world"); sw->handlePlayerGoalFromServer(data); break; } diff --git a/src/network/protocols/lobby_protocol.cpp b/src/network/protocols/lobby_protocol.cpp index db6850259..7813238ea 100644 --- a/src/network/protocols/lobby_protocol.cpp +++ b/src/network/protocols/lobby_protocol.cpp @@ -28,7 +28,6 @@ #include "network/protocols/game_protocol.hpp" #include "network/protocols/game_events_protocol.hpp" #include "network/race_event_manager.hpp" -#include "network/rewind_manager.hpp" #include "race/race_manager.hpp" #include "states_screens/state_manager.hpp" @@ -59,8 +58,6 @@ LobbyProtocol::~LobbyProtocol() void LobbyProtocol::loadWorld() { Log::info("LobbyProtocol", "Ready !"); - RewindManager::setEnable(true); - // Race startup sequence // --------------------- // This creates the network world. diff --git a/src/network/protocols/lobby_protocol.hpp b/src/network/protocols/lobby_protocol.hpp index 27a1929cb..b047d5188 100644 --- a/src/network/protocols/lobby_protocol.hpp +++ b/src/network/protocols/lobby_protocol.hpp @@ -60,7 +60,8 @@ public: LE_CHAT, LE_SERVER_OWNERSHIP, LE_KICK_HOST, - LE_CHANGE_TEAM + LE_CHANGE_TEAM, + LE_BAD_TEAM }; enum RejectReason : uint8_t diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index d30e80985..e7746949c 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -381,7 +381,7 @@ void ServerLobby::asynchronousUpdate() if (NetworkConfig::get()->isOwnerLess()) { auto players = m_game_setup->getPlayers(); - if (((float)players.size() > + if (((float)players.size() >= (float)NetworkConfig::get()->getMaxPlayers() * UserConfigParams::m_start_game_threshold || m_game_setup->isGrandPrixStarted()) && @@ -761,6 +761,31 @@ void ServerLobby::startSelection(const Event *event) } } + if (NetworkConfig::get()->hasTeamChoosing()) + { + int red_count = 0; + int blue_count = 0; + auto players = m_game_setup->getConnectedPlayers(); + for (auto& player : players) + { + if (player->getTeam() == SOCCER_TEAM_RED) + red_count++; + else if (player->getTeam() == SOCCER_TEAM_BLUE) + blue_count++; + if (red_count != 0 && blue_count != 0) + break; + } + if ((red_count == 0 || blue_count == 0) && players.size() != 1) + { + Log::warn("ServerLobby", "Bad team choosing."); + NetworkString* bt = getNetworkString(); + bt->addUInt8(LE_BAD_TEAM); + sendMessageToPeers(bt, true/*reliable*/); + delete bt; + return; + } + } + ProtocolManager::lock()->findAndTerminate(PROTOCOL_CONNECTION); if (NetworkConfig::get()->isWAN()) { @@ -1857,16 +1882,25 @@ void ServerLobby::handlePendingConnection() auto key = m_keys.find(online_id); if (key != m_keys.end() && key->second.m_tried == false) { - if (decryptConnectionRequest(peer, it->second.second, - key->second.m_aes_key, key->second.m_aes_iv, online_id, - key->second.m_name)) + try { - it = m_pending_connection.erase(it); - m_keys.erase(online_id); - continue; + if (decryptConnectionRequest(peer, it->second.second, + key->second.m_aes_key, key->second.m_aes_iv, online_id, + key->second.m_name)) + { + it = m_pending_connection.erase(it); + m_keys.erase(online_id); + continue; + } + else + key->second.m_tried = true; } - else + catch (std::exception& e) + { + Log::error("ServerLobby", + "handlePendingConnection error: %s", e.what()); key->second.m_tried = true; + } } it++; } diff --git a/src/network/rewind_info.cpp b/src/network/rewind_info.cpp index e0c19cd10..676e68115 100644 --- a/src/network/rewind_info.cpp +++ b/src/network/rewind_info.cpp @@ -19,8 +19,9 @@ #include "network/rewind_info.hpp" #include "network/network_config.hpp" +#include "network/rewinder.hpp" #include "network/rewind_manager.hpp" -#include "physics/physics.hpp" +#include "items/projectile_manager.hpp" /** Constructor for a state: it only takes the size, and allocates a buffer * for all state info. @@ -63,16 +64,44 @@ void RewindInfoState::restore() m_buffer->reset(); for (const std::string& name : m_rewinder_using) { - uint16_t count = m_buffer->getUInt16(); - Rewinder* r = RewindManager::get()->getRewinder(name); - if (r == NULL) + const uint16_t data_size = m_buffer->getUInt16(); + const unsigned current_offset_now = m_buffer->getCurrentOffset(); + std::shared_ptr<Rewinder> r = + RewindManager::get()->getRewinder(name); + + if (!r) + { + // For now we only need to get missing rewinder from + // projectile_manager + r = projectile_manager->addRewinderFromNetworkState(name); + } + if (!r) { Log::error("RewindInfoState", "Missing rewinder %s", name.c_str()); - m_buffer->skip(count); + m_buffer->skip(data_size); continue; } - r->restoreState(m_buffer, count); + try + { + r->restoreState(m_buffer, data_size); + } + catch (std::exception& e) + { + Log::error("RewindInfoState", "Restore state error: %s", + e.what()); + m_buffer->reset(); + m_buffer->skip(current_offset_now + data_size); + continue; + } + + if (m_buffer->getCurrentOffset() - current_offset_now != data_size) + { + Log::error("RewindInfoState", "Wrong size read when restore " + "state, incompatible binary?"); + m_buffer->reset(); + m_buffer->skip(current_offset_now + data_size); + } } // for all rewinder } // restore diff --git a/src/network/rewind_info.hpp b/src/network/rewind_info.hpp index 5ca1bf0cb..26310ea74 100644 --- a/src/network/rewind_info.hpp +++ b/src/network/rewind_info.hpp @@ -21,7 +21,6 @@ #include "network/event_rewinder.hpp" #include "network/network_string.hpp" -#include "network/rewinder.hpp" #include "utils/cpp2011.hpp" #include "utils/leak_check.hpp" #include "utils/ptr_vector.hpp" @@ -173,14 +172,18 @@ public: class RewindInfoEventFunction : public RewindInfo { private: - const std::function<void()> m_undo_function, m_replay_function; + const std::function<void()> m_undo_function, m_replay_function, + m_destroy_function; public: RewindInfoEventFunction(int ticks, std::function<void()> undo_function = [](){}, std::function<void()> replay_function = [](){}, - bool is_confirmed = true) - : RewindInfo(ticks, is_confirmed), - m_undo_function(undo_function), m_replay_function(replay_function) {} + std::function<void()> destroy_function = [](){}) + : RewindInfo(ticks, true/*is_confirmed*/), + m_undo_function(undo_function), m_replay_function(replay_function), + m_destroy_function(destroy_function) {} + // ------------------------------------------------------------------------ + ~RewindInfoEventFunction() { m_destroy_function(); } // ------------------------------------------------------------------------ /** An event is never 'restored', it is only rewound. */ void restore() {} diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index eb37d92b2..c881f99b9 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -82,18 +82,7 @@ void RewindManager::reset() if (!m_enable_rewind_manager) return; - for (auto it = m_all_rewinder.begin(); it != m_all_rewinder.end();) - { - if (!it->second->canBeDestroyed()) - { - it++; - continue; - } - Rewinder* rewinder = it->second; - it = m_all_rewinder.erase(it); - delete rewinder; - } - + clearExpiredRewinder(); m_rewind_queue.reset(); } // reset @@ -166,7 +155,9 @@ void RewindManager::saveState() { // TODO: check if it's worth passing in a sufficiently large buffer from // GameProtocol - this would save the copy operation. - BareNetworkString* buffer = p.second->saveState(&rewinder_using); + BareNetworkString* buffer = NULL; + if (auto r = p.second.lock()) + buffer = r->saveState(&rewinder_using); if (buffer != NULL) { m_overall_state_size += buffer->size(); @@ -197,12 +188,16 @@ void RewindManager::update(int ticks_not_used) if (ticks - m_last_saved_state < m_state_frequency) return; - // Save state + // Save state, remove expired rewinder first + clearExpiredRewinder(); if (NetworkConfig::get()->isClient()) { auto& ret = m_local_state[ticks]; for (auto& p : m_all_rewinder) - ret.push_back(p.second->getLocalStateRestoreFunction()); + { + if (auto r = p.second.lock()) + ret.push_back(r->getLocalStateRestoreFunction()); + } } else { @@ -222,6 +217,10 @@ void RewindManager::update(int ticks_not_used) */ void RewindManager::playEventsTill(int world_ticks, int *ticks) { + // We add the RewindInfoEventFunction to rewind queue before and after + // possible rewind, some RewindInfoEventFunction can be created during + // rewind + mergeRewindInfoEventFunction(); bool needs_rewind; int rewind_ticks; @@ -255,6 +254,20 @@ void RewindManager::playEventsTill(int world_ticks, int *ticks) m_is_rewinding = false; } // playEventsTill +// ---------------------------------------------------------------------------- +/** Adds a Rewinder to the list of all rewinders. + * \return true If successfully added, false otherwise. + */ +bool RewindManager::addRewinder(std::shared_ptr<Rewinder> rewinder) +{ + if (!m_enable_rewind_manager) return false; + // Maximum 1 bit to store no of rewinder used + if (m_all_rewinder.size() == 255) + return false; + m_all_rewinder[rewinder->getUniqueIdentity()] = rewinder; + return true; +} // addRewinder + // ---------------------------------------------------------------------------- /** Rewinds to the specified time, then goes forward till the current * World::getTime() is reached again: it will replay everything before @@ -274,7 +287,10 @@ void RewindManager::rewindTo(int rewind_ticks, int now_ticks) // can be computed between the transforms before and after // the rewind. for (auto& p : m_all_rewinder) - p.second->saveTransform(); + { + if (auto r = p.second.lock()) + r->saveTransform(); + } // Then undo the rewind infos going backwards in time // -------------------------------------------------- @@ -348,10 +364,14 @@ void RewindManager::rewindTo(int rewind_ticks, int now_ticks) // Now compute the errors which need to be visually smoothed for (auto& p : m_all_rewinder) - p.second->computeError(); + { + if (auto r = p.second.lock()) + r->computeError(); + } history->setReplayHistory(is_history); m_is_rewinding = false; + mergeRewindInfoEventFunction(); } // rewindTo // ---------------------------------------------------------------------------- @@ -360,3 +380,11 @@ bool RewindManager::useLocalEvent() const return NetworkConfig::get()->isNetworking() && NetworkConfig::get()->isClient() && !m_is_rewinding; } // useLocalEvent + +// ---------------------------------------------------------------------------- +void RewindManager::mergeRewindInfoEventFunction() +{ + for (RewindInfoEventFunction* rief : m_pending_rief) + m_rewind_queue.insertRewindInfo(rief); + m_pending_rief.clear(); +} // mergeRewindInfoEventFunction diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index ef945b34b..6b3319b2e 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -19,7 +19,6 @@ #ifndef HEADER_REWIND_MANAGER_HPP #define HEADER_REWIND_MANAGER_HPP -#include "network/rewinder.hpp" #include "network/rewind_queue.hpp" #include "utils/ptr_vector.hpp" #include "utils/synchronised.hpp" @@ -27,10 +26,15 @@ #include <assert.h> #include <atomic> #include <functional> +#include <memory> #include <map> +#include <set> +#include <string> #include <vector> +class Rewinder; class RewindInfo; +class RewindInfoEventFunction; class EventRewinder; /** \ingroup network @@ -93,7 +97,7 @@ private: std::map<int, std::vector<std::function<void()> > > m_local_state; /** A list of all objects that can be rewound. */ - std::map<std::string, Rewinder*> m_all_rewinder; + std::map<std::string, std::weak_ptr<Rewinder> > m_all_rewinder; /** The queue that stores all rewind infos. */ RewindQueue m_rewind_queue; @@ -115,8 +119,25 @@ private: * rewinds. */ std::atomic<int> m_not_rewound_ticks; + std::vector<RewindInfoEventFunction*> m_pending_rief; + RewindManager(); ~RewindManager(); + // ------------------------------------------------------------------------ + void clearExpiredRewinder() + { + for (auto it = m_all_rewinder.begin(); it != m_all_rewinder.end();) + { + if (it->second.expired()) + { + it = m_all_rewinder.erase(it); + continue; + } + it++; + } + } + // ------------------------------------------------------------------------ + void mergeRewindInfoEventFunction(); public: // First static functions to manage rewinding. @@ -151,27 +172,18 @@ public: void addNetworkState(BareNetworkString *buffer, int ticks); void saveState(); // ------------------------------------------------------------------------ - Rewinder* getRewinder(const std::string& name) + std::shared_ptr<Rewinder> getRewinder(const std::string& name) { auto it = m_all_rewinder.find(name); - if (it == m_all_rewinder.end()) - return NULL; - return it->second; + if (it != m_all_rewinder.end()) + { + if (auto r = it->second.lock()) + return r; + } + return nullptr; } // ------------------------------------------------------------------------ - /** Adds a Rewinder to the list of all rewinders. - * \return true If rewinding is enabled, false otherwise. - */ - bool addRewinder(Rewinder *rewinder) - { - if (!m_enable_rewind_manager) return false; - // Maximum 1 bit to store no of rewinder used - if (m_all_rewinder.size() == 255) - return false; - m_all_rewinder[rewinder->getUniqueIdentity()] = rewinder; - return true; - } // addRewinder - + bool addRewinder(std::shared_ptr<Rewinder> rewinder); // ------------------------------------------------------------------------ /** Returns true if currently a rewind is happening. */ bool isRewinding() const { return m_is_rewinding; } @@ -182,8 +194,6 @@ public: return m_not_rewound_ticks.load(std::memory_order_relaxed); } // getNotRewoundWorldTicks // ------------------------------------------------------------------------ - RewindQueue& getRewindQueue() { return m_rewind_queue; } - // ------------------------------------------------------------------------ /** Returns the time of the latest confirmed state. */ int getLatestConfirmedState() const { @@ -200,6 +210,9 @@ public: { return m_current_rewinder_using; } // ------------------------------------------------------------------------ bool useLocalEvent() const; + // ------------------------------------------------------------------------ + void addRewindInfoEventFunction(RewindInfoEventFunction* rief) + { m_pending_rief.push_back(rief); } }; // RewindManager diff --git a/src/network/rewind_queue.cpp b/src/network/rewind_queue.cpp index ab14482d6..b9475c4fb 100644 --- a/src/network/rewind_queue.cpp +++ b/src/network/rewind_queue.cpp @@ -21,6 +21,7 @@ #include "config/stk_config.hpp" #include "modes/world.hpp" #include "network/network_config.hpp" +#include "network/rewinder.hpp" #include "network/rewind_info.hpp" #include "network/rewind_manager.hpp" @@ -353,8 +354,10 @@ int RewindQueue::undoUntil(int undo_ticks) { // A rewind is done after a state in the past is inserted. This function // makes sure that m_current is not end() - assert(m_current != m_all_rewind_info.end()); - + //assert(m_current != m_all_rewind_info.end()); + assert(!m_all_rewind_info.empty()); + m_current = m_all_rewind_info.end(); + m_current--; while((*m_current)->getTicks() > undo_ticks || (*m_current)->isEvent() || !(*m_current)->isConfirmed()) { @@ -418,9 +421,9 @@ void RewindQueue::unitTesting() virtual void rewind(BareNetworkString *s) {} virtual void saveTransform() {} virtual void computeError() {} - DummyRewinder() : Rewinder("dummy_rewinder", true) {} + DummyRewinder() : Rewinder() {} }; - DummyRewinder *dummy_rewinder = new DummyRewinder(); + auto dummy_rewinder = std::make_shared<DummyRewinder>(); // First tests: add a state first, then an event, and make // sure the state stays first @@ -434,7 +437,7 @@ void RewindQueue::unitTesting() assert(q0.hasMoreRewindInfo()); assert(q0.undoUntil(0) == 0); - q0.addNetworkEvent(dummy_rewinder, NULL, 0); + q0.addNetworkEvent(dummy_rewinder.get(), NULL, 0); // Network events are not immediately merged assert(q0.m_all_rewind_info.size() == 1); @@ -462,9 +465,9 @@ void RewindQueue::unitTesting() assert((*rii)->isEvent()); // Test time base comparisons: adding an event to the end - q0.addLocalEvent(dummy_rewinder, NULL, true, 4); + q0.addLocalEvent(dummy_rewinder.get(), NULL, true, 4); // Then adding an earlier event - q0.addLocalEvent(dummy_rewinder, NULL, false, 1); + q0.addLocalEvent(dummy_rewinder.get(), NULL, false, 1); // rii points to the 3rd element, the ones added just now // should be elements4 and 5: rii++; diff --git a/src/network/rewind_queue.hpp b/src/network/rewind_queue.hpp index 98f03290b..90f1b39fc 100644 --- a/src/network/rewind_queue.hpp +++ b/src/network/rewind_queue.hpp @@ -19,14 +19,13 @@ #ifndef HEADER_REWIND_QUEUE_HPP #define HEADER_REWIND_QUEUE_HPP -#include "network/rewinder.hpp" -#include "utils/ptr_vector.hpp" #include "utils/synchronised.hpp" #include <assert.h> #include <list> #include <vector> +class BareNetworkString; class EventRewinder; class RewindInfo; class TimeStepInfo; diff --git a/src/network/rewinder.cpp b/src/network/rewinder.cpp index 48b8c9b41..00d76c1dc 100644 --- a/src/network/rewinder.cpp +++ b/src/network/rewinder.cpp @@ -20,27 +20,11 @@ #include "network/rewind_manager.hpp" -/** Constructor. It will add this object to the list of all rewindable +// ---------------------------------------------------------------------------- +/** Add this object to the list of all rewindable * objects in the rewind manager. */ -Rewinder::Rewinder(const std::string& ui, bool can_be_destroyed, bool auto_add) - : m_unique_identity(ui) +bool Rewinder::rewinderAdd() { - assert(!m_unique_identity.empty() && m_unique_identity.size() < 255); - m_can_be_destroyed = can_be_destroyed; - if (auto_add) - add(); -} // Rewinder - -// ---------------------------------------------------------------------------- -/** Destructor. - */ -Rewinder::~Rewinder() -{ -} // ~Rewinder - -// ---------------------------------------------------------------------------- -void Rewinder::add() -{ - RewindManager::get()->addRewinder(this); -} // Rewinder + return RewindManager::get()->addRewinder(shared_from_this()); +} // rewinderAdd diff --git a/src/network/rewinder.hpp b/src/network/rewinder.hpp index 364023d29..e314152dd 100644 --- a/src/network/rewinder.hpp +++ b/src/network/rewinder.hpp @@ -19,33 +19,25 @@ #ifndef HEADER_REWINDER_HPP #define HEADER_REWINDER_HPP +#include <cassert> #include <functional> #include <string> +#include <memory> #include <vector> class BareNetworkString; -class Rewinder +class Rewinder : public std::enable_shared_from_this<Rewinder> { protected: - void add(); - // ------------------------------------------------------------------------- void setUniqueIdentity(const std::string& uid) { m_unique_identity = uid; } - private: std::string m_unique_identity; - /** True if this object can be destroyed, i.e. if this object is a 'stand - * alone' (i.e. not used in inheritance). If the object is used in - * inheritance (e.g. KartRewinder, which is a Rewinder and Kart), then - * freeing the kart will free this rewinder instance as well. - */ - bool m_can_be_destroyed; - public: - Rewinder(const std::string& ui, bool can_be_destroyed, - bool auto_add = true); - virtual ~Rewinder(); + Rewinder(const std::string& ui = "") { m_unique_identity = ui; } + + virtual ~Rewinder() {} /** Called before a rewind. Is used to save the previous position of an * object before a rewind, so that the error due to a rewind can be @@ -89,14 +81,20 @@ public: /** Nothing to do here. */ virtual void reset() {} // ------------------------------------------------------------------------- - /** True if this rewinder can be destroyed. Karts can not be destroyed, - * cakes can. This is used by the RewindManager in reset. */ - bool canBeDestroyed() const { return m_can_be_destroyed; } - // ------------------------------------------------------------------------- virtual std::function<void()> getLocalStateRestoreFunction() { return nullptr; } // ------------------------------------------------------------------------- - const std::string& getUniqueIdentity() const { return m_unique_identity; } + const std::string& getUniqueIdentity() const + { + assert(!m_unique_identity.empty() && m_unique_identity.size() < 255); + return m_unique_identity; + } + // ------------------------------------------------------------------------- + bool rewinderAdd(); + // ------------------------------------------------------------------------- + template<typename T> std::shared_ptr<T> getShared() + { return std::dynamic_pointer_cast<T>(shared_from_this()); } + }; // Rewinder #endif diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index 046bcfc54..c707c25bc 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -720,7 +720,15 @@ void STKHost::mainLoop() auto sl = LobbyProtocol::get<ServerLobby>(); if (direct_socket && sl && sl->waitingForPlayers()) { - handleDirectSocketRequest(direct_socket, sl); + try + { + handleDirectSocketRequest(direct_socket, sl); + } + catch (std::exception& e) + { + Log::warn("STKHost", "Direct socket error: %s", + e.what()); + } } // if discovery host if (is_server) diff --git a/src/online/online_player_profile.cpp b/src/online/online_player_profile.cpp index 3ebb7bc33..aeef259a5 100644 --- a/src/online/online_player_profile.cpp +++ b/src/online/online_player_profile.cpp @@ -344,6 +344,14 @@ namespace Online request->queue(); } // requestPoll() + // ------------------------------------------------------------------------ + OnlinePlayerProfile::PollRequest::PollRequest() + : XMLRequest(true) + { + m_disable_sending_log = NetworkConfig::get()->isNetworking() && + NetworkConfig::get()->isServer(); + } // PollRequest + // ------------------------------------------------------------------------ /** Callback for the poll request. Parses the information and spawns * notifications accordingly. diff --git a/src/online/online_player_profile.hpp b/src/online/online_player_profile.hpp index 9f2935b56..2a7b9a222 100644 --- a/src/online/online_player_profile.hpp +++ b/src/online/online_player_profile.hpp @@ -61,7 +61,7 @@ namespace Online { virtual void callback (); public: - PollRequest() : XMLRequest(true) {} + PollRequest(); }; // PollRequest private: diff --git a/src/physics/btKartRaycast.cpp b/src/physics/btKartRaycast.cpp index 2a607a3ab..331e80a59 100644 --- a/src/physics/btKartRaycast.cpp +++ b/src/physics/btKartRaycast.cpp @@ -71,8 +71,6 @@ void* btKartRaycaster::castRay(const btVector3& from, const btVector3& to, result.m_hitNormalInWorld.normalize(); result.m_distFraction = rayCallback.m_closestHitFraction; result.m_triangle_index = -1; - const TriangleMesh &tm = - Track::getCurrentTrack()->getTriangleMesh(); // FIXME: this code assumes atm that the object the kart is // driving on is the main track (and not e.g. a physical object). // If this should not be the case (i.e. the object hit by the diff --git a/src/physics/physical_object.cpp b/src/physics/physical_object.cpp index d7434414e..8482861ae 100644 --- a/src/physics/physical_object.cpp +++ b/src/physics/physical_object.cpp @@ -123,12 +123,11 @@ void PhysicalObject::Settings::init() } // Settings // ============================================================================ -PhysicalObject* PhysicalObject::fromXML(bool is_dynamic, - const XMLNode &xml_node, - TrackObject* object) +std::shared_ptr<PhysicalObject> PhysicalObject::fromXML + (bool is_dynamic, const XMLNode &xml_node, TrackObject* object) { PhysicalObject::Settings settings(xml_node); - return new PhysicalObject(is_dynamic, settings, object); + return std::make_shared<PhysicalObject>(is_dynamic, settings, object); } // fromXML // ---------------------------------------------------------------------------- @@ -136,7 +135,6 @@ PhysicalObject* PhysicalObject::fromXML(bool is_dynamic, PhysicalObject::PhysicalObject(bool is_dynamic, const PhysicalObject::Settings& settings, TrackObject* object) - : Rewinder("P", false/*can_be_destroyed*/, false/*auto_add*/) { m_shape = NULL; m_body = NULL; @@ -793,7 +791,7 @@ void PhysicalObject::addForRewind() SmoothNetworkBody::setAdjustVerticalOffset(false); Rewinder::setUniqueIdentity(std::string("P") + StringUtils::toString (Track::getCurrentTrack()->getPhysicalObjectUID())); - Rewinder::add(); + Rewinder::rewinderAdd(); } // addForRewind // ---------------------------------------------------------------------------- diff --git a/src/physics/physical_object.hpp b/src/physics/physical_object.hpp index a2c115d55..480e5ca5f 100644 --- a/src/physics/physical_object.hpp +++ b/src/physics/physical_object.hpp @@ -197,8 +197,8 @@ public: PhysicalObject(bool is_dynamic, const Settings& settings, TrackObject* object); - static PhysicalObject* fromXML(bool is_dynamic, const XMLNode &node, - TrackObject* object); + static std::shared_ptr<PhysicalObject> fromXML + (bool is_dynamic, const XMLNode &node, TrackObject* object); virtual ~PhysicalObject (); virtual void reset (); diff --git a/src/race/history.cpp b/src/race/history.cpp index c7dcbced4..b51f99ce1 100644 --- a/src/race/history.cpp +++ b/src/race/history.cpp @@ -296,11 +296,13 @@ void History::Load() { fgets(s, 1023, fd); InputEvent &ie = m_all_input_events[i]; + int action = 0; if (sscanf(s, "%d %d %d %d\n", &ie.m_world_ticks, &ie.m_kart_index, - &ie.m_action, &ie.m_value) != 4 ) + &action, &ie.m_value) != 4) { Log::warn("History", "Problems reading event: '%s'", s); } + ie.m_action = (PlayerAction)action; } // for i RewindManager::setEnable(rewind_manager_was_enabled); diff --git a/src/replay/replay_play.cpp b/src/replay/replay_play.cpp index 9be552705..6207cf77d 100644 --- a/src/replay/replay_play.cpp +++ b/src/replay/replay_play.cpp @@ -58,7 +58,7 @@ void ReplayPlay::reset() { for(unsigned int i=0; i<(unsigned int)m_ghost_karts.size(); i++) { - m_ghost_karts[i].reset(); + m_ghost_karts[i]->reset(); } } // reset @@ -297,7 +297,7 @@ bool ReplayPlay::addReplayFile(const std::string& fn, bool custom_replay, int ca //----------------------------------------------------------------------------- void ReplayPlay::load() { - m_ghost_karts.clearAndDeleteAll(); + m_ghost_karts.clear(); if (m_second_replay_enabled) loadFile(/* second replay */ true); @@ -365,11 +365,11 @@ void ReplayPlay::readKartData(FILE *fd, char *next_line, bool second_replay) first_loaded_f_num = m_replay_file_list.at(m_second_replay_file).m_kart_list.size(); ReplayData &rd = m_replay_file_list[replay_index]; - m_ghost_karts.push_back(new GhostKart(rd.m_kart_list.at(kart_num-first_loaded_f_num), - kart_num, kart_num + 1, - rd.m_kart_color.at(kart_num-first_loaded_f_num))); - m_ghost_karts[kart_num].init(RaceManager::KT_GHOST); - Controller* controller = new GhostController(getGhostKart(kart_num), + m_ghost_karts.push_back(std::make_shared<GhostKart> + (rd.m_kart_list.at(kart_num-first_loaded_f_num), kart_num, kart_num + 1, + rd.m_kart_color.at(kart_num-first_loaded_f_num))); + m_ghost_karts[kart_num]->init(RaceManager::KT_GHOST); + Controller* controller = new GhostController(getGhostKart(kart_num).get(), rd.m_name_list[kart_num-first_loaded_f_num]); getGhostKart(kart_num)->setController(controller); @@ -423,7 +423,7 @@ void ReplayPlay::readKartData(FILE *fd, char *next_line, bool second_replay) kre.m_skidding_effect = skidding; kre.m_red_skidding = red_skidding!=0; kre.m_jumping = jumping != 0; - m_ghost_karts[kart_num].addReplayEvent(time, + m_ghost_karts[kart_num]->addReplayEvent(time, btTransform(q, xyz), pi, bi, kre); } else @@ -472,7 +472,7 @@ void ReplayPlay::readKartData(FILE *fd, char *next_line, bool second_replay) kre.m_skidding_effect = skidding; kre.m_red_skidding = red_skidding!=0; kre.m_jumping = jumping != 0; - m_ghost_karts[kart_num].addReplayEvent(time, + m_ghost_karts[kart_num]->addReplayEvent(time, btTransform(q, xyz), pi, bi, kre); } else diff --git a/src/replay/replay_play.hpp b/src/replay/replay_play.hpp index ad0b1a703..ec12e4c30 100644 --- a/src/replay/replay_play.hpp +++ b/src/replay/replay_play.hpp @@ -20,10 +20,10 @@ #define HEADER_REPLAY__PLAY_HPP #include "replay/replay_base.hpp" -#include "utils/ptr_vector.hpp" #include "irrString.h" #include <algorithm> +#include <memory> #include <string> #include <vector> @@ -118,7 +118,7 @@ private: std::vector<ReplayData> m_replay_file_list; /** All ghost karts. */ - PtrVector<GhostKart> m_ghost_karts; + std::vector<std::shared_ptr<GhostKart> > m_ghost_karts; ReplayPlay(); ~ReplayPlay(); @@ -162,7 +162,7 @@ public: const unsigned int getNumReplayFile() const { return (unsigned int)m_replay_file_list.size(); } // ------------------------------------------------------------------------ - GhostKart* getGhostKart(int n) { return m_ghost_karts.get(n); } + std::shared_ptr<GhostKart> getGhostKart(int n) { return m_ghost_karts[n]; } // ------------------------------------------------------------------------ const unsigned int getNumGhostKart() const { diff --git a/src/states_screens/networking_lobby.cpp b/src/states_screens/networking_lobby.cpp index 33bc0c2c6..a7b8a84cf 100644 --- a/src/states_screens/networking_lobby.cpp +++ b/src/states_screens/networking_lobby.cpp @@ -17,6 +17,7 @@ #include "states_screens/networking_lobby.hpp" +#include <cmath> #include <algorithm> #include <string> @@ -212,7 +213,7 @@ void NetworkingLobby::onUpdate(float delta) if (m_timeout_message->isVisible() && m_player_list) { float cur_player = (float)(m_player_list->getItemCount()); - if (cur_player > m_server_max_player * m_start_threshold && + if (cur_player >= m_server_max_player * m_start_threshold && m_cur_starting_timer == std::numeric_limits<float>::max()) { m_cur_starting_timer = m_start_timeout; @@ -220,7 +221,13 @@ void NetworkingLobby::onUpdate(float delta) else if (cur_player < m_server_max_player * m_start_threshold) { m_cur_starting_timer = std::numeric_limits<float>::max(); - m_timeout_message->setText(L"", true); + //I18N: In the networking lobby, display the number of players + //required to start a game for owner-less server + core::stringw msg = + _P("Game will start if there is more than %d player.", + "Game will start if there are more than %d players.", + (int)ceil(m_server_max_player * m_start_threshold) - 1); + m_timeout_message->setText(msg, true); } if (m_cur_starting_timer != std::numeric_limits<float>::max()) diff --git a/src/states_screens/race_gui_base.cpp b/src/states_screens/race_gui_base.cpp index 0db5a8265..6706c1d5f 100644 --- a/src/states_screens/race_gui_base.cpp +++ b/src/states_screens/race_gui_base.cpp @@ -1029,7 +1029,7 @@ void RaceGUIBase::drawPlungerInFace(const Camera *camera, float dt) if(m_plunger_move_time < dt && m_plunger_state!=PLUNGER_STATE_FAST) { const float fast_time = 0.3f; - if(kart->getBlockedByPlungerTicks()<fast_time) + if(kart->getBlockedByPlungerTicks()<stk_config->time2Ticks(fast_time)) { // First time we reach faste state: select random target point // at top of screen and set speed accordingly diff --git a/src/states_screens/race_gui_overworld.cpp b/src/states_screens/race_gui_overworld.cpp index 0b7405760..35ff7d434 100644 --- a/src/states_screens/race_gui_overworld.cpp +++ b/src/states_screens/race_gui_overworld.cpp @@ -189,10 +189,6 @@ void RaceGUIOverworld::renderGlobal(float dt) if (race_manager->getIfEmptyScreenSpaceExists() && !GUIEngine::ModalDialog::isADialogActive()) { - const float sqrt_num_players = - sqrtf((float)race_manager->getNumLocalPlayers()); - const int rows = (int)ceil(sqrt_num_players); - const int cols = (int)round(sqrt_num_players); static video::SColor black = video::SColor(255,0,0,0); GL32_draw2DRectangle(black, irr_driver->getSplitscreenWindow( race_manager->getNumLocalPlayers())); diff --git a/src/states_screens/tracks_screen.cpp b/src/states_screens/tracks_screen.cpp index d72106858..7862585d6 100644 --- a/src/states_screens/tracks_screen.cpp +++ b/src/states_screens/tracks_screen.cpp @@ -277,12 +277,16 @@ void TracksScreen::init() //I18N: In track screen getWidget<LabelWidget>("lap-text")->setText(_("Number of goals to win"), false); m_laps->setValue(UserConfigParams::m_num_goals); + m_laps->setMin(1); + m_laps->setMax(10); } else { //I18N: In track screen getWidget<LabelWidget>("lap-text")->setText(_("Maximum time (min.)"), false); m_laps->setValue(UserConfigParams::m_soccer_time_limit); + m_laps->setMin(1); + m_laps->setMax(15); } getWidget("reverse-text")->setVisible(true); //I18N: In track screen @@ -296,6 +300,8 @@ void TracksScreen::init() //I18N: In track screen getWidget<LabelWidget>("lap-text")->setText(_("Number of laps"), false); m_laps->setVisible(true); + m_laps->setMin(1); + m_laps->setMax(20); m_laps->setValue(UserConfigParams::m_num_laps); getWidget("reverse-text")->setVisible(true); //I18N: In track screen diff --git a/src/tracks/ambient_light_sphere.cpp b/src/tracks/ambient_light_sphere.cpp deleted file mode 100644 index bf31836c3..000000000 --- a/src/tracks/ambient_light_sphere.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009-2015 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "tracks/ambient_light_sphere.hpp" - -#include <string> -#include <stdio.h> - -#include "graphics/camera.hpp" -#include "io/xml_node.hpp" -#include "karts/abstract_kart.hpp" -#include "race/race_manager.hpp" -#include "tracks/track.hpp" - -/** Constructor for a checksphere. - * \param node XML node containing the parameters for this checkline. - */ -AmbientLightSphere::AmbientLightSphere(const XMLNode &node, - unsigned int index) - : CheckSphere(node, index) -{ - m_ambient_color = video::SColor(255, 0, 255, 0); // green - m_inner_radius2 = 1; - node.get("inner-radius", &m_inner_radius2); - m_inner_radius2 *= m_inner_radius2; // store the squared value - node.get("color", &m_ambient_color); -} // AmbientLightSphere - -// ---------------------------------------------------------------------------- -void AmbientLightSphere::update(float dt) -{ - CheckStructure::update(dt); - - for(unsigned int i=0; i<Camera::getNumCameras(); i++) - { - Camera *camera = Camera::getCamera(i); - const AbstractKart *kart=camera->getKart(); - if(!kart) continue; - if(isInside(i)) - { - float d2=getDistance2ForKart(i); - video::SColor color; - Track *track=Track::getCurrentTrack(); - if(d2<m_inner_radius2) - { // Inside inner radius --> use new ambient color - color = m_ambient_color; - } - else // Interpolate between default and this ambient color - { - float f = (getRadius2()-d2)/(getRadius2()-m_inner_radius2); - const video::SColor &def = track->getDefaultAmbientColor(); - color = m_ambient_color.getInterpolated(def, f); - } - camera->setAmbientLight(color); - } // if active - } // for i<num_karts -} // update - -// ---------------------------------------------------------------------------- -/** Only calls the sphere check if the kart is a player kart, since other - * karts won't change the ambient light. - * \param old_pos Position in previous frame. - * \param new_pos Position in current frame. - * \param indx Index of the kart, can be used to store kart specific - * additional data. - */ -bool AmbientLightSphere::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int indx) -{ - for(unsigned int i=0; i<Camera::getNumCameras(); i++) - { - if(Camera::getCamera(i)->getKart()->getWorldKartId()==indx) - return CheckSphere::isTriggered(old_pos, new_pos, indx); - } - return false; -} // isTriggered diff --git a/src/tracks/ambient_light_sphere.hpp b/src/tracks/ambient_light_sphere.hpp deleted file mode 100644 index d3dd28d93..000000000 --- a/src/tracks/ambient_light_sphere.hpp +++ /dev/null @@ -1,60 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009-2015 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef HEADER_AMBIENT_LIGHT_SPHERE_HPP -#define HEADER_AMBIENT_LIGHT_SPHERE_HPP - -#include <SColor.h> -using namespace irr; - -#include "tracks/check_sphere.hpp" - -class XMLNode; -class CheckManager; - -/** - * \brief This class implements a check sphere that is used to change the ambient - * light if a kart is inside this sphere. - * - * Besides a normal radius this sphere also has a 2nd 'inner' radius: player karts - * inside the inner radius will have the full new ambient light, karts outside the - * default light, and karts in between will mix the light dependent on distance. - * - * \ingroup tracks - */ -class AmbientLightSphere : public CheckSphere -{ -private: - /** The inner radius defines the area during which the ambient light - * is extrapolated. The square of the value specified in the scene - * file is stored. */ - float m_inner_radius2; - - /** THe full ambient color to use once the kart is inside the - * inner radius. */ - video::SColor m_ambient_color; -public: - AmbientLightSphere(const XMLNode &node, unsigned int index); - virtual ~AmbientLightSphere() {}; - virtual void update(float dt); - virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int indx); -}; // AmbientLightSphere - -#endif - diff --git a/src/tracks/check_manager.cpp b/src/tracks/check_manager.cpp index 19c9a85c3..a678aeac5 100644 --- a/src/tracks/check_manager.cpp +++ b/src/tracks/check_manager.cpp @@ -23,11 +23,11 @@ #include "io/xml_node.hpp" #include "karts/abstract_kart.hpp" -#include "tracks/ambient_light_sphere.hpp" #include "tracks/check_cannon.hpp" #include "tracks/check_goal.hpp" #include "tracks/check_lap.hpp" #include "tracks/check_line.hpp" +#include "tracks/check_sphere.hpp" #include "tracks/check_structure.hpp" #include "tracks/drive_graph.hpp" #include "utils/log.hpp" diff --git a/src/tracks/graph.cpp b/src/tracks/graph.cpp index 5ef0876dc..b53db294f 100644 --- a/src/tracks/graph.cpp +++ b/src/tracks/graph.cpp @@ -120,7 +120,7 @@ void Graph::cleanupDebugMesh() /** Creates the actual mesh that is used by createDebugMesh() or makeMiniMap() */ void Graph::createMesh(bool show_invisible, bool enable_transparency, - const video::SColor *track_color) + const video::SColor *track_color, bool invert_x_z) { #ifndef SERVER_ONLY // The debug track will not be lighted or culled. @@ -172,6 +172,18 @@ void Graph::createMesh(bool show_invisible, bool enable_transparency, differentNodeColor(count, &this_color); // Transfer the 4 points of the current quad to the list of vertices m_all_nodes[count]->getVertices(new_v+4*i, this_color); + if (invert_x_z) + { + auto* vptr = new_v + 4 * i; + vptr[0].Pos.X = -vptr[0].Pos.X; + vptr[0].Pos.Z = -vptr[0].Pos.Z; + vptr[1].Pos.X = -vptr[1].Pos.X; + vptr[1].Pos.Z = -vptr[1].Pos.Z; + vptr[2].Pos.X = -vptr[2].Pos.X; + vptr[2].Pos.Z = -vptr[2].Pos.Z; + vptr[3].Pos.X = -vptr[3].Pos.X; + vptr[3].Pos.Z = -vptr[3].Pos.Z; + } // Set up the indices for the triangles // (note, afaik with opengl we could use quads directly, but the code @@ -244,7 +256,7 @@ void Graph::createMesh(bool show_invisible, bool enable_transparency, /** Creates the actual mesh that is used by createDebugMesh() or makeMiniMap() */ void Graph::createMeshSP(bool show_invisible, bool enable_transparency, - const video::SColor *track_color) + const video::SColor *track_color, bool invert_x_z) { #ifndef SERVER_ONLY @@ -294,6 +306,18 @@ void Graph::createMeshSP(bool show_invisible, bool enable_transparency, differentNodeColor(count, &this_color); // Transfer the 4 points of the current quad to the list of vertices m_all_nodes[count]->getSPMVertices(vertices.data() + (4 * i), this_color); + if (invert_x_z) + { + auto* vptr = vertices.data() + (4 * i); + vptr[0].m_position.X = -vptr[0].m_position.X; + vptr[0].m_position.Z = -vptr[0].m_position.Z; + vptr[1].m_position.X = -vptr[1].m_position.X; + vptr[1].m_position.Z = -vptr[1].m_position.Z; + vptr[2].m_position.X = -vptr[2].m_position.X; + vptr[2].m_position.Z = -vptr[2].m_position.Z; + vptr[3].m_position.X = -vptr[3].m_position.X; + vptr[3].m_position.Z = -vptr[3].m_position.Z; + } // Set up the indices for the triangles indices[6 * i] = 4 * i + 2; // First triangle: vertex 0, 1, 2 @@ -366,7 +390,8 @@ void Graph::createMeshSP(bool show_invisible, bool enable_transparency, */ RenderTarget* Graph::makeMiniMap(const core::dimension2du &dimension, const std::string &name, - const video::SColor &fill_color) + const video::SColor &fill_color, + bool invert_x_z) { // Skip minimap when profiling if (ProfileWorld::isNoGraphics()) return NULL; @@ -384,14 +409,14 @@ RenderTarget* Graph::makeMiniMap(const core::dimension2du &dimension, if (CVS->isGLSL()) { createMeshSP(/*show_invisible part of the track*/ false, - /*enable_transparency*/ false, - /*track_color*/ &fill_color); + /*enable_transparency*/ false, /*track_color*/&fill_color, + invert_x_z); } else { createMesh(/*show_invisible part of the track*/ false, - /*enable_transparency*/ false, - /*track_color*/ &fill_color); + /*enable_transparency*/ false, /*track_color*/&fill_color, + invert_x_z); } #endif diff --git a/src/tracks/graph.hpp b/src/tracks/graph.hpp index b1079354a..b19a7de8c 100644 --- a/src/tracks/graph.hpp +++ b/src/tracks/graph.hpp @@ -93,11 +93,13 @@ private: // ------------------------------------------------------------------------ void createMesh(bool show_invisible=true, bool enable_transparency=false, - const video::SColor *track_color=NULL); + const video::SColor *track_color=NULL, + bool invert_x_z = false); // ------------------------------------------------------------------------ void createMeshSP(bool show_invisible=true, bool enable_transparency=false, - const video::SColor *track_color=NULL); + const video::SColor *track_color=NULL, + bool invert_x_z = false); // ------------------------------------------------------------------------ void cleanupDebugMesh(); // ------------------------------------------------------------------------ @@ -143,7 +145,8 @@ public: // ------------------------------------------------------------------------ RenderTarget* makeMiniMap(const core::dimension2du &dimension, const std::string &name, - const video::SColor &fill_color); + const video::SColor &fill_color, + bool invert_x_z); // ------------------------------------------------------------------------ void mapPoint2MiniMap(const Vec3 &xyz, Vec3 *out) const; // ------------------------------------------------------------------------ diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 82bf23c50..3256a0ed0 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -105,6 +105,7 @@ Track::Track(const std::string &filename) m_magic_number = 0x17AC3802; #endif + m_minimap_invert_x_z = false; m_materials_loaded = false; m_filename = filename; m_root = @@ -709,6 +710,23 @@ void Track::startMusic() const */ void Track::loadArenaGraph(const XMLNode &node) { + // Determine if rotate minimap is needed for soccer mode (for blue team) + // Only need to test local player + if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER) + { + const unsigned pk = race_manager->getNumPlayers(); + for (unsigned i = 0; i < pk; i++) + { + if (!race_manager->getKartInfo(i).isNetworkPlayer() && + race_manager->getKartInfo(i).getSoccerTeam() == + SOCCER_TEAM_BLUE) + { + m_minimap_invert_x_z = true; + break; + } + } + } + ArenaGraph* graph = new ArenaGraph(m_root+"navmesh.xml", &node); Graph::setGraph(graph); @@ -786,7 +804,15 @@ void Track::loadDriveGraph(unsigned int mode_id, const bool reverse) void Track::mapPoint2MiniMap(const Vec3 &xyz, Vec3 *draw_at) const { - Graph::get()->mapPoint2MiniMap(xyz, draw_at); + if (m_minimap_invert_x_z) + { + Vec3 invert = xyz; + invert.setX(-xyz.x()); + invert.setZ(-xyz.z()); + Graph::get()->mapPoint2MiniMap(invert, draw_at); + } + else + Graph::get()->mapPoint2MiniMap(xyz, draw_at); draw_at->setX(draw_at->getX() * m_minimap_x_scale); draw_at->setY(draw_at->getY() * m_minimap_y_scale); } @@ -1115,7 +1141,9 @@ void Track::loadMinimap() m_mini_map_size = World::getWorld()->getRaceGUI()->getMiniMapSize(); //Use twice the size of the rendered minimap to reduce significantly aliasing - m_render_target = Graph::get()->makeMiniMap(m_mini_map_size*2, "minimap::" + m_ident, video::SColor(127, 255, 255, 255)); + m_render_target = Graph::get()->makeMiniMap(m_mini_map_size * 2, + "minimap::" + m_ident, video::SColor(127, 255, 255, 255), + m_minimap_invert_x_z); if (!m_render_target) return; core::dimension2du mini_map_texture_size = m_render_target->getTextureSize(); @@ -1737,6 +1765,7 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) } CameraEnd::clearEndCameras(); + m_minimap_invert_x_z = false; m_sky_type = SKY_NONE; m_track_object_manager = new TrackObjectManager(); diff --git a/src/tracks/track.hpp b/src/tracks/track.hpp index e4547d592..d91f4ef55 100644 --- a/src/tracks/track.hpp +++ b/src/tracks/track.hpp @@ -365,6 +365,8 @@ private: float m_displacement_speed; int m_physical_object_uid; + bool m_minimap_invert_x_z; + /** The levels for color correction * m_color_inlevel(black, gamma, white) * m_color_outlevel(black, white)*/ diff --git a/src/tracks/track_object.cpp b/src/tracks/track_object.cpp index 58b9636ac..a4fc0f856 100644 --- a/src/tracks/track_object.cpp +++ b/src/tracks/track_object.cpp @@ -74,7 +74,6 @@ TrackObject::TrackObject(const core::vector3df& xyz, const core::vector3df& hpr, m_enabled = true; m_presentation = NULL; m_animator = NULL; - m_physical_object = NULL; m_parent_library = NULL; m_interaction = interaction; m_presentation = presentation; @@ -86,9 +85,8 @@ TrackObject::TrackObject(const core::vector3df& xyz, const core::vector3df& hpr, if (m_interaction != "ghost" && m_interaction != "none" && physics_settings ) { - m_physical_object = new PhysicalObject(is_dynamic, - *physics_settings, - this); + m_physical_object = std::make_shared<PhysicalObject> + (is_dynamic, *physics_settings, this); } reset(); @@ -112,7 +110,6 @@ void TrackObject::init(const XMLNode &xml_node, scene::ISceneNode* parent, m_presentation = NULL; m_animator = NULL; m_parent_library = parent_library; - m_physical_object = NULL; xml_node.get("id", &m_id ); xml_node.get("model", &m_name ); @@ -455,7 +452,6 @@ TrackObject::~TrackObject() { delete m_presentation; delete m_animator; - delete m_physical_object; } // ~TrackObject // ---------------------------------------------------------------------------- @@ -482,7 +478,7 @@ void TrackObject::setEnabled(bool enabled) if (getType() == "mesh") { - if (m_physical_object != NULL) + if (m_physical_object) { if (enabled) m_physical_object->addBody(); @@ -508,7 +504,7 @@ void TrackObject::resetEnabled() if (getType() == "mesh") { - if (m_physical_object != NULL) + if (m_physical_object) { if (m_initially_visible) m_physical_object->addBody(); @@ -581,7 +577,7 @@ void TrackObject::move(const core::vector3df& xyz, const core::vector3df& hpr, if (m_presentation != NULL) m_presentation->move(xyz, hpr, scale, isAbsoluteCoord); - if (update_rigid_body && m_physical_object != NULL) + if (update_rigid_body && m_physical_object) { movePhysicalBodyToGraphicalNode(xyz, hpr); } diff --git a/src/tracks/track_object.hpp b/src/tracks/track_object.hpp index 65905a040..e16b302cb 100644 --- a/src/tracks/track_object.hpp +++ b/src/tracks/track_object.hpp @@ -86,7 +86,7 @@ protected: /** True if a kart can drive on this object. This will */ bool m_is_driveable; - PhysicalObject* m_physical_object; + std::shared_ptr<PhysicalObject> m_physical_object; ThreeDAnimation* m_animator; @@ -164,9 +164,10 @@ public: // ------------------------------------------------------------------------ bool isSoccerBall() const { return m_soccer_ball; } // ------------------------------------------------------------------------ - const PhysicalObject* getPhysicalObject() const { return m_physical_object; } + const PhysicalObject* getPhysicalObject() const + { return m_physical_object.get(); } // ------------------------------------------------------------------------ - PhysicalObject* getPhysicalObject() { return m_physical_object; } + PhysicalObject* getPhysicalObject() { return m_physical_object.get(); } // ------------------------------------------------------------------------ const core::vector3df getInitXYZ() const { return m_init_xyz; } // ------------------------------------------------------------------------ @@ -214,7 +215,7 @@ public: /** Get the physics representation of an object. * On the script side, the returned object is of type : @ref Scripting_PhysicalObject */ - PhysicalObject* getPhysics() { return m_physical_object; } + PhysicalObject* getPhysics() { return m_physical_object.get(); } /** Hide or show the object */ void setEnabled(bool mode); diff --git a/src/tracks/track_sector.cpp b/src/tracks/track_sector.cpp index b57fe5fb5..5f131e7eb 100644 --- a/src/tracks/track_sector.cpp +++ b/src/tracks/track_sector.cpp @@ -74,18 +74,21 @@ void TrackSector::update(const Vec3 &xyz, bool ignore_vertical) prev_sector, test_nodes, ignore_vertical); } - // ArenaGraph (battle and soccer mode) doesn't need the code below - if (ag) return; + // Keep the last valid graph node for arena mode + if (ag) + { + if (prev_sector != Graph::UNKNOWN_SECTOR) + m_last_valid_graph_node = prev_sector; + return; + } // keep the current quad as the latest valid one IF the player has one // of the required checklines const DriveNode* dn = DriveGraph::get()->getNode(m_current_graph_node); const std::vector<int>& checkline_requirements = dn->getChecklineRequirements(); - bool isValidQuad = false; if (checkline_requirements.size() == 0) { - isValidQuad = true; if (m_on_road) m_last_valid_graph_node = m_current_graph_node; } @@ -98,7 +101,6 @@ void TrackSector::update(const Vec3 &xyz, bool ignore_vertical) //has_prerequisite = true; if (m_on_road) m_last_valid_graph_node = m_current_graph_node; - isValidQuad = true; break; } } diff --git a/src/tracks/track_sector.hpp b/src/tracks/track_sector.hpp index 08e60ef32..ed1ee45f4 100644 --- a/src/tracks/track_sector.hpp +++ b/src/tracks/track_sector.hpp @@ -81,6 +81,8 @@ public: bool isOnRoad() const { return m_on_road; } // ------------------------------------------------------------------------ void setLastTriggeredCheckline(int i) { m_last_triggered_checkline = i; } + // ------------------------------------------------------------------------ + int getLastValidGraphNode() const { return m_last_valid_graph_node; } }; // TrackSector