// SuperTuxKart - a fun racing game with go-kart // Copyright (C) 2015 Supertuxkart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 3 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "network/protocols/game_protocol.hpp" #include "items/item_manager.hpp" #include "items/network_item_manager.hpp" #include "karts/abstract_kart.hpp" #include "karts/controller/player_controller.hpp" #include "modes/world.hpp" #include "network/event.hpp" #include "network/network_config.hpp" #include "network/game_setup.hpp" #include "network/network_config.hpp" #include "network/network_string.hpp" #include "network/protocol_manager.hpp" #include "network/rewind_info.hpp" #include "network/rewind_manager.hpp" #include "network/stk_host.hpp" #include "network/stk_peer.hpp" #include "utils/log.hpp" #include "utils/time.hpp" #include "main_loop.hpp" // ============================================================================ std::weak_ptr GameProtocol::m_game_protocol; // ============================================================================ std::shared_ptr GameProtocol::createInstance() { if (!emptyInstance()) { Log::fatal("GameProtocol", "Create only 1 instance of GameProtocol!"); return NULL; } auto gm = std::make_shared(); m_game_protocol = gm; return gm; } // createInstance //----------------------------------------------------------------------------- /** Constructor. Allocates the buffer for events to send to the server. */ GameProtocol::GameProtocol() : Protocol( PROTOCOL_CONTROLLER_EVENTS) { m_data_to_send = getNetworkString(); } // GameProtocol //----------------------------------------------------------------------------- GameProtocol::~GameProtocol() { delete m_data_to_send; } // ~GameProtocol //----------------------------------------------------------------------------- /** Synchronous update - will send all commands collected during the last * frame (and could optional only send messages every N frames). */ void GameProtocol::sendActions() { if (m_all_actions.size() == 0) return; // nothing to do // Clear left-over data from previous frame. This way the network // string will increase till it reaches maximum size necessary m_data_to_send->clear(); m_data_to_send->addUInt8(GP_CONTROLLER_ACTION) .addUInt8(uint8_t(m_all_actions.size())); // Add all actions for (auto a : m_all_actions) { if (Network::m_connection_debug) { Log::verbose("GameProtocol", "Controller action: %d %d %d %d %d %d", a.m_ticks, a.m_kart_id, a.m_action, a.m_value, a.m_value_l, a.m_value_r); } m_data_to_send->addUInt32(a.m_ticks); m_data_to_send->addUInt8(a.m_kart_id); m_data_to_send->addUInt8((uint8_t)(a.m_action)).addUInt32(a.m_value) .addUInt32(a.m_value_l).addUInt32(a.m_value_r); } // for a in m_all_actions // FIXME: for now send reliable sendToServer(m_data_to_send, /*reliable*/ true); m_all_actions.clear(); } // sendActions //----------------------------------------------------------------------------- /** Called when a message from a remote GameProtocol is received. */ bool GameProtocol::notifyEventAsynchronous(Event* event) { if(!checkDataSize(event, 1)) return true; NetworkString &data = event->data(); uint8_t message_type = data.getUInt8(); switch (message_type) { case GP_CONTROLLER_ACTION: handleControllerAction(event); break; case GP_STATE: handleState(event); break; case GP_ADJUST_TIME: handleAdjustTime(event); break; //case GP_ITEM_UPDATE: handleItemUpdate(event); break; case GP_ITEM_CONFIRMATION: handleItemEventConfirmation(event); break; default: Log::error("GameProtocol", "Received unknown message type %d - ignored.", message_type); break; } // switch message_type return true; } // notifyEventAsynchronous //----------------------------------------------------------------------------- /** Called from the local kart controller when an action (like steering, * acceleration, ...) was triggered. It sends a message with the new info * to the server and informs the rewind manager to store the event. * \param Kart id that triggered the action. * \param action Which action was triggered. * \param value New value for the given action. */ void GameProtocol::controllerAction(int kart_id, PlayerAction action, int value, int val_l, int val_r) { // Store the action in the list of actions that will be sent to the // server next. assert(NetworkConfig::get()->isClient()); Action a; a.m_kart_id = kart_id; a.m_action = action; a.m_value = value; a.m_value_l = val_l; a.m_value_r = val_r; a.m_ticks = World::getWorld()->getTicksSinceStart(); m_all_actions.push_back(a); // Store the event in the rewind manager, which is responsible // for freeing the allocated memory BareNetworkString *s = new BareNetworkString(4); s->addUInt8(kart_id).addUInt8(action).addUInt32(value) .addUInt32(val_l).addUInt32(val_r); RewindManager::get()->addEvent(this, s, /*confirmed*/true, World::getWorld()->getTicksSinceStart()); } // controllerAction // ---------------------------------------------------------------------------- /** Called when a controller event is received - either on the server from * a client, or on a client from the server. It sorts the event into the * RewindManager's network event queue. The server will also send this * event immediately to all clients (except to the original sender). */ void GameProtocol::handleControllerAction(Event *event) { NetworkString &data = event->data(); uint8_t count = data.getUInt8(); bool will_trigger_rewind = false; //int rewind_delta = 0; int cur_ticks = 0; const int not_rewound = RewindManager::get()->getNotRewoundWorldTicks(); for (unsigned int i = 0; i < count; i++) { cur_ticks = data.getUInt32(); // Since this is running in a thread, it might be called during // a rewind, i.e. with an incorrect world time. So the event // time needs to be compared with the World time independent // of any rewinding. if (cur_ticks < not_rewound && !will_trigger_rewind) { will_trigger_rewind = true; //rewind_delta = not_rewound - cur_ticks; } uint8_t kart_id = data.getUInt8(); if (NetworkConfig::get()->isServer() && !event->getPeer()->availableKartID(kart_id)) { Log::warn("GameProtocol", "Wrong kart id %d from %s.", kart_id, event->getPeer()->getAddress().toString().c_str()); return; } PlayerAction action = (PlayerAction)(data.getUInt8()); int value = data.getUInt32(); int value_l = data.getUInt32(); int value_r = data.getUInt32(); if (Network::m_connection_debug) { Log::verbose("GameProtocol", "Controller action: %d %d %d %d %d %d", cur_ticks, kart_id, action, value, value_l, value_r); } BareNetworkString *s = new BareNetworkString(3); s->addUInt8(kart_id).addUInt8(action).addUInt32(value) .addUInt32(value_l).addUInt32(value_r); RewindManager::get()->addNetworkEvent(this, s, cur_ticks); } if (data.size() > 0) { Log::warn("GameProtocol", "Received invalid controller data - remains %d",data.size()); } if (NetworkConfig::get()->isServer()) { // Send update to all clients except the original sender if the event // is after the server time if (!will_trigger_rewind) STKHost::get()->sendPacketExcept(event->getPeer(), &data, false); // FIXME unless there is a network jitter more than 100ms (more than // server delay), time adjust is not necessary /*if (not_rewound == 0 || m_initial_ticks.find(event->getPeer()) == m_initial_ticks.end()) return; int cur_diff = cur_ticks - not_rewound; const int max_adjustment = 12; const int ticks_difference = m_initial_ticks.at(event->getPeer()); if (will_trigger_rewind) { rewind_delta += max_adjustment; Log::info("GameProtocol", "At %d %f %d requesting time adjust" " (speed up) of %d for host %d", World::getWorld()->getTicksSinceStart(), StkTime::getRealTime(), not_rewound, rewind_delta, event->getPeer()->getHostId()); // This message from a client triggered a rewind in the server. // To avoid this, signal to the client that it should speed up. adjustTimeForClient(event->getPeer(), rewind_delta); return; } if (cur_diff > 0 && cur_diff - ticks_difference > max_adjustment) { const int adjustment = ticks_difference - cur_diff; Log::info("GameProtocol", "At %d %f %d requesting time adjust" " (slow down) of %d for host %d", World::getWorld()->getTicksSinceStart(), StkTime::getRealTime(), not_rewound, adjustment, event->getPeer()->getHostId()); adjustTimeForClient(event->getPeer(), adjustment); }*/ } // if server } // handleControllerAction // ---------------------------------------------------------------------------- /** The server might request that a client adjusts its world clock (in order to * reduce rewinds). This function sends a a (unreliable) message to the * client. * \param peer The peer that triggered the rewind. * \param t Time that the peer needs to slowdown (<0) or speed up(>0). */ void GameProtocol::adjustTimeForClient(STKPeer *peer, int ticks) { assert(NetworkConfig::get()->isServer()); if (m_last_adjustments.find(peer) != m_last_adjustments.end() && StkTime::getRealTime() - m_last_adjustments.at(peer) < 3.0) return; NetworkString *ns = getNetworkString(5); ns->addUInt8(GP_ADJUST_TIME).addUInt32(ticks); // This message can be send unreliable, it's not critical if it doesn't // get delivered, the server will request again later anyway. peer->sendPacket(ns, /*reliable*/false); m_last_adjustments[peer] = StkTime::getRealTime(); delete ns; } // adjustTimeForClient // ---------------------------------------------------------------------------- /** Called on a client when the server requests an adjustment of this client's * world clock time (in order to reduce rewinds). */ void GameProtocol::handleAdjustTime(Event *event) { int ticks = event->data().getUInt32(); main_loop->setTicksAdjustment(ticks); Log::verbose("GameProtocol", "%d ticks adjustment", ticks); } // handleAdjustTime // ---------------------------------------------------------------------------- /** Sends a confirmation to the server that all item events up to 'ticks' * have been received. * \param ticks Up to which time in ticks the item events have been received. */ void GameProtocol::sendItemEventConfirmation(int ticks) { assert(NetworkConfig::get()->isClient()); NetworkString *ns = getNetworkString(5); ns->addUInt8(GP_ITEM_CONFIRMATION).addUInt32(ticks); // This message can be sent unreliable, it's not critical if it doesn't // get delivered, a future update will come through sendToServer(ns, /*reliable*/false); delete ns; } // sendItemEventConfirmation // ---------------------------------------------------------------------------- /** Handles an item even confirmation from a client. Once it has been confirmed * that all clients have received certain events, those can be deleted and * do not need to be sent again. * \param event The data from the client. */ void GameProtocol::handleItemEventConfirmation(Event *event) { assert(NetworkConfig::get()->isServer()); int ticks = event->data().getTime(); NetworkItemManager::get()->setItemConfirmationTime(event->getPeerSP(), ticks); } // handleItemEventConfirmation // ---------------------------------------------------------------------------- /** Called by the server before assembling a new message containing the full * state of the race to be sent to a client. */ void GameProtocol::startNewState() { assert(NetworkConfig::get()->isServer()); m_data_to_send->clear(); m_data_to_send->addUInt8(GP_STATE) .addUInt32(World::getWorld()->getTicksSinceStart()); } // startNewState // ---------------------------------------------------------------------------- /** Called by a server to add data to the current state. The data in buffer * is copied, so the data can be freed after this call/. * \param buffer Adds the data in the buffer to the current state. */ void GameProtocol::addState(BareNetworkString *buffer) { assert(NetworkConfig::get()->isServer()); m_data_to_send->addUInt16(buffer->size()); (*m_data_to_send) += *buffer; } // addState // ---------------------------------------------------------------------------- /** Called by a server to finalize the current state, which add updated * names of rewinder using to the beginning of state buffer * \param cur_rewinder List of current rewinder using. */ void GameProtocol::finalizeState(std::vector& cur_rewinder) { assert(NetworkConfig::get()->isServer()); auto& buffer = m_data_to_send->getBuffer(); auto pos = buffer.begin() + 1/*protocol type*/ + 1 /*gp event type*/+ 4/*time*/; m_data_to_send->reset(); std::vector names; names.push_back((uint8_t)cur_rewinder.size()); for (std::string& name : cur_rewinder) { names.push_back((uint8_t)name.size()); std::vector rewinder(name.begin(), name.end()); names.insert(names.end(), rewinder.begin(), rewinder.end()); } buffer.insert(pos, names.begin(), names.end()); } // finalizeState // ---------------------------------------------------------------------------- /** Called when the last state information has been added and the message * can be sent to the clients. */ void GameProtocol::sendState() { assert(NetworkConfig::get()->isServer()); sendMessageToPeers(m_data_to_send, /*reliable*/false); } // sendState // ---------------------------------------------------------------------------- /** Called when a new full state is received form the server. */ void GameProtocol::handleState(Event *event) { // Ignore events arriving when client has already exited if (!World::getWorld()) return; assert(NetworkConfig::get()->isClient()); NetworkString &data = event->data(); int ticks = data.getUInt32(); // Check for updated rewinder using unsigned rewinder_size = data.getUInt8(); std::vector rewinder_using; for (unsigned i = 0; i < rewinder_size; i++) { std::string name; data.decodeString(&name); rewinder_using.push_back(name); } // The memory for bns will be handled in the RewindInfoState object RewindInfoState* ris = new RewindInfoState(ticks, data.getCurrentOffset(), rewinder_using, data.getBuffer()); RewindManager::get()->addNetworkRewindInfo(ris); } // handleState // ---------------------------------------------------------------------------- /** Called from the RewindManager when rolling back. * \param buffer Pointer to the saved state information. */ void GameProtocol::undo(BareNetworkString *buffer) { } // undo // ---------------------------------------------------------------------------- /** Called from the RewindManager after a rollback to replay the stored * events. * \param buffer Pointer to the saved state information. */ void GameProtocol::rewind(BareNetworkString *buffer) { int kart_id = buffer->getUInt8(); PlayerAction action = PlayerAction(buffer->getUInt8()); int value = buffer->getUInt32(); int value_l = buffer->getUInt32(); int value_r = buffer->getUInt32(); Controller *c = World::getWorld()->getKart(kart_id)->getController(); PlayerController *pc = dynamic_cast(c); // This can be endcontroller when finishing the race if (pc) pc->actionFromNetwork(action, value, value_l, value_r); } // rewind // ---------------------------------------------------------------------------- void GameProtocol::addInitialTicks(STKPeer* p, int ticks) { Log::verbose("GameProtocol", "Host %d with ticks difference %d", p->getHostId(), ticks); m_initial_ticks[p] = ticks; } // addInitialTicks