Merge remote-tracking branch 'origin/network-item-debugging'

This commit is contained in:
Benau 2018-09-28 23:39:04 +08:00
commit abd3a79526
14 changed files with 196 additions and 116 deletions

View File

@ -37,6 +37,25 @@
#include <ISceneManager.h> #include <ISceneManager.h>
// ----------------------------------------------------------------------------
/** Constructor.
* \param type Type of the item.
* \param owner If not NULL it is the kart that dropped this item; NULL
* indicates an item that's part of the track.
* \param id Index of this item in the array of all items.
*/
ItemState::ItemState(ItemType type, const AbstractKart *owner, int id)
{
setType(type);
m_item_id = id;
m_previous_owner = owner;
m_used_up_counter = -1;
if (owner)
setDeactivatedTicks(stk_config->time2Ticks(1.5f));
else
setDeactivatedTicks(0);
} // ItemState(ItemType)
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Sets the disappear counter depending on type. */ /** Sets the disappear counter depending on type. */
void ItemState::setDisappearCounter() void ItemState::setDisappearCounter()
@ -52,9 +71,23 @@ void ItemState::setDisappearCounter()
} // switch } // switch
} // setDisappearCounter } // setDisappearCounter
// -----------------------------------------------------------------------
/** Initialises an item.
* \param type Type for this item.
*/
void ItemState::initItem(ItemType type, const Vec3& xyz)
{
m_xyz = xyz;
m_original_type = ITEM_NONE;
m_ticks_till_return = 0;
setDisappearCounter();
} // initItem
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Update the state of the item, called once per physics frame. /** Update the state of the item, called once per physics frame.
* \param ticks Number of ticks to simulate (typically 1). * \param ticks Number of ticks to simulate. While this value is 1 when
* called during the normal game loop, during a rewind this value
* can be (much) larger than 1.
*/ */
void ItemState::update(int ticks) void ItemState::update(int ticks)
{ {
@ -107,12 +140,17 @@ void ItemState::collected(const AbstractKart *kart)
* \param normal The normal upon which the item is placed (so that it can * \param normal The normal upon which the item is placed (so that it can
* be aligned properly with the ground). * be aligned properly with the ground).
* \param mesh The mesh to be used for this item. * \param mesh The mesh to be used for this item.
* \param owner 'Owner' of this item, i.e. the kart that drops it. This is
* used to deactivate this item for the owner, i.e. avoid that a kart
* 'collects' its own bubble gum. NULL means no owner, and the item
* can be collected immediatley by any kart.
* \param is_predicted True if the creation of the item is predicted by * \param is_predicted True if the creation of the item is predicted by
* a client. Only used in networking. * a client. Only used in networking.
*/ */
Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal, Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal,
scene::IMesh* mesh, scene::IMesh* lowres_mesh, bool is_predicted) scene::IMesh* mesh, scene::IMesh* lowres_mesh,
: ItemState(type) const AbstractKart *owner, bool is_predicted)
: ItemState(type, owner)
{ {
assert(type != ITEM_TRIGGER); // use other constructor for that assert(type != ITEM_TRIGGER); // use other constructor for that
@ -190,7 +228,6 @@ Item::Item(const Vec3& xyz, float distance, TriggerItemListener* trigger)
void Item::initItem(ItemType type, const Vec3 &xyz) void Item::initItem(ItemType type, const Vec3 &xyz)
{ {
ItemState::initItem(type, xyz); ItemState::initItem(type, xyz);
m_previous_owner = NULL;
m_rotate = (getType()!=ITEM_BUBBLEGUM) && m_rotate = (getType()!=ITEM_BUBBLEGUM) &&
(getType()!=ITEM_TRIGGER ); (getType()!=ITEM_TRIGGER );
// Now determine in which quad this item is, and its distance // Now determine in which quad this item is, and its distance
@ -334,17 +371,6 @@ void Item::reset()
} }
} // reset } // reset
//-----------------------------------------------------------------------------
/** Sets which karts dropped an item. This is used to avoid that a kart is
* affected by its own items.
* \param parent Kart that dropped the item.
*/
void Item::setParent(const AbstractKart* parent)
{
m_previous_owner = parent;
ItemState::setDeactivatedTicks(stk_config->time2Ticks(1.5f));
} // setParent
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Updated the item - rotates it, takes care of items coming back into /** Updated the item - rotates it, takes care of items coming back into
* the game after it has been collected. * the game after it has been collected.

View File

@ -122,35 +122,25 @@ private:
* and then converting this value to a Vec3. */ * and then converting this value to a Vec3. */
Vec3 m_xyz; Vec3 m_xyz;
protected:
/** The 'owner' of the item, i.e. the kart that dropped this item. /** The 'owner' of the item, i.e. the kart that dropped this item.
* Is NULL if the item is part of the track. */ * Is NULL if the item is part of the track. */
const AbstractKart *m_previous_owner; const AbstractKart *m_previous_owner;
protected:
friend class ItemManager; friend class ItemManager;
friend class NetworkItemManager; friend class NetworkItemManager;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
virtual void setType(ItemType type) { m_type = type; } virtual void setType(ItemType type) { m_type = type; }
public: public:
/** Constructor. ItemState(ItemType type, const AbstractKart *owner=NULL, int id = -1);
* \param type Type of the item. void initItem(ItemType type, const Vec3& xyz);
* \param id Index of this item in the array of all items. void update(int ticks);
* \param kart_id If !=-1 the kart that dropped this item; -1 void setDisappearCounter();
* indicates an item that's part of the track. */ virtual void collected(const AbstractKart *kart);
ItemState(ItemType type, int id=-1, AbstractKart *kart=NULL)
{
setType(type);
m_item_id = id;
m_previous_owner = kart;
} // ItemState(ItemType)
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
virtual ~ItemState() {} virtual ~ItemState() {}
void setDisappearCounter();
void update(int ticks);
virtual void collected(const AbstractKart *kart);
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
void reset() void reset()
@ -166,19 +156,6 @@ public:
} }
} // reset } // reset
// -----------------------------------------------------------------------
/** Initialises an item.
* \param type Type for this item.
*/
void initItem(ItemType type, const Vec3& xyz)
{
m_xyz = xyz;
m_original_type = ITEM_NONE;
m_deactive_ticks = 0;
m_ticks_till_return = 0;
setDisappearCounter();
} // initItem
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Switches an item to be of a different type. Used for the switch /** Switches an item to be of a different type. Used for the switch
* powerup. * powerup.
@ -329,19 +306,18 @@ private:
public: public:
Item(ItemType type, const Vec3& xyz, const Vec3& normal, Item(ItemType type, const Vec3& xyz, const Vec3& normal,
scene::IMesh* mesh, scene::IMesh* lowres_mesh, scene::IMesh* mesh, scene::IMesh* lowres_mesh,
const AbstractKart *owner,
bool is_predicted=false); bool is_predicted=false);
Item(const Vec3& xyz, float distance, Item(const Vec3& xyz, float distance,
TriggerItemListener* trigger); TriggerItemListener* trigger);
virtual ~Item (); virtual ~Item ();
void updateGraphics(float dt); void updateGraphics(float dt);
virtual void collected(const AbstractKart *kart) OVERRIDE; virtual void collected(const AbstractKart *kart) OVERRIDE;
void setParent(const AbstractKart* parent);
void reset(); void reset();
void switchTo(ItemType type, scene::IMesh *mesh, scene::IMesh *lowmesh); void switchTo(ItemType type, scene::IMesh *mesh,
scene::IMesh *lowmesh);
void switchBack(); void switchBack();
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns true if the Kart is close enough to hit this item, the item is /** Returns true if the Kart is close enough to hit this item, the item is
* not deactivated anymore, and it wasn't placed by this kart (this is * not deactivated anymore, and it wasn't placed by this kart (this is
@ -352,7 +328,7 @@ public:
*/ */
bool hitKart(const Vec3 &xyz, const AbstractKart *kart=NULL) const bool hitKart(const Vec3 &xyz, const AbstractKart *kart=NULL) const
{ {
if (m_previous_owner == kart && getDeactivatedTicks() > 0) if (getPreviousOwner() == kart && getDeactivatedTicks() > 0)
return false; return false;
Vec3 lc = quatRotate(m_original_rotation, xyz - getXYZ()); Vec3 lc = quatRotate(m_original_rotation, xyz - getXYZ());
// Don't be too strict if the kart is a bit above the item // Don't be too strict if the kart is a bit above the item
@ -373,7 +349,8 @@ protected:
bool hitLine(const core::line3df &line, bool hitLine(const core::line3df &line,
const AbstractKart *kart=NULL) const const AbstractKart *kart=NULL) const
{ {
if (m_previous_owner == kart && getDeactivatedTicks() >0) return false; if (getPreviousOwner() == kart && getDeactivatedTicks() > 0)
return false;
Vec3 closest = line.getClosestPoint(getXYZ().toIrrVector()); Vec3 closest = line.getClosestPoint(getXYZ().toIrrVector());
return hitKart(closest, kart); return hitKart(closest, kart);

View File

@ -31,6 +31,7 @@
*/ */
ItemEventInfo::ItemEventInfo(BareNetworkString *buffer, int *count) ItemEventInfo::ItemEventInfo(BareNetworkString *buffer, int *count)
{ {
m_ticks_till_return = 0;
m_type = (EventType)buffer->getUInt8(); m_type = (EventType)buffer->getUInt8();
m_ticks = buffer->getTime(); m_ticks = buffer->getTime();
m_kart_id = buffer->getInt8(); m_kart_id = buffer->getInt8();
@ -41,7 +42,11 @@ ItemEventInfo::ItemEventInfo(BareNetworkString *buffer, int *count)
m_xyz = buffer->getVec3(); m_xyz = buffer->getVec3();
*count -= 12; *count -= 12;
} }
else if (m_type == IEI_COLLECT)
{
m_ticks_till_return = buffer->getUInt16();
*count -= 2;
}
} // ItemEventInfo(BareNetworkString, int *count) } // ItemEventInfo(BareNetworkString, int *count)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -55,7 +60,6 @@ void ItemEventInfo::saveState(BareNetworkString *buffer)
.addUInt16(m_index); .addUInt16(m_index);
if(m_type == IEI_NEW) if(m_type == IEI_NEW)
buffer->add(m_xyz); buffer->add(m_xyz);
else if (m_type == IEI_COLLECT)
buffer->addUInt16(m_ticks_till_return);
} // saveState } // saveState

View File

@ -51,13 +51,20 @@ private:
/** In case of new items the position of the new item. */ /** In case of new items the position of the new item. */
Vec3 m_xyz; Vec3 m_xyz;
/** Ticks for the item to return, atm used by collecting banana
* with bomb to delay the return for banana. */
int16_t m_ticks_till_return;
public: public:
/** Constructor for collecting an existing item. /** Constructor for collecting an existing item.
* \param ticks Time of the event. * \param ticks Time of the event.
* \param item_id The index of the item that was collected. * \param item_id The index of the item that was collected.
* \param kart_id the kart that collected the item. */ * \param kart_id the kart that collected the item.
ItemEventInfo(int ticks, int index, int kart_id) * \param ttr Ticks till return after being collected. */
: m_ticks(ticks), m_index(index), m_kart_id(kart_id)
ItemEventInfo(int ticks, int index, int kart_id, int16_t ttr)
: m_ticks(ticks), m_index(index), m_kart_id(kart_id),
m_ticks_till_return(ttr)
{ {
m_type = IEI_COLLECT; m_type = IEI_COLLECT;
} // ItemEventInfo(collected existing item) } // ItemEventInfo(collected existing item)
@ -69,14 +76,15 @@ public:
*/ */
ItemEventInfo(int ticks, ItemState::ItemType type, int index, ItemEventInfo(int ticks, ItemState::ItemType type, int index,
int kart_id, const Vec3 &xyz) int kart_id, const Vec3 &xyz)
: m_ticks(ticks), m_index(index), m_kart_id(kart_id), m_xyz(xyz) : m_ticks(ticks), m_index(index), m_kart_id(kart_id), m_xyz(xyz),
m_ticks_till_return(0)
{ {
m_type = IEI_NEW; m_type = IEI_NEW;
} // ItemEventInfo(new item) } // ItemEventInfo(new item)
// -------------------------------------------------------------------- // --------------------------------------------------------------------
/** Constructor for switching items. */ /** Constructor for switching items. */
ItemEventInfo(int ticks) : m_ticks(ticks) ItemEventInfo(int ticks) : m_ticks(ticks), m_ticks_till_return(0)
{ {
m_type = IEI_SWITCH; m_type = IEI_SWITCH;
} // ItemEventInfo(switch) } // ItemEventInfo(switch)
@ -116,6 +124,9 @@ public:
return m_xyz; return m_xyz;
} // getXYZ } // getXYZ
// -------------------------------------------------------------------- // --------------------------------------------------------------------
/** Returns the ticks till return, used only by collection events. */
int getTicksTillReturn() const { return m_ticks_till_return; }
// --------------------------------------------------------------------
/** Returns the type of this item. Note at this stage only bubble gums /** Returns the type of this item. Note at this stage only bubble gums
* can be created during a race. */ * can be created during a race. */
ItemState::ItemType getNewItemType() const ItemState::ItemType getNewItemType() const

View File

@ -226,13 +226,15 @@ unsigned int ItemManager::insertItem(Item *item)
// previously deleted entry, otherwise at the end. // previously deleted entry, otherwise at the end.
int index = -1; int index = -1;
for(index=(int)m_all_items.size()-1; index>=0 && m_all_items[index]; index--) {} for(index=(int)m_all_items.size()-1; index>=0 && m_all_items[index]; index--) {}
if (index == -1)
if(index==-1) index = (int)m_all_items.size(); {
index = (int)m_all_items.size();
if(index<(int)m_all_items.size())
m_all_items[index] = item;
else
m_all_items.push_back(item); m_all_items.push_back(item);
}
else
{
m_all_items[index] = item;
}
item->setItemId(index); item->setItemId(index);
// Now insert into the appropriate quad list, if there is a quad list // Now insert into the appropriate quad list, if there is a quad list
@ -286,9 +288,7 @@ Item* ItemManager::dropNewItem(ItemState::ItemType type,
} }
Item* item = new Item(type, pos, normal, m_item_mesh[mesh_type], Item* item = new Item(type, pos, normal, m_item_mesh[mesh_type],
m_item_lowres_mesh[mesh_type]); m_item_lowres_mesh[mesh_type], /*prev_owner*/kart);
if(kart != NULL) item->setParent(kart);
insertItem(item); insertItem(item);
if(m_switch_ticks>=0) if(m_switch_ticks>=0)
{ {
@ -318,7 +318,7 @@ Item* ItemManager::placeItem(ItemState::ItemType type, const Vec3& xyz,
ItemState::ItemType mesh_type = type; ItemState::ItemType mesh_type = type;
Item* item = new Item(type, xyz, normal, m_item_mesh[mesh_type], Item* item = new Item(type, xyz, normal, m_item_mesh[mesh_type],
m_item_lowres_mesh[mesh_type]); m_item_lowres_mesh[mesh_type], /*prev_owner*/NULL);
insertItem(item); insertItem(item);
if (m_switch_ticks >= 0) if (m_switch_ticks >= 0)
@ -354,7 +354,7 @@ Item* ItemManager::placeTrigger(const Vec3& xyz, float distance,
* \param item The item that was collected. * \param item The item that was collected.
* \param kart The kart that collected the item. * \param kart The kart that collected the item.
*/ */
void ItemManager::collectedItem(Item *item, AbstractKart *kart) void ItemManager::collectedItem(ItemState *item, AbstractKart *kart)
{ {
assert(item); assert(item);
// Spare tire karts don't collect items // Spare tire karts don't collect items

View File

@ -133,7 +133,7 @@ public:
void updateGraphics (float dt); void updateGraphics (float dt);
void checkItemHit (AbstractKart* kart); void checkItemHit (AbstractKart* kart);
void reset (); void reset ();
virtual void collectedItem (Item *item, AbstractKart *kart); virtual void collectedItem (ItemState *item, AbstractKart *kart);
void switchItems (); void switchItems ();
bool areItemSwitched() { return (m_switch_ticks > 0); } bool areItemSwitched() { return (m_switch_ticks > 0); }
bool randomItemsForArena(const AlignedArray<btTransform>& pos); bool randomItemsForArena(const AlignedArray<btTransform>& pos);

View File

@ -95,17 +95,18 @@ void NetworkItemManager::initClientConfirmState()
* \param item The item that was collected. * \param item The item that was collected.
* \param kart The kart that collected the item. * \param kart The kart that collected the item.
*/ */
void NetworkItemManager::collectedItem(Item *item, AbstractKart *kart) void NetworkItemManager::collectedItem(ItemState *item, AbstractKart *kart)
{ {
if(NetworkConfig::get()->isServer()) if(NetworkConfig::get()->isServer())
{ {
ItemManager::collectedItem(item, kart);
// The server saves the collected item as item event info // The server saves the collected item as item event info
m_item_events.lock(); m_item_events.lock();
m_item_events.getData().emplace_back(World::getWorld()->getTicksSinceStart(), m_item_events.getData().emplace_back(World::getWorld()->getTicksSinceStart(),
item->getItemId(), item->getItemId(),
kart->getWorldKartId()); kart->getWorldKartId(),
item->getTicksTillReturn());
m_item_events.unlock(); m_item_events.unlock();
ItemManager::collectedItem(item, kart);
} }
else else
{ {
@ -142,7 +143,7 @@ Item* NetworkItemManager::dropNewItem(ItemState::ItemType type,
kart->getXYZ() ); kart->getXYZ() );
m_item_events.unlock(); m_item_events.unlock();
return item; return item;
} // newItem } // dropNewItem
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Called by the GameProtocol when a confirmation for an item event is /** Called by the GameProtocol when a confirmation for an item event is
@ -237,9 +238,9 @@ void NetworkItemManager::forwardTime(int ticks)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** Restores the state of the items to the current world time. It takes the /** Restores the state of the items to the current world time. It takes the
* last saved * last saved confirmed state, applies any updates from the server, and
* then syncs up the confirmed state to the in-race items.
* using exactly 'count' bytes of the message. * It uses exactly 'count' bytes of the message.
* \param buffer the state content. * \param buffer the state content.
* \param count Number of bytes used for this state. * \param count Number of bytes used for this state.
*/ */
@ -267,6 +268,7 @@ void NetworkItemManager::restoreState(BareNetworkString *buffer, int count)
// From here the replay can happen. // From here the replay can happen.
// 1) Remove predicted items: // 1) Remove predicted items:
// --------------------------
for (unsigned int i=0; i<m_all_items.size(); i++) for (unsigned int i=0; i<m_all_items.size(); i++)
{ {
Item *item = m_all_items[i]; Item *item = m_all_items[i];
@ -274,20 +276,22 @@ void NetworkItemManager::restoreState(BareNetworkString *buffer, int count)
{ {
deleteItem(item); deleteItem(item);
} }
} } // for i in m_all_items
// 2) Apply all events to current confirmed state: // 2) Apply all events to current confirmed state:
// -----------------------------------------------
World *world = World::getWorld();
int current_time = m_confirmed_state_time; int current_time = m_confirmed_state_time;
bool has_state = count > 0; bool has_state = count > 0;
while(count > 0) while(count > 0)
{ {
// 1) Decode the event in the message // 2.1) Decode the event in the message
// ---------------------------------- // ------------------------------------
ItemEventInfo iei(buffer, &count); ItemEventInfo iei(buffer, &count);
// 2) If the event needs to be applied, forward // 2.2) If the event needs to be applied, forward
// the time to the time of this event: // the time to the time of this event:
// -------------------------------------------- // ----------------------------------------------
int dt = iei.getTicks() - current_time; int dt = iei.getTicks() - current_time;
// Skip an event that are 'in the past' (i.e. have been sent again by // 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 // the server because it has not yet received confirmation from all
@ -297,13 +301,35 @@ void NetworkItemManager::restoreState(BareNetworkString *buffer, int count)
// Forward the saved state: // Forward the saved state:
if (dt>0) forwardTime(dt); if (dt>0) forwardTime(dt);
// TODO: apply the various events types, atm only collection is supported: // TODO: apply the various events types, atm only collection
// and new items are supported.
if(iei.isItemCollection()) if(iei.isItemCollection())
{ {
int index = iei.getIndex(); int index = iei.getIndex();
// An item on the track was collected: // An item on the track was collected:
AbstractKart *kart = World::getWorld()->getKart(iei.getKartId()); AbstractKart *kart = world->getKart(iei.getKartId());
m_confirmed_state[index]->collected(kart);
// The world clock was set by the RewindManager to be the time
// of the state we are rewinding to. But this confirmed collection
// event happened in the past (we are replaying item events since
// the last confirmed state in order to get a new confirmed state).
// So we need to reset the clock to the time at which this event
// happened so that (e.g.) kart can use the right time (for
// example, bubble gum torque depends on time, and would be wrong
// otherwise resulting in stuttering).
int old_time = world->getTicksSinceStart(); // Save time we rewind to
world->setTicksForRewind(iei.getTicks()); // Set time of event
if (m_confirmed_state[index] != NULL)
{
m_confirmed_state[index]->collected(kart);// Collect item
// Reset till ticks return from state (required for eating banana with bomb)
int ttr = iei.getTicksTillReturn();
m_confirmed_state[index]->setTicksTillReturn(ttr);
}
world->setTicksForRewind(old_time); // Set time to rewind-to
if (m_confirmed_state[index]->isUsedUp()) if (m_confirmed_state[index]->isUsedUp())
{ {
delete m_confirmed_state[index]; delete m_confirmed_state[index];
@ -312,9 +338,9 @@ void NetworkItemManager::restoreState(BareNetworkString *buffer, int count)
} }
else if(iei.isNewItem()) else if(iei.isNewItem())
{ {
AbstractKart *kart = World::getWorld()->getKart(iei.getKartId()); AbstractKart *kart = world->getKart(iei.getKartId());
ItemState *is = new ItemState(iei.getNewItemType(), iei.getIndex(), ItemState *is = new ItemState(iei.getNewItemType(), kart,
kart); iei.getIndex() );
is->initItem(iei.getNewItemType(), iei.getXYZ()); is->initItem(iei.getNewItemType(), iei.getXYZ());
if (m_confirmed_state.size() <= is->getItemId()) if (m_confirmed_state.size() <= is->getItemId())
{ {
@ -325,29 +351,34 @@ void NetworkItemManager::restoreState(BareNetworkString *buffer, int count)
if (m_confirmed_state[is->getItemId()] == NULL) if (m_confirmed_state[is->getItemId()] == NULL)
m_confirmed_state[is->getItemId()] = is; m_confirmed_state[is->getItemId()] = is;
else else
{
*m_confirmed_state[is->getItemId()] = *is; *m_confirmed_state[is->getItemId()] = *is;
delete is;
}
} }
} }
current_time = iei.getTicks(); current_time = iei.getTicks();
} // while count >0 } // while count >0
// Inform the server which events have been received. // Inform the server which events have been received (if there has
// been any updates - no need to send messages if nothing has changed)
if (has_state) if (has_state)
{ {
if (auto gp = GameProtocol::lock()) if (auto gp = GameProtocol::lock())
gp->sendItemEventConfirmation(World::getWorld()->getTicksSinceStart()); gp->sendItemEventConfirmation(world->getTicksSinceStart());
} }
// Forward the confirmed item state to the world time: // Forward the confirmed item state to the world time:
int dt = World::getWorld()->getTicksSinceStart() - current_time; int dt = world->getTicksSinceStart() - current_time;
if(dt>0) forwardTime(dt); if(dt>0) forwardTime(dt);
// Restore the state to the current world time: // 3. Restore the state to the current world time:
// ============================================ // ===============================================
for(unsigned int i=0; i<m_confirmed_state.size(); i++) for(unsigned int i=0; i<m_confirmed_state.size(); i++)
{ {
Item *item = m_all_items[i]; Item *item = i < m_all_items.size() ? m_all_items[i] : NULL;
const ItemState *is = m_confirmed_state[i]; const ItemState *is = m_confirmed_state[i];
if (is && item) if (is && item)
*(ItemState*)item = *is; *(ItemState*)item = *is;
@ -356,18 +387,33 @@ void NetworkItemManager::restoreState(BareNetworkString *buffer, int count)
Vec3 xyz = is->getXYZ(); Vec3 xyz = is->getXYZ();
Item *item_new = dropNewItem(is->getType(), is->getPreviousOwner(), Item *item_new = dropNewItem(is->getType(), is->getPreviousOwner(),
&xyz); &xyz);
item_new->setPredicted(false); if (i != item_new->getItemId())
item_new->setItemId(i); {
// The server has this item at a different index in the list
// of all items, which means the client has an incorrect
// item at the index given by the server - delete that item
Log::info("nim", "about to delete item with not matching index i %d item %d",
i, item_new->getItemId());
if(m_all_items[i])
deleteItem(m_all_items[i]);
m_all_items[item_new->getItemId()] = NULL;
m_all_items[i] = item_new; m_all_items[i] = item_new;
item_new->setItemId(i);
}
item_new->setPredicted(false);
item_new->setDeactivatedTicks(is->getDeactivatedTicks());
*((ItemState*)m_all_items[i]) = *is; *((ItemState*)m_all_items[i]) = *is;
} }
else if (!is && item) else if (!is && item)
{ {
Log::info("nim", "About to delete item index %d i %d",
item->getItemId(), i);
deleteItem(m_all_items[i]); deleteItem(m_all_items[i]);
} }
} }
// Now we save the current local // Now we save the current local
m_confirmed_state_time = World::getWorld()->getTicksSinceStart(); m_confirmed_state_time = world->getTicksSinceStart();
} // restoreState } // restoreState

View File

@ -73,7 +73,7 @@ public:
virtual void reset() OVERRIDE; virtual void reset() OVERRIDE;
virtual void setItemConfirmationTime(std::weak_ptr<STKPeer> peer, virtual void setItemConfirmationTime(std::weak_ptr<STKPeer> peer,
int ticks) OVERRIDE; int ticks) OVERRIDE;
virtual void collectedItem(Item *item, AbstractKart *kart) OVERRIDE; virtual void collectedItem(ItemState *item, AbstractKart *kart) OVERRIDE;
virtual Item* dropNewItem(ItemState::ItemType type, const AbstractKart *kart, virtual Item* dropNewItem(ItemState::ItemType type, const AbstractKart *kart,
const Vec3 *xyz=NULL) OVERRIDE; const Vec3 *xyz=NULL) OVERRIDE;
virtual BareNetworkString* saveState(std::vector<std::string>* ru) virtual BareNetworkString* saveState(std::vector<std::string>* ru)

View File

@ -561,11 +561,6 @@ void Powerup::hitBonusBox(const ItemState &item_state)
new_powerup = powerup_manager->getRandomPowerup(position, &n, new_powerup = powerup_manager->getRandomPowerup(position, &n,
random_number); random_number);
// FIXME Disable switch and bubblegum for now in network
if (NetworkConfig::get()->isNetworking() &&
(new_powerup == PowerupManager::POWERUP_BUBBLEGUM ||
new_powerup == PowerupManager::POWERUP_SWITCH))
new_powerup = PowerupManager::POWERUP_BOWLING;
// Always add a new powerup in ITEM_MODE_NEW (or if the kart // Always add a new powerup in ITEM_MODE_NEW (or if the kart
// doesn't have a powerup atm). // doesn't have a powerup atm).

View File

@ -1554,7 +1554,7 @@ void Kart::update(int ticks)
"v(16-18) %f %f %f steerf(20) %f maxangle(22) %f speed(24) %f " "v(16-18) %f %f %f steerf(20) %f maxangle(22) %f speed(24) %f "
"steering(26-27) %f %f clock(29) %lf skidstate(31) %d factor(33) %f " "steering(26-27) %f %f clock(29) %lf skidstate(31) %d factor(33) %f "
"maxspeed(35) %f engf(37) %f braketick(39) %d brakes(41) %d heading(43) %f " "maxspeed(35) %f engf(37) %f braketick(39) %d brakes(41) %d heading(43) %f "
"noderot(45) %f suslen %f", "bubticks(45) %d bubtor(47) %f",
getIdent().c_str(), getIdent().c_str(),
World::getWorld()->getTime(), World::getWorld()->getTicksSinceStart(), World::getWorld()->getTime(), World::getWorld()->getTicksSinceStart(),
getXYZ().getX(), getXYZ().getY(), getXYZ().getZ(), getXYZ().getX(), getXYZ().getY(), getXYZ().getZ(),
@ -1575,8 +1575,8 @@ void Kart::update(int ticks)
m_brake_ticks, //39 m_brake_ticks, //39
m_controls.getButtonsCompressed(), //41 m_controls.getButtonsCompressed(), //41
getHeading(), //43 getHeading(), //43
m_node->getAbsoluteTransformation().getRotationDegrees().Y, //45 m_bubblegum_ticks, // 45
m_vehicle->getWheelInfo(0).m_raycastInfo.m_suspensionLength m_bubblegum_torque // 47
); );
#endif #endif
// After the physics step was done, the position of the wheels (as stored // After the physics step was done, the position of the wheels (as stored

View File

@ -159,7 +159,8 @@ const std::string& EasterEggHunt::getIdent() const
/** Called when a kart has collected an egg. /** Called when a kart has collected an egg.
* \param kart The kart that collected an egg. * \param kart The kart that collected an egg.
*/ */
void EasterEggHunt::collectedItem(const AbstractKart *kart, const Item *item) void EasterEggHunt::collectedItem(const AbstractKart *kart,
const ItemState *item )
{ {
if(item->getType() != ItemState::ITEM_EASTER_EGG) return; if(item->getType() != ItemState::ITEM_EASTER_EGG) return;

View File

@ -66,7 +66,7 @@ public:
virtual void getKartsDisplayInfo( virtual void getKartsDisplayInfo(
std::vector<RaceGUIBase::KartIconDisplayInfo> *info) OVERRIDE; std::vector<RaceGUIBase::KartIconDisplayInfo> *info) OVERRIDE;
virtual void collectedItem(const AbstractKart *kart, virtual void collectedItem(const AbstractKart *kart,
const Item *item ) OVERRIDE; const ItemState *item ) OVERRIDE;
void collectedEasterEggGhost(int world_id); void collectedEasterEggGhost(int world_id);
const int numberOfEggsFound() { return m_eggs_found; } const int numberOfEggsFound() { return m_eggs_found; }

View File

@ -42,7 +42,7 @@
class AbstractKart; class AbstractKart;
class btRigidBody; class btRigidBody;
class Controller; class Controller;
class Item; class ItemState;
class PhysicalObject; class PhysicalObject;
namespace Scripting namespace Scripting
@ -263,7 +263,8 @@ public:
int *amount ); int *amount );
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Receives notification if an item is collected. Used for easter eggs. */ /** Receives notification if an item is collected. Used for easter eggs. */
virtual void collectedItem(const AbstractKart *kart, const Item *item) {} virtual void collectedItem(const AbstractKart *kart,
const ItemState *item ) {}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
virtual void endRaceEarly() { return; } virtual void endRaceEarly() { return; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------

View File

@ -154,8 +154,27 @@ void RewindManager::saveState()
m_overall_state_size = 0; m_overall_state_size = 0;
std::vector<std::string> rewinder_using; std::vector<std::string> rewinder_using;
// We must save the item state first (so that it is restored first),
// otherwise state updates for a kart could be overwritten by
// e.g. simulating the item collection later (which resets bubblegum
// counter).
BareNetworkString* buffer = NULL;
if(auto r = m_all_rewinder["N"].lock())
buffer = r->saveState(&rewinder_using);
if (buffer)
{
m_overall_state_size += buffer->size();
gp->addState(buffer);
}
delete buffer; // buffer can be freed
for (auto& p : m_all_rewinder) for (auto& p : m_all_rewinder)
{ {
// The Network ItemManager was saved first before this loop,
// so skip it here.
if(p.first=="N") continue;
// TODO: check if it's worth passing in a sufficiently large buffer from // TODO: check if it's worth passing in a sufficiently large buffer from
// GameProtocol - this would save the copy operation. // GameProtocol - this would save the copy operation.
BareNetworkString* buffer = NULL; BareNetworkString* buffer = NULL;