2018-05-08 05:12:06 -04:00
|
|
|
//
|
|
|
|
// SuperTuxKart - a fun racing game with go-kart
|
|
|
|
// Copyright (C) 2018 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 "items/network_item_manager.hpp"
|
|
|
|
|
|
|
|
#include "karts/abstract_kart.hpp"
|
|
|
|
#include "modes/world.hpp"
|
|
|
|
#include "network/network_config.hpp"
|
|
|
|
#include "network/protocols/game_protocol.hpp"
|
|
|
|
#include "network/stk_host.hpp"
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/** Creates one instance of the item manager. */
|
|
|
|
void NetworkItemManager::create()
|
|
|
|
{
|
|
|
|
assert(!m_item_manager);
|
|
|
|
m_item_manager = new NetworkItemManager();
|
|
|
|
} // create
|
|
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
/** Creates a new instance of the item manager. This is done at startup
|
|
|
|
* of each race. */
|
|
|
|
NetworkItemManager::NetworkItemManager()
|
2018-05-08 10:48:20 -04:00
|
|
|
: Rewinder(/*can be deleted*/false),
|
|
|
|
ItemManager()
|
2018-05-08 05:12:06 -04:00
|
|
|
{
|
|
|
|
m_last_confirmed_item_ticks.lock();
|
|
|
|
m_last_confirmed_item_ticks.getData().clear();
|
|
|
|
|
|
|
|
if (NetworkConfig::get()->isServer())
|
|
|
|
m_last_confirmed_item_ticks.getData().resize(STKHost::get()->getPeerCount(), 0);
|
|
|
|
|
|
|
|
m_last_confirmed_item_ticks.unlock();
|
|
|
|
} // NetworkItemManager
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/** Destructor. Cleans up all items and meshes stored.
|
|
|
|
*/
|
|
|
|
NetworkItemManager::~NetworkItemManager()
|
|
|
|
{
|
|
|
|
} // ~NetworkItemManager
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void NetworkItemManager::reset()
|
|
|
|
{
|
|
|
|
m_last_confirmed_event = 0;
|
|
|
|
ItemManager::reset();
|
|
|
|
} // reset
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/** Copies the initial state at the start of a race as confirmed state.
|
|
|
|
*/
|
|
|
|
void NetworkItemManager::saveInitialState()
|
|
|
|
{
|
|
|
|
m_confirmed_state_time = 0;
|
|
|
|
m_last_confirmed_event = 0;
|
|
|
|
|
|
|
|
m_confirmed_state.clear();
|
|
|
|
for(auto i : m_all_items)
|
|
|
|
{
|
2018-05-09 11:09:23 -04:00
|
|
|
ItemState *is = new ItemState(*i);
|
|
|
|
m_confirmed_state.push_back(is);
|
2018-05-08 05:12:06 -04:00
|
|
|
}
|
|
|
|
} // saveInitialState
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/** Called when a kart collects an item. In network games only the server
|
|
|
|
* acts on this event.
|
|
|
|
* \param item The item that was collected.
|
|
|
|
* \param kart The kart that collected the item.
|
|
|
|
* \param add_info
|
|
|
|
*/
|
|
|
|
void NetworkItemManager::collectedItem(Item *item, AbstractKart *kart,
|
|
|
|
int add_info)
|
|
|
|
{
|
|
|
|
if(NetworkConfig::get()->isServer())
|
|
|
|
{
|
2018-05-09 11:09:23 -04:00
|
|
|
m_item_events.lock();
|
|
|
|
m_item_events.getData().emplace_back(World::getWorld()->getTimeTicks(),
|
|
|
|
item->getItemId(),
|
|
|
|
kart->getWorldKartId(),
|
|
|
|
/*item_info*/0);
|
|
|
|
m_item_events.unlock();
|
2018-05-08 05:12:06 -04:00
|
|
|
ItemManager::collectedItem(item, kart, add_info);
|
|
|
|
}
|
|
|
|
} // collectedItem
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2018-05-09 11:09:23 -04:00
|
|
|
/** Called by the GameProtocol when a confirmation for an item event is
|
|
|
|
* received by a host. Once all hosts have confirmed an event, it can be
|
|
|
|
* deleted and won't be send to any clients again.
|
|
|
|
* \param host_id Host identification of the host confirming the latest
|
|
|
|
* event time received.
|
|
|
|
* \param ticks Time at which the last event was received.
|
|
|
|
*/
|
2018-05-08 05:12:06 -04:00
|
|
|
void NetworkItemManager::setItemConfirmationTime(int host_id, int ticks)
|
|
|
|
{
|
2018-05-09 11:09:23 -04:00
|
|
|
assert(NetworkConfig::get()->isServer());
|
2018-05-08 05:12:06 -04:00
|
|
|
m_last_confirmed_item_ticks.lock();
|
|
|
|
if (ticks > m_last_confirmed_item_ticks.getData()[host_id])
|
|
|
|
m_last_confirmed_item_ticks.getData()[host_id] = ticks;
|
2018-05-09 11:09:23 -04:00
|
|
|
|
|
|
|
// Now discard unneeded events, i.e. all events that have
|
|
|
|
// been confirmed by all clients:
|
|
|
|
int min_time = 999999;
|
|
|
|
for (auto i : m_last_confirmed_item_ticks.getData())
|
|
|
|
if (i < min_time) min_time = i;
|
2018-05-08 05:12:06 -04:00
|
|
|
m_last_confirmed_item_ticks.unlock();
|
2018-05-09 11:09:23 -04:00
|
|
|
|
|
|
|
// Find the last entry before the minimal confirmed time.
|
|
|
|
// Since the event list is sorted, all events up to this
|
|
|
|
// entry can be deleted.
|
|
|
|
m_item_events.lock();
|
|
|
|
auto p = m_item_events.getData().begin();
|
|
|
|
while (p != m_item_events.getData().end() && p->m_ticks < min_time)
|
|
|
|
p++;
|
|
|
|
m_item_events.getData().erase(m_item_events.getData().begin(), p);
|
|
|
|
m_item_events.unlock();
|
|
|
|
|
|
|
|
// TODO: Get informed when a client drops out!!!
|
2018-05-08 05:12:06 -04:00
|
|
|
} // setItemConfirmationTime
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2018-05-09 11:09:23 -04:00
|
|
|
/** Saves the state of all items. This is done by using a state that has
|
|
|
|
* been confirmed by all clients as a base, and then only adding any
|
|
|
|
* changes applied to that state later. As clients keep on confirming events
|
|
|
|
* the confirmed event will be moved forward in time, and older events can
|
|
|
|
* be deleted (and not sent to the clients anymore).
|
|
|
|
* This function is also called on the client in the first frame of a race
|
|
|
|
* to save the initial state, which is the first confirmed state by all
|
|
|
|
* clients.
|
|
|
|
*/
|
2018-05-08 05:12:06 -04:00
|
|
|
BareNetworkString* NetworkItemManager::saveState()
|
|
|
|
{
|
|
|
|
if(NetworkConfig::get()->isClient())
|
|
|
|
{
|
|
|
|
saveInitialState();
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// On the server:
|
|
|
|
// ==============
|
2018-05-09 11:09:23 -04:00
|
|
|
m_item_events.lock();
|
|
|
|
uint16_t n = (uint16_t)m_item_events.getData().size();
|
2018-05-08 05:12:06 -04:00
|
|
|
if(n==0)
|
|
|
|
{
|
|
|
|
BareNetworkString *s =
|
|
|
|
new BareNetworkString();
|
2018-05-09 11:09:23 -04:00
|
|
|
m_item_events.unlock();
|
2018-05-08 05:12:06 -04:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
BareNetworkString *s =
|
|
|
|
new BareNetworkString(n * (sizeof(int) + sizeof(uint16_t) +
|
|
|
|
+ sizeof(uint8_t) ) );
|
2018-05-09 11:09:23 -04:00
|
|
|
for (auto p : m_item_events.getData())
|
2018-05-08 05:12:06 -04:00
|
|
|
{
|
2018-05-09 11:09:23 -04:00
|
|
|
s->addTime(p.m_ticks).addUInt16(p.m_index);
|
|
|
|
if (p.m_index > -1) s->addUInt8(p.m_kart_id);
|
2018-05-08 05:12:06 -04:00
|
|
|
}
|
2018-05-09 11:09:23 -04:00
|
|
|
m_item_events.unlock();
|
2018-05-08 05:12:06 -04:00
|
|
|
Log::verbose("NIM", "Including %d item update at %d",
|
|
|
|
n, World::getWorld()->getTimeTicks());
|
|
|
|
return s;
|
|
|
|
} // saveState
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2018-05-09 11:09:23 -04:00
|
|
|
/** Progresses the time for all item by the given number of ticks. Used
|
|
|
|
* when computing a new state from a confirmed state.
|
|
|
|
* \param ticks Number of ticks that need to be simulated.
|
|
|
|
*/
|
|
|
|
void NetworkItemManager::forwardTime(int ticks)
|
|
|
|
{
|
|
|
|
for(auto &i : m_confirmed_state)
|
|
|
|
{
|
|
|
|
if (i) i->update(ticks);
|
|
|
|
} // for m_all_items
|
|
|
|
} // forwardTime
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/** Restores the state of the items to the current world time. It takes the
|
|
|
|
* last saved
|
|
|
|
|
|
|
|
* using exactly 'count' bytes of the message.
|
2018-05-08 05:12:06 -04:00
|
|
|
* \param buffer the state content.
|
|
|
|
* \param count Number of bytes used for this state.
|
|
|
|
*/
|
|
|
|
void NetworkItemManager::restoreState(BareNetworkString *buffer, int count)
|
|
|
|
{
|
2018-05-09 11:09:23 -04:00
|
|
|
// The state at World::getTimeTicks() needs to be restored. The confirmed
|
|
|
|
// state in this instance was taken at m_confirmed_state_time. So first
|
|
|
|
// apply the new events to the confirmed state till we reach the end of
|
|
|
|
// all new events (which must be <= getTimeTicks() (since the server will
|
|
|
|
// only send events till the specified state time). Then forward the
|
|
|
|
// state to getTimeTicks().
|
|
|
|
|
|
|
|
int current_time = m_confirmed_state_time;
|
2018-05-08 05:12:06 -04:00
|
|
|
while(count > 0)
|
|
|
|
{
|
2018-05-09 11:09:23 -04:00
|
|
|
// 1) Decode the event in the message
|
|
|
|
// ----------------------------------
|
|
|
|
int ticks = buffer->getTime();
|
|
|
|
int item_index = buffer->getUInt16();
|
|
|
|
int kart_id = -1;
|
|
|
|
count -= 6;
|
|
|
|
if(item_index>-1)
|
2018-05-08 05:12:06 -04:00
|
|
|
{
|
|
|
|
kart_id = buffer->getUInt8();
|
|
|
|
count --;
|
|
|
|
} // item_id>-1
|
2018-05-09 11:09:23 -04:00
|
|
|
|
|
|
|
// 2) If the event needs to be applied, forward
|
|
|
|
// the time to the time of this event:
|
|
|
|
// --------------------------------------------
|
|
|
|
int dt = ticks - current_time;
|
|
|
|
// Skip an event that are 'in the past' (i.e. have been sent again by
|
|
|
|
// the server because it has not yet received confirmation from all
|
|
|
|
// clients.
|
|
|
|
if(dt<0) continue;
|
|
|
|
|
|
|
|
// Forward the saved state:
|
|
|
|
if (dt>0) forwardTime(dt);
|
|
|
|
|
|
|
|
// TODO: apply the various events types, atm only collection is supported:
|
|
|
|
ItemState *item_state = m_confirmed_state[item_index];
|
|
|
|
if (item_index > -1)
|
|
|
|
{
|
|
|
|
// An item on the track was collected:
|
|
|
|
AbstractKart *kart = World::getWorld()->getKart(kart_id);
|
|
|
|
m_confirmed_state[item_index]->collected(kart);
|
|
|
|
}
|
|
|
|
|
|
|
|
current_time = ticks;
|
2018-05-08 05:12:06 -04:00
|
|
|
} // while count >0
|
|
|
|
|
2018-05-09 11:09:23 -04:00
|
|
|
// Inform the server which events have been received.
|
|
|
|
GameProtocol::lock()->sendItemEventConfirmation(World::getWorld()->getTimeTicks());
|
|
|
|
|
|
|
|
// Forward the confirmed item state till the world time:
|
|
|
|
int dt = World::getWorld()->getTimeTicks() - current_time;
|
|
|
|
if(dt>0) forwardTime(dt);
|
|
|
|
|
|
|
|
// Restore the state to the current world time:
|
|
|
|
// ============================================
|
|
|
|
|
|
|
|
for(unsigned int i=0; i<m_confirmed_state.size(); i++)
|
|
|
|
{
|
|
|
|
Item *item = m_all_items[i];
|
|
|
|
const ItemState *is = m_confirmed_state[i];
|
|
|
|
item->setTicksTillReturn(is->getTicksTillReturn());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now we save the current local
|
|
|
|
m_confirmed_state_time = m_last_confirmed_event;
|
|
|
|
} // restoreState
|
2018-05-08 05:12:06 -04:00
|
|
|
|