summaryrefslogtreecommitdiffstats
path: root/src/Entities
diff options
context:
space:
mode:
Diffstat (limited to 'src/Entities')
-rw-r--r--src/Entities/Entity.cpp121
-rw-r--r--src/Entities/Entity.h10
-rw-r--r--src/Entities/Player.cpp21
-rw-r--r--src/Entities/Player.h10
4 files changed, 112 insertions, 50 deletions
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 334cf5aa7..a7b6cca27 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -1051,15 +1051,16 @@ void cEntity::DetectPortal()
class cPortalChunkLoader : public cChunkStay
{
public:
- cPortalChunkLoader(cEntity * a_Entity, Vector3i & a_PortalPos) :
+ cPortalChunkLoader(cEntity * a_Entity, cWorld & a_World, Vector3i & a_PortalPos) :
m_Entity(a_Entity),
- m_PortalPos(a_PortalPos)
+ m_PortalPos(a_PortalPos),
+ m_World(a_World)
{}
private:
virtual bool OnAllChunksAvailable(void) override
{
- m_Entity->CreateExitPortal(m_PortalPos.x, m_PortalPos.y, m_PortalPos.z);
+ cEntity::CreateExitPortal(m_PortalPos.x, m_PortalPos.y, m_PortalPos.z, m_Entity->GetWidth(), m_Entity->GetHeight(), m_World, m_Entity->GetUniqueID());
return true;
}
@@ -1068,6 +1069,7 @@ void cEntity::DetectPortal()
cEntity * m_Entity;
Vector3i m_PortalPos;
+ cWorld & m_World;
};
int X = POSX_TOINT, Y = POSY_TOINT, Z = POSZ_TOINT;
@@ -1077,106 +1079,167 @@ void cEntity::DetectPortal()
{
case E_BLOCK_NETHER_PORTAL:
{
- if (!GetWorld()->AreNetherPortalsEnabled())
+ if (!GetWorld()->AreNetherPortalsEnabled() || m_PortalCooldownData.second)
{
return;
}
+ if (m_PortalCooldownData.first != 80)
+ {
+ m_PortalCooldownData.first++;
+ return;
+ }
+ m_PortalCooldownData.first = 0;
+
switch (GetWorld()->GetDimension())
{
- case dimNether: MoveToWorld(GetWorld()->GetLinkedOverworldName(), cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName())); break;
+ case dimNether:
+ {
+ m_PortalCooldownData.second = true; // Stop portals from working on respawn
+
+ if (IsPlayer())
+ {
+ ((cPlayer *)this)->GetClientHandle()->SendRespawn(dimOverworld);
+ }
+ MoveToWorld(GetWorld()->GetLinkedOverworldName(), cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName()), false);
+
+ return;
+ }
case dimOverworld:
{
+ m_PortalCooldownData.second = true; // Stop portals from working on respawn
+
if (IsPlayer())
{
((cPlayer *)this)->AwardAchievement(achEnterPortal);
+ ((cPlayer *)this)->GetClientHandle()->SendRespawn(dimNether);
}
- MoveToWorld(GetWorld()->GetNetherWorldName(), cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetNetherWorldName(), dimNether, GetWorld()->GetName()));
+ MoveToWorld(GetWorld()->GetNetherWorldName(), cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetNetherWorldName(), dimNether, GetWorld()->GetName()), false);
- cChunkStay * Stay = new cPortalChunkLoader(this, Vector3i(X, Y, Z));
+ cChunkStay * Stay = new cPortalChunkLoader(this, *cRoot::Get()->GetWorld(GetWorld()->GetNetherWorldName()), Vector3i(X, Y, Z));
int MinChunkX, MaxChunkX;
int MinChunkZ, MaxChunkZ;
cChunkDef::BlockToChunk(X - 128, Z - 128, MinChunkX, MinChunkZ);
cChunkDef::BlockToChunk(X + 128, Z + 128, MaxChunkX, MaxChunkZ);
- for (int OtherMinChunkX = MinChunkX; OtherMinChunkX <= MaxChunkX; ++OtherMinChunkX)
+ for (int ChunkX = MinChunkX; ChunkX <= MaxChunkX; ++ChunkX)
{
- for (int OtherMinChunkZ = MinChunkZ; OtherMinChunkZ <= MaxChunkZ; ++OtherMinChunkZ)
+ for (int ChunkZ = MinChunkZ; ChunkZ <= MaxChunkZ; ++ChunkZ)
{
- Stay->Add(OtherMinChunkX, OtherMinChunkZ);
+ LOG("Queue %i %i", ChunkX, ChunkZ);
+ Stay->Add(ChunkX, ChunkZ);
}
}
Stay->Enable(*GetWorld()->GetChunkMap());
- break;
+ return;
}
default: break;
}
- break;
+ return;
}
case E_BLOCK_END_PORTAL:
{
- if (!GetWorld()->AreEndPortalsEnabled())
+ if (!GetWorld()->AreEndPortalsEnabled() || m_PortalCooldownData.second)
+ {
+ return;
+ }
+
+ if (m_PortalCooldownData.first != 80)
{
+ m_PortalCooldownData.first++;
return;
}
+ m_PortalCooldownData.first = 0;
switch (GetWorld()->GetDimension())
{
case dimEnd:
{
- MoveToWorld(GetWorld()->GetLinkedOverworldName(), cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName()));
+ m_PortalCooldownData.second = true; // Stop portals from working on respawn
if (IsPlayer())
{
cPlayer * Player = (cPlayer *)this;
Player->TeleportToCoords(Player->GetLastBedPos().x, Player->GetLastBedPos().y, Player->GetLastBedPos().z);
+ Player->GetClientHandle()->SendRespawn(dimOverworld);
}
- break;
+ MoveToWorld(GetWorld()->GetLinkedOverworldName(), cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName()), false);
+
+ return;
}
case dimOverworld:
{
+ m_PortalCooldownData.second = true; // Stop portals from working on respawn
+
if (IsPlayer())
{
((cPlayer *)this)->AwardAchievement(achEnterTheEnd);
+ ((cPlayer *)this)->GetClientHandle()->SendRespawn(dimEnd);
}
- MoveToWorld(GetWorld()->GetEndWorldName(), cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetEndWorldName(), dimEnd, GetWorld()->GetName()));
- break;
+ MoveToWorld(GetWorld()->GetEndWorldName(), cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetEndWorldName(), dimEnd, GetWorld()->GetName()), false);
+
+ return;
}
default: break;
}
+ return;
}
default: break;
}
}
+
+ // Allow portals to work again
+ m_PortalCooldownData.second = false;
+ m_PortalCooldownData.first = 0;
}
-void cEntity::CreateExitPortal(int a_BlockX, int a_BlockY, int a_BlockZ)
+void cEntity::CreateExitPortal(int a_BlockX, int a_BlockY, int a_BlockZ, double a_EntityWidth, double a_EntityHeight, cWorld & a_World, int a_UniqueIDToTeleport)
{
cBlockArea Area;
- Area.Read(GetWorld(), a_BlockX - 128, a_BlockX + 128, 0, 128, a_BlockZ - 128, a_BlockZ + 128);
+ Area.Read(&a_World, a_BlockX - 128, a_BlockX + 128, 0, 128, a_BlockZ - 128, a_BlockZ + 128);
for (int x = a_BlockX - 128; x <= a_BlockX + 128; ++x) for (int y = 0; y <= 128; ++y) for (int z = a_BlockZ - 128; z <= a_BlockZ + 128; ++z)
{
if (
(Area.GetBlockType(x, y, z) == E_BLOCK_NETHER_PORTAL) &&
(
- (Area.GetBlockType(x, (int)floor(y + GetHeight()), z) == E_BLOCK_NETHER_PORTAL) ||
- (Area.GetBlockType(x, (int)floor(y - GetHeight()), z) == E_BLOCK_NETHER_PORTAL)
+ (Area.GetBlockType(x, (int)floor(y + a_EntityHeight), z) == E_BLOCK_NETHER_PORTAL) ||
+ (Area.GetBlockType(x, (int)floor(y - a_EntityHeight), z) == E_BLOCK_NETHER_PORTAL)
)
)
{
- TeleportToCoords(x, y, z);
+ class cTeleportEntityToPortalCallback : public cEntityCallback
+ {
+ public:
+ cTeleportEntityToPortalCallback(int a_X, int a_Y, int a_Z) :
+ m_X(a_X),
+ m_Y(a_Y),
+ m_Z(a_Z)
+ {}
+
+ virtual bool Item(cEntity * a_Entity) override
+ {
+ a_Entity->TeleportToCoords(m_X, m_Y, m_Z);
+ return true;
+ }
+
+ private:
+ int m_X, m_Y, m_Z;
+ };
+
+ cTeleportEntityToPortalCallback TETPC(x, y, z);
+ a_World.DoWithEntityByID(a_UniqueIDToTeleport, TETPC);
return;
}
}
- int MinX = std::max(a_BlockX - (int)ceil(GetWidth()), a_BlockX - 2), MaxX = std::max(a_BlockX + (int)ceil(GetWidth()), a_BlockX + 1);
- int MinY = std::max(a_BlockY - (int)ceil(GetHeight()), a_BlockY - 2), MaxY = std::max(a_BlockY + (int)ceil(GetHeight()), a_BlockY + 1);
+ int MinX = std::max(a_BlockX - (int)ceil(a_EntityWidth), a_BlockX - 2), MaxX = std::max(a_BlockX + (int)ceil(a_EntityWidth), a_BlockX + 1);
+ int MinY = std::max(a_BlockY - (int)ceil(a_EntityHeight), a_BlockY - 2), MaxY = std::max(a_BlockY + (int)ceil(a_EntityHeight), a_BlockY + 1);
for (int y = MinY; y < MaxY + 1; y += MaxY - MinY) for (int x = MinX; x < MaxX + 1; ++x)
{
@@ -1187,15 +1250,17 @@ void cEntity::CreateExitPortal(int a_BlockX, int a_BlockY, int a_BlockZ)
Area.SetBlockType(x, y, a_BlockZ, E_BLOCK_OBSIDIAN);
}
- Area.Write(GetWorld(), MinX, MinY, a_BlockZ);
+ Area.Write(&a_World, MinX, MinY, a_BlockZ);
}
-bool cEntity::MoveToWorld(const AString & a_WorldName, cWorld * a_World)
+bool cEntity::MoveToWorld(const AString & a_WorldName, cWorld * a_World, bool a_ShouldSendRespawn)
{
+ UNUSED(a_ShouldSendRespawn);
+
cWorld * World;
if (a_World == NULL)
{
@@ -1213,6 +1278,7 @@ bool cEntity::MoveToWorld(const AString & a_WorldName, cWorld * a_World)
if (GetWorld() == World)
{
+ // Don't move to same world
return false;
}
@@ -1220,8 +1286,7 @@ bool cEntity::MoveToWorld(const AString & a_WorldName, cWorld * a_World)
GetWorld()->RemoveEntity(this);
GetWorld()->BroadcastDestroyEntity(*this);
- // Add to all the necessary parts of the new world
- SetWorld(World);
+ // Queue add to new world
World->AddEntity(this);
return true;
diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h
index 934e0302b..4d922aa11 100644
--- a/src/Entities/Entity.h
+++ b/src/Entities/Entity.h
@@ -338,7 +338,7 @@ public:
virtual void OnFinishedBurning(void);
/** Creates exit portal at given coordinates */
- void CreateExitPortal(int a_BlockX, int a_BlockY, int a_BlockZ);
+ static void CreateExitPortal(int a_BlockX, int a_BlockY, int a_BlockZ, double a_EntityWidth, double a_EntityHeight, cWorld & a_World, int a_UniqueIDToTeleport);
// tolua_begin
@@ -374,7 +374,7 @@ public:
virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ);
/** Moves entity to specified world */
- virtual bool MoveToWorld(const AString & a_WorldName, cWorld * a_World = NULL);
+ virtual bool MoveToWorld(const AString & a_WorldName, cWorld * a_World = NULL, bool a_ShouldSendRespawn = true);
// tolua_end
@@ -510,6 +510,12 @@ protected:
/** Air level of a mobile */
int m_AirLevel;
int m_AirTickTimer;
+
+ /** Portal delay timer and cooldown boolean
+ First value is to delay sending the repsawn packet (which triggers the Entering the {Dimension} screen).
+ Second value is to prevent a teleportation loop by ensuring we do not reenter a portal that we came out of.
+ */
+ std::pair<unsigned short, bool> m_PortalCooldownData;
private:
/** Measured in degrees, [-180, +180) */
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 4d6688694..a346e68cf 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -948,7 +948,7 @@ void cPlayer::Respawn(void)
m_LifetimeTotalXp = 0;
// ToDo: send score to client? How?
- m_ClientHandle->SendRespawn(*GetWorld());
+ m_ClientHandle->SendRespawn(GetWorld()->GetDimension());
// Extinguish the fire:
StopBurning();
@@ -1570,7 +1570,7 @@ void cPlayer::TossItems(const cItems & a_Items)
-bool cPlayer::MoveToWorld(const AString & a_WorldName, cWorld * a_World)
+bool cPlayer::MoveToWorld(const AString & a_WorldName, cWorld * a_World, bool a_ShouldSendRespawn)
{
cWorld * World;
if (a_World == NULL)
@@ -1589,31 +1589,22 @@ bool cPlayer::MoveToWorld(const AString & a_WorldName, cWorld * a_World)
if (GetWorld() == World)
{
+ // Don't move to same world
return false;
}
// Send the respawn packet:
- if (m_ClientHandle != NULL)
+ if (a_ShouldSendRespawn && (m_ClientHandle != NULL))
{
- m_ClientHandle->SendRespawn(*World);
+ m_ClientHandle->SendRespawn(World->GetDimension());
}
- // Remove all links to the old world
+ // Remove player from old world
m_World->RemovePlayer(this);
- // If the dimension is different, we can send the respawn packet
- // http://wiki.vg/Protocol#0x09 says "don't send if dimension is the same" as of 2013_07_02
-
// Queue adding player to the new world, including all the necessary adjustments to the object
World->AddPlayer(this);
- if (GetWorld()->GetDimension() != World->GetDimension())
- {
- GetClientHandle()->SendPlayerMoveLook();
- GetClientHandle()->SendHealth();
- GetClientHandle()->SendWholeInventory(*GetWindow());
- }
-
return true;
}
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index 99a0e601c..8f319f1ae 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -323,14 +323,14 @@ public:
virtual void Killed(cEntity * a_Victim) override;
- void Respawn(void); // tolua_export
+ void Respawn(void); // tolua_export
- void SetVisible( bool a_bVisible ); // tolua_export
- bool IsVisible(void) const { return m_bVisible; } // tolua_export
+ void SetVisible(bool a_bVisible); // tolua_export
+ bool IsVisible(void) const { return m_bVisible; } // tolua_export
/** Moves the player to the specified world.
Returns true if successful, false on failure (world not found). */
- virtual bool MoveToWorld(const AString & a_WorldName, cWorld * a_World = NULL) override; // tolua_export
+ virtual bool MoveToWorld(const AString & a_WorldName, cWorld * a_World = NULL, bool a_ShouldSendRespawn = true) override; // tolua_export
/** Saves all player data, such as inventory, to JSON */
bool SaveToDisk(void);
@@ -339,7 +339,7 @@ public:
Takes a (NULL) cWorld pointer which it will assign a value to based on either the loaded world or default world
*/
bool LoadFromDisk(cWorld *& a_World);
- void LoadPermissionsFromDisk(void); // tolua_export
+ void LoadPermissionsFromDisk(void); // tolua_export
const AString & GetLoadedWorldName() { return m_LoadedWorldName; }