diff --git a/src/karts/controller/player_controller.cpp b/src/karts/controller/player_controller.cpp index 6de9e3d07..a844c6c3a 100644 --- a/src/karts/controller/player_controller.cpp +++ b/src/karts/controller/player_controller.cpp @@ -328,13 +328,6 @@ void PlayerController::update(int ticks) { steer(ticks, m_steer_val); - if (World::getWorld()->getPhase() == World::GOAL_PHASE) - { - m_controls->setBrake(false); - m_controls->setAccel(0.0f); - return; - } - if (World::getWorld()->isStartPhase()) { if (m_controls->getAccel() || m_controls->getBrake()|| diff --git a/src/modes/soccer_world.cpp b/src/modes/soccer_world.cpp index ca87ec540..6bd44f053 100644 --- a/src/modes/soccer_world.cpp +++ b/src/modes/soccer_world.cpp @@ -32,7 +32,10 @@ #include "karts/controller/local_player_controller.hpp" #include "karts/controller/network_player_controller.hpp" #include "network/network_config.hpp" +#include "network/network_string.hpp" +#include "network/protocols/game_events_protocol.hpp" #include "network/rewind_manager.hpp" +#include "network/stk_host.hpp" #include "physics/physics.hpp" #include "states_screens/race_gui_base.hpp" #include "tracks/track.hpp" @@ -146,7 +149,8 @@ void SoccerWorld::reset() m_blue_kdm.clear(); m_ball_heading = 0.0f; m_ball_invalid_timer = 0; - + m_goal_transforms.clear(); + m_goal_transforms.resize(m_karts.size()); if (m_goal_sound != NULL && m_goal_sound->getStatus() == SFXBase::SFX_PLAYING) { @@ -158,9 +162,11 @@ void SoccerWorld::reset() m_ball_track_sector->reset(); } + m_reset_ball_ticks = -1; initKartList(); m_ball->reset(); m_bgd.reset(); + m_ball->setEnabled(false); // Make the player kart in profiling mode up // ie make this kart less likely to affect gaming result @@ -169,6 +175,27 @@ void SoccerWorld::reset() } // reset +//----------------------------------------------------------------------------- +void SoccerWorld::onGo() +{ + m_ball->setEnabled(true); + m_ball->reset(); + WorldWithRank::onGo(); +} // onGo + +//----------------------------------------------------------------------------- +void SoccerWorld::terminateRace() +{ + const unsigned int kart_amount = getNumKarts(); + for (unsigned int i = 0; i < kart_amount ; i++) + { + // Soccer mode use goal for race result, and each goal time is + // handled by handlePlayerGoalFromServer already + m_karts[i]->finishedRace(0.0f, true/*from_server*/); + } // isetVelocity(btVector3(0, 0, 0)); + AbstractKart* kart = m_karts[i]; + if (kart->isEliminated()) + continue; + kart->getBody()->setLinearVelocity(Vec3(0.0f)); + kart->getBody()->setAngularVelocity(Vec3(0.0f)); + kart->getBody()->proceedToTransform(m_goal_transforms[i]); + kart->setTrans(m_goal_transforms[i]); } - m_goal_timer += ticks; + if (NetworkConfig::get()->isNetworking() && + NetworkConfig::get()->isClient()) + return; + + m_goal_timer += ticks; if (m_goal_timer > stk_config->time2Ticks(3.0f)) { - setPhase(WorldStatus::RACE_PHASE); m_goal_timer = 0; if (!isRaceOver()) { - // Reset all karts - for (unsigned int i = 0; i < m_karts.size(); i++) - moveKartAfterRescue(m_karts[i]); + // Reset all karts and ball + resetKartsToSelfGoals(); if (UserConfigParams::m_arena_ai_stats) getKart(8)->flyUp(); } @@ -226,11 +259,14 @@ void SoccerWorld::update(int ticks) //----------------------------------------------------------------------------- void SoccerWorld::onCheckGoalTriggered(bool first_goal) { - if (isRaceOver() || isStartPhase()) + if (isRaceOver() || isStartPhase() || + (NetworkConfig::get()->isNetworking() && + NetworkConfig::get()->isClient())) return; setPhase(WorldStatus::GOAL_PHASE); m_goal_sound->play(); + m_ball->setEnabled(false); if (m_ball_hitter != -1) { if (UserConfigParams::m_arena_ai_stats) @@ -256,6 +292,7 @@ void SoccerWorld::onCheckGoalTriggered(bool first_goal) ->setAnimation(KartModel::AF_LOSE_START, true/* play_non_loop*/); } + float score_time = 0.0f; if (first_goal) { // Notice: true first_goal means it's blue goal being shoot, @@ -263,28 +300,146 @@ void SoccerWorld::onCheckGoalTriggered(bool first_goal) m_red_scorers.push_back(sd); if (race_manager->hasTimeTarget()) { - m_red_score_times.push_back(race_manager->getTimeTarget() - - getTime()); + score_time = race_manager->getTimeTarget() - getTime(); } else - m_red_score_times.push_back(getTime()); + score_time = getTime(); + m_red_score_times.push_back(score_time); } else { m_blue_scorers.push_back(sd); if (race_manager->hasTimeTarget()) { - m_blue_score_times.push_back(race_manager->getTimeTarget() - - getTime()); + score_time = race_manager->getTimeTarget() - getTime(); } else - m_blue_score_times.push_back(getTime()); + score_time = getTime(); + m_blue_score_times.push_back(score_time); + } + if (NetworkConfig::get()->isNetworking() && + NetworkConfig::get()->isServer()) + { + NetworkString p(PROTOCOL_GAME_EVENTS); + p.setSynchronous(true); + p.addUInt8(GameEventsProtocol::GE_PLAYER_GOAL) + .addUInt8((uint8_t)sd.m_id).addUInt8(sd.m_correct_goal) + .addUInt8(first_goal).addFloat(score_time) + .addTime(World::getWorld()->getTicksSinceStart() + + stk_config->time2Ticks(3.0f)); + STKHost::get()->sendPacketToAllPeers(&p, true); } } - m_ball->reset(); + for (unsigned i = 0; i < m_karts.size(); i++) + { + AbstractKart* kart = m_karts[i]; + if (kart->isEliminated()) + continue; + kart->getBody()->setLinearVelocity(Vec3(0.0f)); + kart->getBody()->setAngularVelocity(Vec3(0.0f)); + m_goal_transforms[i] = kart->getBody()->getWorldTransform(); + } } // onCheckGoalTriggered +//----------------------------------------------------------------------------- +void SoccerWorld::handleResetBallFromServer(const NetworkString& ns) +{ + int ticks_now = World::getWorld()->getTicksSinceStart(); + int ticks_back_to_own_goal = ns.getTime(); + if (ticks_now >= ticks_back_to_own_goal) + { + Log::warn("SoccerWorld", "Server ticks %d is too close to client ticks " + "%d when reset player", ticks_back_to_own_goal, ticks_now); + return; + } + RewindManager::get()->getRewindQueue().insertRewindInfo(new + RewindInfoEventFunction(ticks_back_to_own_goal, + [](){}, std::bind(&SoccerWorld::resetKartsToSelfGoals, this))); + +} // handleResetBallFromServer + +//----------------------------------------------------------------------------- +void SoccerWorld::handlePlayerGoalFromServer(const NetworkString& ns) +{ + ScorerData sd; + sd.m_id = ns.getUInt8(); + sd.m_correct_goal = ns.getUInt8() == 1; + bool first_goal = ns.getUInt8() == 1; + float score_time = ns.getFloat(); + if (sd.m_correct_goal) + { + m_karts[sd.m_id]->getKartModel() + ->setAnimation(KartModel::AF_WIN_START, true/* play_non_loop*/); + } + else if (!sd.m_correct_goal) + { + m_karts[sd.m_id]->getKartModel() + ->setAnimation(KartModel::AF_LOSE_START, true/* play_non_loop*/); + } + + if (first_goal) + { + m_red_scorers.push_back(sd); + m_red_score_times.push_back(score_time); + } + else + { + m_blue_scorers.push_back(sd); + m_blue_score_times.push_back(score_time); + } + int ticks_now = World::getWorld()->getTicksSinceStart(); + int ticks_back_to_own_goal = ns.getTime(); + + if (ticks_now >= ticks_back_to_own_goal) + { + Log::warn("SoccerWorld", "Server ticks %d is too close to client ticks " + "%d when goal", ticks_back_to_own_goal, ticks_now); + return; + } + + setPhase(WorldStatus::GOAL_PHASE); + m_goal_sound->play(); + m_ball->setEnabled(false); + for (unsigned i = 0; i < m_karts.size(); i++) + { + AbstractKart* kart = m_karts[i]; + if (kart->isEliminated()) + continue; + btTransform transform_now = kart->getBody()->getWorldTransform(); + kart->getBody()->setLinearVelocity(Vec3(0.0f)); + kart->getBody()->setAngularVelocity(Vec3(0.0f)); + kart->getBody()->proceedToTransform(transform_now); + kart->setTrans(transform_now); + m_goal_transforms[i] = transform_now; + } + RewindManager::get()->getRewindQueue().insertRewindInfo(new + RewindInfoEventFunction(ticks_back_to_own_goal, + [](){}, std::bind(&SoccerWorld::resetKartsToSelfGoals, this))); + +} // handlePlayerGoalFromServer + +//----------------------------------------------------------------------------- +void SoccerWorld::resetKartsToSelfGoals() +{ + m_ball->setEnabled(true); + m_ball->reset(); + m_bgd.resetCheckGoal(Track::getCurrentTrack()); + setPhase(WorldStatus::RACE_PHASE); + for (unsigned i = 0; i < m_karts.size(); i++) + { + AbstractKart* kart = m_karts[i]; + if (kart->isEliminated()) + continue; + + kart->getBody()->setLinearVelocity(Vec3(0.0f)); + kart->getBody()->setAngularVelocity(Vec3(0.0f)); + unsigned index = m_kart_position_map.at(kart->getWorldKartId()); + btTransform t = Track::getCurrentTrack()->getStartTransform(index); + moveKartTo(kart, t); + } +} // resetKartsToSelfGoals + //----------------------------------------------------------------------------- /** Sets the last kart that hit the ball, to be able to * identify the scorer later. @@ -299,16 +454,15 @@ void SoccerWorld::setBallHitter(unsigned int kart_id) */ bool SoccerWorld::isRaceOver() { - - if(race_manager->hasTimeTarget()) + if (race_manager->hasTimeTarget()) { return m_count_down_reached_zero; } // One team scored the target goals ... else { - return (getScore(SOCCER_TEAM_BLUE) >= m_goal_target || - getScore(SOCCER_TEAM_RED) >= m_goal_target); + return (getScore(SOCCER_TEAM_BLUE) >= m_goal_target || + getScore(SOCCER_TEAM_RED) >= m_goal_target); } } // isRaceOver @@ -492,22 +646,46 @@ void SoccerWorld::updateBallPosition(int ticks) { m_ball_track_sector ->update(getBallPosition(), true/*ignore_vertical*/); - if (!m_ball_track_sector->isOnRoad() && getPhase() == RACE_PHASE) + if (!m_ball_track_sector->isOnRoad() && getPhase() == RACE_PHASE && + m_reset_ball_ticks == -1) { m_ball_invalid_timer += ticks; // Reset the ball and karts if out of navmesh after 2 seconds if (m_ball_invalid_timer >= stk_config->time2Ticks(2.0f)) { - m_ball_invalid_timer = 0; - m_ball->reset(); - for (unsigned int i = 0; i < m_karts.size(); i++) - moveKartAfterRescue(m_karts[i]); - if (UserConfigParams::m_arena_ai_stats) - getKart(8)->flyUp(); + if (NetworkConfig::get()->isNetworking() && + NetworkConfig::get()->isServer()) + { + // Reset the ball 2 seconds in the future to make sure it's + // after all clients time + m_reset_ball_ticks = + World::getWorld()->getTicksSinceStart() + + stk_config->time2Ticks(2.0f); + + NetworkString p(PROTOCOL_GAME_EVENTS); + p.setSynchronous(true); + p.addUInt8(GameEventsProtocol::GE_RESET_BALL) + .addTime(m_reset_ball_ticks); + STKHost::get()->sendPacketToAllPeers(&p, true); + } + else if (!NetworkConfig::get()->isNetworking()) + { + m_ball_invalid_timer = 0; + resetKartsToSelfGoals(); + if (UserConfigParams::m_arena_ai_stats) + getKart(8)->flyUp(); + } } } else m_ball_invalid_timer = 0; + if (m_reset_ball_ticks == World::getWorld()->getTicksSinceStart()) + { + assert(NetworkConfig::get()->isNetworking() && + NetworkConfig::get()->isServer()); + resetKartsToSelfGoals(); + m_reset_ball_ticks = -1; + } } } // updateBallPosition diff --git a/src/modes/soccer_world.hpp b/src/modes/soccer_world.hpp index ac31d03b1..3fe790aa5 100644 --- a/src/modes/soccer_world.hpp +++ b/src/modes/soccer_world.hpp @@ -30,6 +30,7 @@ class AbstractKart; class Controller; +class NetworkString; class TrackObject; class TrackSector; @@ -248,7 +249,11 @@ private: return (reverse ? m_trans(Vec3(-x, 0, -y)) : m_trans(Vec3(x, 0, y))); } // getAimPosition - + void resetCheckGoal(const Track* t) + { + m_red_check_goal->reset(*t); + m_blue_check_goal->reset(*t); + } }; // BallGoalData std::vector m_red_kdm; @@ -290,6 +295,7 @@ private: float m_ball_heading; + std::vector m_goal_transforms; /** Set the team for the karts */ void initKartList(); /** Function to update the location the ball on the polygon map */ @@ -303,16 +309,21 @@ private: int m_frame_count; std::vector m_goal_frame; + int m_reset_ball_ticks; + void resetKartsToSelfGoals(); + public: SoccerWorld(); virtual ~SoccerWorld(); virtual void init() OVERRIDE; + virtual void onGo() OVERRIDE; // clock events virtual bool isRaceOver() OVERRIDE; virtual void countdownReachedZero() OVERRIDE; + virtual void terminateRace() OVERRIDE; // overriding World methods virtual void reset() OVERRIDE; @@ -394,6 +405,9 @@ public: // ------------------------------------------------------------------------ void setAITeam(); // ------------------------------------------------------------------------ + void handlePlayerGoalFromServer(const NetworkString& ns); + // ------------------------------------------------------------------------ + void handleResetBallFromServer(const NetworkString& ns); }; // SoccerWorld diff --git a/src/network/protocols/game_events_protocol.cpp b/src/network/protocols/game_events_protocol.cpp index 1f50402f7..f7f8f7ebc 100644 --- a/src/network/protocols/game_events_protocol.cpp +++ b/src/network/protocols/game_events_protocol.cpp @@ -1,17 +1,18 @@ #include "network/protocols/game_events_protocol.hpp" #include "karts/abstract_kart.hpp" -#include "modes/world.hpp" +#include "modes/soccer_world.hpp" #include "network/event.hpp" #include "network/game_setup.hpp" #include "network/network_config.hpp" +#include "network/rewind_manager.hpp" #include "network/stk_host.hpp" #include "network/stk_peer.hpp" #include /** This class handles all 'major' game events. E.g. - * finishing a race etc. The game events manager is notified from the + * finishing a race or goal etc. The game events manager is notified from the * game code, and it calls the corresponding function in this class. * The server then notifies all clients. Clients receive the message * in the synchronous notifyEvent function here, decode the message @@ -43,12 +44,27 @@ bool GameEventsProtocol::notifyEvent(Event* event) return true; } uint8_t type = data.getUInt8(); + SoccerWorld* sw = dynamic_cast(World::getWorld()); switch (type) { case GE_KART_FINISHED_RACE: kartFinishedRace(data); break; case GE_PLAYER_DISCONNECT: eliminatePlayer(data); break; + case GE_RESET_BALL: + { + if (!sw) + throw("No soccer world"); + sw->handleResetBallFromServer(data); + break; + } + case GE_PLAYER_GOAL: + { + if (!sw) + throw("No soccer world"); + sw->handlePlayerGoalFromServer(data); + break; + } default: Log::warn("GameEventsProtocol", "Unkown message type."); break; diff --git a/src/network/protocols/game_events_protocol.hpp b/src/network/protocols/game_events_protocol.hpp index 987a47204..a735887da 100644 --- a/src/network/protocols/game_events_protocol.hpp +++ b/src/network/protocols/game_events_protocol.hpp @@ -12,7 +12,9 @@ public: enum GameEventType : uint8_t { GE_KART_FINISHED_RACE = 1, - GE_PLAYER_DISCONNECT = 2 + GE_PLAYER_DISCONNECT = 2, + GE_RESET_BALL = 3, + GE_PLAYER_GOAL = 4 }; // GameEventType private: void eliminatePlayer(const NetworkString &ns);