Only store IDs across ticks
This commit is contained in:
parent
63adc6d7dc
commit
e98f93a079
@ -73,6 +73,7 @@ cEntity::cEntity(eEntityType a_EntityType, Vector3d a_Pos, double a_Width, doubl
|
|||||||
m_Height(a_Height),
|
m_Height(a_Height),
|
||||||
m_InvulnerableTicks(0)
|
m_InvulnerableTicks(0)
|
||||||
{
|
{
|
||||||
|
m_WorldChangeInfo.m_NewWorld = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1406,7 +1407,7 @@ bool cEntity::DetectPortal()
|
|||||||
cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedOverworldName());
|
cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedOverworldName());
|
||||||
ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
|
ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
|
||||||
LOGD("Jumping %s -> %s", DimensionToString(dimNether).c_str(), DimensionToString(DestionationDim).c_str());
|
LOGD("Jumping %s -> %s", DimensionToString(dimNether).c_str(), DimensionToString(DestionationDim).c_str());
|
||||||
new cNetherPortalScanner(this, TargetWorld, TargetPos, cChunkDef::Height);
|
new cNetherPortalScanner(*this, TargetWorld, TargetPos, cChunkDef::Height);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Nether portal in the overworld
|
// Nether portal in the overworld
|
||||||
@ -1438,7 +1439,7 @@ bool cEntity::DetectPortal()
|
|||||||
cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedNetherWorldName());
|
cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedNetherWorldName());
|
||||||
ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
|
ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
|
||||||
LOGD("Jumping %s -> %s", DimensionToString(dimOverworld).c_str(), DimensionToString(DestionationDim).c_str());
|
LOGD("Jumping %s -> %s", DimensionToString(dimOverworld).c_str(), DimensionToString(DestionationDim).c_str());
|
||||||
new cNetherPortalScanner(this, TargetWorld, TargetPos, (cChunkDef::Height / 2));
|
new cNetherPortalScanner(*this, TargetWorld, TargetPos, (cChunkDef::Height / 2));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1587,26 +1588,47 @@ bool cEntity::MoveToWorld(cWorld * a_World, Vector3d a_NewPosition, bool a_SetPo
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new world change info
|
if (m_WorldChangeInfo.m_NewWorld != nullptr)
|
||||||
auto NewWCI = cpp14::make_unique<sWorldChangeInfo>();
|
|
||||||
*NewWCI = { a_World, a_NewPosition, a_SetPortalCooldown, a_ShouldSendRespawn };
|
|
||||||
|
|
||||||
// Publish atomically
|
|
||||||
auto OldWCI = m_WorldChangeInfo.exchange(std::move(NewWCI));
|
|
||||||
|
|
||||||
if (OldWCI == nullptr)
|
|
||||||
{
|
{
|
||||||
// Schedule a new world change.
|
// Avoid scheduling multiple warp tasks
|
||||||
GetWorld()->QueueTask(
|
return true;
|
||||||
[this](cWorld & a_CurWorld)
|
|
||||||
{
|
|
||||||
auto WCI = m_WorldChangeInfo.exchange(nullptr);
|
|
||||||
cWorld::cLock Lock(a_CurWorld);
|
|
||||||
DoMoveToWorld(*WCI);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create new world change info
|
||||||
|
m_WorldChangeInfo = { a_World, a_NewPosition, a_SetPortalCooldown, a_ShouldSendRespawn };
|
||||||
|
|
||||||
|
// TODO: move to capture when C++14
|
||||||
|
const auto EntityID = GetUniqueID();
|
||||||
|
|
||||||
|
/* Requirements:
|
||||||
|
Only one world change in-flight at any time
|
||||||
|
No ticking during world changes
|
||||||
|
The last invocation takes effect
|
||||||
|
|
||||||
|
As of writing, cWorld ticks entities, clients, and then processes tasks
|
||||||
|
We may call MoveToWorld (any number of times - consider multiple /portal commands within a tick) in the first and second stages
|
||||||
|
Queue a task onto the third stage to invoke DoMoveToWorld ONCE with the last given destination world
|
||||||
|
Store entity IDs in case client tick found the player disconnected and immediately destroys the object
|
||||||
|
|
||||||
|
After the move begins, no further calls to MoveToWorld is possible since neither the client nor entity is ticked
|
||||||
|
This remains until the warp is complete and the destination world resumes ticking.
|
||||||
|
*/
|
||||||
|
GetWorld()->QueueTask(
|
||||||
|
[EntityID](cWorld & a_CurWorld)
|
||||||
|
{
|
||||||
|
a_CurWorld.DoWithEntityByID(
|
||||||
|
EntityID,
|
||||||
|
[](cEntity & a_Entity)
|
||||||
|
{
|
||||||
|
auto & WCI = a_Entity.m_WorldChangeInfo;
|
||||||
|
a_Entity.DoMoveToWorld(WCI);
|
||||||
|
WCI.m_NewWorld = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -489,7 +489,7 @@ public:
|
|||||||
/** Returns true if a world change is scheduled to happen. */
|
/** Returns true if a world change is scheduled to happen. */
|
||||||
bool IsWorldChangeScheduled() const
|
bool IsWorldChangeScheduled() const
|
||||||
{
|
{
|
||||||
return (m_WorldChangeInfo.load() != nullptr);
|
return (m_WorldChangeInfo.m_NewWorld != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Updates clients of changes in the entity. */
|
/** Updates clients of changes in the entity. */
|
||||||
@ -662,8 +662,8 @@ protected:
|
|||||||
|
|
||||||
cWorld * m_World;
|
cWorld * m_World;
|
||||||
|
|
||||||
/** If not nullptr, a world change is scheduled and a task is queued in the current world. */
|
/** If field m_NewWorld not nullptr, a world change is scheduled and a task is queued in the current world. */
|
||||||
cAtomicUniquePtr<sWorldChangeInfo> m_WorldChangeInfo;
|
sWorldChangeInfo m_WorldChangeInfo;
|
||||||
|
|
||||||
/** Whether the entity is capable of taking fire or lava damage. */
|
/** Whether the entity is capable of taking fire or lava damage. */
|
||||||
bool m_IsFireproof;
|
bool m_IsFireproof;
|
||||||
|
@ -17,8 +17,9 @@ const double cNetherPortalScanner::AcrossOffset = 0.5;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
cNetherPortalScanner::cNetherPortalScanner(cEntity * a_MovingEntity, cWorld * a_DestinationWorld, Vector3d a_DestPosition, int a_MaxY) :
|
cNetherPortalScanner::cNetherPortalScanner(cEntity & a_MovingEntity, cWorld * a_DestinationWorld, Vector3d a_DestPosition, int a_MaxY) :
|
||||||
m_Entity(a_MovingEntity),
|
m_EntityID(a_MovingEntity.GetUniqueID()),
|
||||||
|
m_SourceWorld(*a_MovingEntity.GetWorld()),
|
||||||
m_World(a_DestinationWorld),
|
m_World(a_DestinationWorld),
|
||||||
m_FoundPortal(false),
|
m_FoundPortal(false),
|
||||||
m_BuildPlatform(true),
|
m_BuildPlatform(true),
|
||||||
@ -296,8 +297,18 @@ void cNetherPortalScanner::OnDisabled(void)
|
|||||||
Position.z += OutOffset;
|
Position.z += OutOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
FLOGD("Placing player at {0}", Position);
|
// Lookup our warping entity by ID
|
||||||
m_Entity->MoveToWorld(m_World, Position, true, false);
|
// Necessary as they may have been destroyed in the meantime (#4582)
|
||||||
|
m_SourceWorld.DoWithEntityByID(
|
||||||
|
m_EntityID,
|
||||||
|
[this, &Position](cEntity & a_Entity)
|
||||||
|
{
|
||||||
|
FLOGD("Placing player at {0}", Position);
|
||||||
|
a_Entity.MoveToWorld(m_World, Position, true, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
delete this;
|
delete this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ class cWorld;
|
|||||||
class cNetherPortalScanner : public cChunkStay
|
class cNetherPortalScanner : public cChunkStay
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
cNetherPortalScanner(cEntity * a_MovingEntity, cWorld * a_DestinationWorld, Vector3d a_DestPosition, int a_MaxY);
|
cNetherPortalScanner(cEntity & a_MovingEntity, cWorld * a_DestinationWorld, Vector3d a_DestPosition, int a_MaxY);
|
||||||
virtual void OnChunkAvailable(int a_ChunkX, int a_ChunkY) override;
|
virtual void OnChunkAvailable(int a_ChunkX, int a_ChunkY) override;
|
||||||
virtual bool OnAllChunksAvailable(void) override;
|
virtual bool OnAllChunksAvailable(void) override;
|
||||||
virtual void OnDisabled(void) override;
|
virtual void OnDisabled(void) override;
|
||||||
@ -48,8 +48,11 @@ private:
|
|||||||
/** Whether the given location is a valid location to build a portal. */
|
/** Whether the given location is a valid location to build a portal. */
|
||||||
bool IsValidBuildLocation(Vector3i a_BlockPosition);
|
bool IsValidBuildLocation(Vector3i a_BlockPosition);
|
||||||
|
|
||||||
/** The entity that's being moved. */
|
/** The ID of the entity that's being moved. */
|
||||||
cEntity * m_Entity;
|
UInt32 m_EntityID;
|
||||||
|
|
||||||
|
/** The world we're moving the entity from, used to query the entity ID. */
|
||||||
|
cWorld & m_SourceWorld;
|
||||||
|
|
||||||
/** The world we're moving the entity to. */
|
/** The world we're moving the entity to. */
|
||||||
cWorld * m_World;
|
cWorld * m_World;
|
||||||
|
Loading…
Reference in New Issue
Block a user