summaryrefslogtreecommitdiffstats
path: root/src/Entities
diff options
context:
space:
mode:
authorTiger Wang <ziwei.tiger@outlook.com>2020-04-05 14:41:14 +0200
committerTiger Wang <ziwei.tiger@outlook.com>2020-04-18 20:51:30 +0200
commite98f93a079c2cfea7b5478e2cb2934874e0b88e0 (patch)
treebf4726fd6c2a31da5bb6aab973354654a88f13ce /src/Entities
parentAdd API documentation for repeater metadata constants (diff)
downloadcuberite-e98f93a079c2cfea7b5478e2cb2934874e0b88e0.tar
cuberite-e98f93a079c2cfea7b5478e2cb2934874e0b88e0.tar.gz
cuberite-e98f93a079c2cfea7b5478e2cb2934874e0b88e0.tar.bz2
cuberite-e98f93a079c2cfea7b5478e2cb2934874e0b88e0.tar.lz
cuberite-e98f93a079c2cfea7b5478e2cb2934874e0b88e0.tar.xz
cuberite-e98f93a079c2cfea7b5478e2cb2934874e0b88e0.tar.zst
cuberite-e98f93a079c2cfea7b5478e2cb2934874e0b88e0.zip
Diffstat (limited to 'src/Entities')
-rw-r--r--src/Entities/Entity.cpp58
-rw-r--r--src/Entities/Entity.h6
2 files changed, 43 insertions, 21 deletions
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 23a29a3a4..e0ea32e78 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -73,6 +73,7 @@ cEntity::cEntity(eEntityType a_EntityType, Vector3d a_Pos, double a_Width, doubl
m_Height(a_Height),
m_InvulnerableTicks(0)
{
+ m_WorldChangeInfo.m_NewWorld = nullptr;
}
@@ -1406,7 +1407,7 @@ bool cEntity::DetectPortal()
cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedOverworldName());
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());
- new cNetherPortalScanner(this, TargetWorld, TargetPos, cChunkDef::Height);
+ new cNetherPortalScanner(*this, TargetWorld, TargetPos, cChunkDef::Height);
return true;
}
// Nether portal in the overworld
@@ -1438,7 +1439,7 @@ bool cEntity::DetectPortal()
cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedNetherWorldName());
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());
- new cNetherPortalScanner(this, TargetWorld, TargetPos, (cChunkDef::Height / 2));
+ new cNetherPortalScanner(*this, TargetWorld, TargetPos, (cChunkDef::Height / 2));
return true;
}
}
@@ -1587,25 +1588,46 @@ bool cEntity::MoveToWorld(cWorld * a_World, Vector3d a_NewPosition, bool a_SetPo
return false;
}
+ if (m_WorldChangeInfo.m_NewWorld != nullptr)
+ {
+ // Avoid scheduling multiple warp tasks
+ return true;
+ }
+
// Create new world change info
- auto NewWCI = cpp14::make_unique<sWorldChangeInfo>();
- *NewWCI = { a_World, a_NewPosition, a_SetPortalCooldown, a_ShouldSendRespawn };
+ m_WorldChangeInfo = { a_World, a_NewPosition, a_SetPortalCooldown, a_ShouldSendRespawn };
- // Publish atomically
- auto OldWCI = m_WorldChangeInfo.exchange(std::move(NewWCI));
+ // TODO: move to capture when C++14
+ const auto EntityID = GetUniqueID();
- if (OldWCI == nullptr)
- {
- // Schedule a new world change.
- GetWorld()->QueueTask(
- [this](cWorld & a_CurWorld)
- {
- auto WCI = m_WorldChangeInfo.exchange(nullptr);
- cWorld::cLock Lock(a_CurWorld);
- DoMoveToWorld(*WCI);
- }
- );
- }
+ /* 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;
}
diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h
index 25784081f..573bc34cf 100644
--- a/src/Entities/Entity.h
+++ b/src/Entities/Entity.h
@@ -489,7 +489,7 @@ public:
/** Returns true if a world change is scheduled to happen. */
bool IsWorldChangeScheduled() const
{
- return (m_WorldChangeInfo.load() != nullptr);
+ return (m_WorldChangeInfo.m_NewWorld != nullptr);
}
/** Updates clients of changes in the entity. */
@@ -662,8 +662,8 @@ protected:
cWorld * m_World;
- /** If not nullptr, a world change is scheduled and a task is queued in the current world. */
- cAtomicUniquePtr<sWorldChangeInfo> m_WorldChangeInfo;
+ /** If field m_NewWorld not nullptr, a world change is scheduled and a task is queued in the current world. */
+ sWorldChangeInfo m_WorldChangeInfo;
/** Whether the entity is capable of taking fire or lava damage. */
bool m_IsFireproof;