Added handling of predicted item drops.

This commit is contained in:
hiker
2018-05-21 10:09:10 +10:00
parent cdea16f81d
commit 9108934c18
8 changed files with 134 additions and 46 deletions

20
src/items/item.cpp Normal file → Executable file
View File

@@ -102,14 +102,23 @@ void ItemState::collected(const AbstractKart *kart)
} // collected
// ============================================================================
/** Constructor for an item.
* \param type Type of the item.
* \param xyz Location of the item.
* \param normal The normal upon which the item is placed (so that it can
* be aligned properly with the ground).
* \param mesh The mesh to be used for this item.
* \param is_predicted True if the creation of the item is predicted by
* a client. Only used in networking.
*/
Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal,
scene::IMesh* mesh, scene::IMesh* lowres_mesh)
scene::IMesh* mesh, scene::IMesh* lowres_mesh, bool is_predicted)
: ItemState(type)
{
assert(type != ITEM_TRIGGER); // use other constructor for that
m_distance_2 = 1.2f;
m_is_predicted = is_predicted;
initItem(type, xyz);
m_original_rotation = shortestArcQuat(Vec3(0, 1, 0), normal);
@@ -118,9 +127,9 @@ Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal,
m_original_lowmesh = lowres_mesh;
m_listener = NULL;
LODNode* lodnode = new LODNode("item",
irr_driver->getSceneManager()->getRootSceneNode(),
irr_driver->getSceneManager());
LODNode* lodnode =
new LODNode("item", irr_driver->getSceneManager()->getRootSceneNode(),
irr_driver->getSceneManager());
scene::ISceneNode* meshnode =
irr_driver->addMesh(mesh, StringUtils::insertValues("item_%i", (int)type));
@@ -161,6 +170,7 @@ Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal,
Item::Item(const Vec3& xyz, float distance, TriggerItemListener* trigger)
: ItemState(ITEM_TRIGGER)
{
m_is_predicted = false;
m_distance_2 = distance*distance;
initItem(ITEM_TRIGGER, xyz);
m_original_rotation = btQuaternion(0, 0, 0, 1);

16
src/items/item.hpp Normal file → Executable file
View File

@@ -63,6 +63,7 @@ public:
*/
class ItemState
{
LEAK_CHECK();
public:
/**
* The list of all items. Important for the switch item function:
@@ -257,7 +258,6 @@ class Item : public ItemState, public NoCopy
{
private:
LEAK_CHECK();
/** Stores the original rotation of an item. This is used in
* case of a switch to restore the rotation of a bubble gum
@@ -305,7 +305,10 @@ private:
/** The closest point to the left and right of this item at which it
* would not be collected. Used by the AI to avoid items. */
Vec3 *m_avoidance_points[2];
Vec3 *m_avoidance_points[2];
/** True if this item is predicted to exists. Used in networking only. */
bool m_is_predicted;
void setType(ItemType type);
void initItem(ItemType type, const Vec3 &xyz);
@@ -313,7 +316,8 @@ private:
public:
Item(ItemType type, const Vec3& xyz, const Vec3& normal,
scene::IMesh* mesh, scene::IMesh* lowres_mesh);
scene::IMesh* mesh, scene::IMesh* lowres_mesh,
bool is_predicted=false);
Item(const Vec3& xyz, float distance,
TriggerItemListener* trigger);
virtual ~Item ();
@@ -368,6 +372,12 @@ public:
/** Returns the XYZ position of the item. */
const Vec3& getXYZ() const { return m_xyz; }
// ------------------------------------------------------------------------
/** Sets if this is a predicted item or not. */
void setPredicted(bool p) { m_is_predicted = p; }
// ------------------------------------------------------------------------
/** Returns if this item is predicted or not. */
bool isPredicted() const { return m_is_predicted; }
// ------------------------------------------------------------------------
/** Returns the index of the graph node this item is on. */
int getGraphNode() const { return m_graph_node; }
// ------------------------------------------------------------------------

View File

@@ -31,10 +31,17 @@
*/
ItemEventInfo::ItemEventInfo(BareNetworkString *buffer, int *count)
{
m_type = (EventType)buffer->getUInt8();
m_ticks = buffer->getTime();
m_kart_id = buffer->getInt8();
m_index = buffer->getUInt16();
*count -= 7;
*count -= 8;
if (m_type == IEI_NEW)
{
m_xyz = buffer->getVec3();
*count -= 12;
}
} // ItemEventInfo(BareNetworkString, int *count)
//-----------------------------------------------------------------------------
@@ -44,10 +51,10 @@ ItemEventInfo::ItemEventInfo(BareNetworkString *buffer, int *count)
void ItemEventInfo::saveState(BareNetworkString *buffer)
{
assert(NetworkConfig::get()->isServer());
buffer->addTime(m_ticks).addUInt8(m_kart_id).addUInt16(m_index);
if(isNewItem())
{
}
buffer->addUInt8(m_type).addTime(m_ticks).addUInt8(m_kart_id)
.addUInt16(m_index);
if(m_type == IEI_NEW)
buffer->add(m_xyz);
} // saveState

View File

@@ -34,6 +34,9 @@ class BareNetworkString;
class ItemEventInfo
{
private:
/** Type of this event. */
enum EventType {IEI_COLLECT, IEI_NEW, IEI_SWITCH} m_type;
/** Time at which this event happens. */
int m_ticks;
@@ -56,7 +59,7 @@ public:
ItemEventInfo(int ticks, int index, int kart_id)
: m_ticks(ticks), m_index(index), m_kart_id(kart_id)
{
assert(kart_id >= 0);
m_type = IEI_COLLECT;
} // ItemEventInfo(collected existing item)
// --------------------------------------------------------------------
@@ -65,15 +68,17 @@ public:
* need to encode the new item type.
*/
ItemEventInfo(int ticks, ItemState::ItemType type, int index,
const Vec3 &xyz)
: m_ticks(ticks), m_index(index), m_kart_id(-1), m_xyz(xyz)
int kart_id, const Vec3 &xyz)
: m_ticks(ticks), m_index(index), m_kart_id(kart_id), m_xyz(xyz)
{
m_type = IEI_NEW;
} // ItemEventInfo(new item)
// --------------------------------------------------------------------
/** Constructor for switching items. */
ItemEventInfo(int ticks) : m_ticks(ticks), m_kart_id(-2)
ItemEventInfo(int ticks) : m_ticks(ticks)
{
m_type = IEI_SWITCH;
} // ItemEventInfo(switch)
// --------------------------------------------------------------------
@@ -82,13 +87,13 @@ public:
// --------------------------------------------------------------------
/** Returns if this event represents a new item. */
bool isNewItem() const { return m_kart_id == -1; }
bool isNewItem() const { return m_type == IEI_NEW; }
// --------------------------------------------------------------------
/** Returns true if this event represents collection of an item. */
bool isItemCollection() const { return m_kart_id >= 0; }
bool isItemCollection() const { return m_type == IEI_COLLECT; }
// --------------------------------------------------------------------
/** Returns true if this event represent a switch usage. */
bool isSwitch() const { return m_kart_id == -2; }
bool isSwitch() const { return m_type == IEI_SWITCH; }
// --------------------------------------------------------------------
/** Returns the index of this item. */
int getIndex() const { return m_index; }
@@ -100,7 +105,6 @@ public:
* to be called when this event is an item collection. */
int getKartId() const
{
assert(isItemCollection());
return m_kart_id;
} // getKartId
// --------------------------------------------------------------------
@@ -111,6 +115,13 @@ public:
assert(isNewItem());
return m_xyz;
} // getXYZ
// --------------------------------------------------------------------
/** Returns the type of this item. Note at this stage only bubble gums
* can be created during a race. */
ItemState::ItemType getNewItemType() const
{
return ItemState::ITEM_BUBBLEGUM;
} // getNewItemType
}; // class ItemEventInfo

8
src/items/item_manager.cpp Normal file → Executable file
View File

@@ -256,13 +256,15 @@ unsigned int ItemManager::insertItem(Item *item)
* bubblegum).
* \param type Type of the item.
* \param kart The kart that drops the new item.
* \param xyz Can be used to overwrite the item location (used in networking).
*/
Item* ItemManager::dropNewItem(ItemState::ItemType type, AbstractKart *kart)
Item* ItemManager::dropNewItem(ItemState::ItemType type,
AbstractKart *kart, const Vec3 *xyz)
{
Vec3 hit_point;
Vec3 normal;
const Material* material_hit;
Vec3 pos = kart->getXYZ();
Vec3 pos = xyz ? *xyz : kart->getXYZ();
Vec3 to = pos + kart->getTrans().getBasis() * Vec3(0, -10000, 0);
Track::getCurrentTrack()->getTriangleMesh().castRay(pos, to,
&hit_point,
@@ -286,8 +288,8 @@ Item* ItemManager::dropNewItem(ItemState::ItemType type, AbstractKart *kart)
Item* item = new Item(type, pos, normal, m_item_mesh[mesh_type],
m_item_lowres_mesh[mesh_type]);
insertItem(item);
if(kart != NULL) item->setParent(kart);
insertItem(item);
if(m_switch_ticks>=0)
{
ItemState::ItemType new_type = m_switch_to[item->getType()];

2
src/items/item_manager.hpp Normal file → Executable file
View File

@@ -118,7 +118,7 @@ public:
virtual Item* placeItem (ItemState::ItemType type, const Vec3& xyz,
const Vec3 &normal);
virtual Item* dropNewItem (ItemState::ItemType type,
AbstractKart* parent);
AbstractKart* parent, const Vec3 *xyz=NULL);
virtual Item* placeTrigger (const Vec3& xyz, float distance,
TriggerItemListener* listener);
void update (int ticks);

View File

@@ -90,7 +90,8 @@ unsigned int NetworkItemManager::insertItem(Item *item)
unsigned int index = ItemManager::insertItem(item);
if(index>=m_confirmed_state.size())
{
m_confirmed_state.push_back(NULL);
ItemState *is = new ItemState(*item);
m_confirmed_state.push_back(is);
}
else
{
@@ -109,6 +110,7 @@ void NetworkItemManager::collectedItem(Item *item, AbstractKart *kart)
{
if(NetworkConfig::get()->isServer())
{
// The server saves the collected item as item event info
m_item_events.lock();
m_item_events.getData().emplace_back(World::getWorld()->getTimeTicks(),
item->getItemId(),
@@ -118,8 +120,7 @@ void NetworkItemManager::collectedItem(Item *item, AbstractKart *kart)
}
else
{
// If we are predicting (i.e. not rewinding), the client
// predicts item collection:
// The client predicts item collection:
ItemManager::collectedItem(item, kart);
}
} // collectedItem
@@ -127,18 +128,30 @@ void NetworkItemManager::collectedItem(Item *item, AbstractKart *kart)
// ----------------------------------------------------------------------------
/** Called when a new item is created, e.g. bubble gum.
* \param type Type of the item.
* \param parent In case of a dropped item used to avoid that a kart
* \param kart In case of a dropped item used to avoid that a kart
* is affected by its own items.
* \param xyz Location of the item. If specified will override the
* kart position (used in networking only).
*/
Item* NetworkItemManager::dropNewItem(ItemState::ItemType type,
AbstractKart *kart)
AbstractKart *kart, const Vec3 *xyz)
{
Item *item = ItemManager::dropNewItem(type, kart);
Item *item = ItemManager::dropNewItem(type, kart, xyz);
if(!item) return NULL;
if (NetworkConfig::get()->isClient())
{
// If this is called when replaying a server event, the calling
// function restoreState will set the item to be not-predicted.
item->setPredicted(true);
return item;
}
// Server: store the data for this event:
m_item_events.lock();
m_item_events.getData().emplace_back(World::getWorld()->getTimeTicks(), type,
item->getItemId(),
m_item_events.getData().emplace_back(World::getWorld()->getTimeTicks(),
type, item->getItemId(),
kart->getWorldKartId(),
kart->getXYZ() );
m_item_events.unlock();
return item;
@@ -203,8 +216,7 @@ BareNetworkString* NetworkItemManager::saveState()
uint16_t n = (uint16_t)m_item_events.getData().size();
if(n==0)
{
BareNetworkString *s =
new BareNetworkString();
BareNetworkString *s = new BareNetworkString();
m_item_events.unlock();
return s;
}
@@ -245,13 +257,42 @@ void NetworkItemManager::forwardTime(int ticks)
*/
void NetworkItemManager::restoreState(BareNetworkString *buffer, int count)
{
assert(NetworkConfig::get()->isClient());
// 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().
// state in this instance was taken at m_confirmed_state_time. First
// forward this confirmed state to the current time (i.e. world time).
// This is done in several steps:
// 1) First remove all client-side predicted items from the list of all
// items, and store them for now in a predictem_item cache. Predicted
// item only happen between m_confirmed_state_time and 'now'.
// 2) Apply all events included in this state to the confirmed state.
// a) When a collection event is found, adjust the confirmed item state
// only (this state will later be copied to the current item state).
// b) When a new item is created, search in the item cache to see
// if a predicted item for this slot already exists to speed up
// item creation (if not, create a new item). Put this new item
// into the current item state as well as in the confirmed state
// (in the same index position).
// 3) Once all new events have been applied to the confirmed state the
// time must be <= world time. Forward the confirmed state to
// world time, and update m_confirmed_state_time to the world time.
// From here the replay can happen.
// 1) Remove predicted items:
for (unsigned int i=0; i<m_all_items.size(); i++)
{
Item *item = m_all_items[i];
if(item && item->isPredicted())
{
Log::verbose("NIM", "Deleting item %d at %d",
item->getItemId(), World::getWorld()->getTimeTicks());
delete item;
m_all_items[i] = NULL;
}
}
// 2) Apply all events to current confirmed state:
int current_time = m_confirmed_state_time;
while(count > 0)
{
@@ -272,14 +313,22 @@ void NetworkItemManager::restoreState(BareNetworkString *buffer, int count)
if (dt>0) forwardTime(dt);
// TODO: apply the various events types, atm only collection is supported:
ItemState *item_state = m_confirmed_state[iei.getIndex()];
if(iei.isItemCollection())
{
ItemState *item_state = m_confirmed_state[iei.getIndex()];
// An item on the track was collected:
AbstractKart *kart = World::getWorld()->getKart(iei.getKartId());
m_confirmed_state[iei.getIndex()]->collected(kart);
}
else if(iei.isNewItem())
{
AbstractKart *kart = World::getWorld()->getKart(iei.getKartId());
Item *item = dropNewItem(ItemState::ITEM_BUBBLEGUM,
kart, &iei.getXYZ() );
item->setPredicted(false);
Log::verbose("NIM", "item %d not predicted at %d due to server info",
item->getItemId(), World::getWorld()->getTimeTicks());
}
current_time = iei.getTicks();
} // while count >0
@@ -297,8 +346,7 @@ void NetworkItemManager::restoreState(BareNetworkString *buffer, int count)
{
Item *item = m_all_items[i];
const ItemState *is = m_confirmed_state[i];
if(is)
*(ItemState*)item = *is;
if (is && item) *(ItemState*)item = *is;
}
// Now we save the current local

View File

@@ -68,8 +68,8 @@ public:
virtual void reset();
virtual void setItemConfirmationTime(int host_id, int ticks) OVERRIDE;
virtual void collectedItem(Item *item, AbstractKart *kart) OVERRIDE;
virtual Item* dropNewItem(ItemState::ItemType type,
AbstractKart *kart) OVERRIDE;
virtual Item* dropNewItem(ItemState::ItemType type, AbstractKart *kart,
const Vec3 *xyz=NULL) OVERRIDE;
virtual BareNetworkString* saveState() OVERRIDE;
virtual void restoreState(BareNetworkString *buffer, int count) OVERRIDE;
// ------------------------------------------------------------------------