From be3111d133cf01fe266d1e267acdb9f22f98f513 Mon Sep 17 00:00:00 2001 From: JK2K Date: Sat, 2 Oct 2021 22:28:24 +0200 Subject: Perform bed checks upon respawn (#5300) Co-authored-by: Tiger Wang --- src/Entities/Entity.cpp | 2 +- src/Entities/Player.cpp | 56 ++++++++++++++++++++++++++++++++++++++----------- src/Entities/Player.h | 36 +++++++++++++++++++------------ 3 files changed, 68 insertions(+), 26 deletions(-) (limited to 'src/Entities') diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 74b184c54..e275d03da 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -1500,7 +1500,7 @@ bool cEntity::DetectPortal() if (IsPlayer()) { cPlayer * Player = static_cast(this); - if (Player->GetBedWorld() == TargetWorld) + if (Player->GetRespawnWorld() == TargetWorld) { return MoveToWorld(*TargetWorld, Player->GetLastBedPos()); } diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index b4d806c4f..b99c0227a 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -786,8 +786,7 @@ void cPlayer::SetCustomName(const AString & a_CustomName) void cPlayer::SetBedPos(const Vector3i a_Position) { - m_LastBedPos = a_Position; - m_SpawnWorldName = m_World->GetName(); + SetBedPos(a_Position, *m_World); } @@ -796,7 +795,8 @@ void cPlayer::SetBedPos(const Vector3i a_Position) void cPlayer::SetBedPos(const Vector3i a_Position, const cWorld & a_World) { - m_LastBedPos = a_Position; + m_RespawnPosition = a_Position; + m_IsRespawnPointForced = false; m_SpawnWorldName = a_World.GetName(); } @@ -804,7 +804,18 @@ void cPlayer::SetBedPos(const Vector3i a_Position, const cWorld & a_World) -cWorld * cPlayer::GetBedWorld() +void cPlayer::SetRespawnPosition(const Vector3i a_Position, const cWorld & a_World) +{ + m_RespawnPosition = a_Position; + m_IsRespawnPointForced = true; + m_SpawnWorldName = a_World.GetName(); +} + + + + + +cWorld * cPlayer::GetRespawnWorld() { if (const auto World = cRoot::Get()->GetWorld(m_SpawnWorldName); World != nullptr) { @@ -943,14 +954,33 @@ void cPlayer::Respawn(void) // Extinguish the fire: StopBurning(); - if (const auto BedWorld = GetBedWorld(); m_World != BedWorld) + // Disable flying: + SetFlying(false); + + if (!m_IsRespawnPointForced) + { + // Check if the bed is still present: + if (GetRespawnWorld()->GetBlock(m_RespawnPosition) != E_BLOCK_BED) + { + const auto & DefaultWorld = *cRoot::Get()->GetDefaultWorld(); + + // If not, reset spawn to default and inform: + SetRespawnPosition(Vector3d(DefaultWorld.GetSpawnX(), DefaultWorld.GetSpawnY(), DefaultWorld.GetSpawnZ()), DefaultWorld); + SendAboveActionBarMessage("Your home bed was missing or obstructed"); + } + + // TODO: bed obstruction check here + } + + + if (const auto RespawnWorld = GetRespawnWorld(); m_World != RespawnWorld) { - MoveToWorld(*BedWorld, GetLastBedPos(), false, false); + MoveToWorld(*RespawnWorld, m_RespawnPosition, false, false); } else { m_ClientHandle->SendRespawn(m_World->GetDimension(), true); - TeleportToCoords(GetLastBedPos().x, GetLastBedPos().y, GetLastBedPos().z); + TeleportToCoords(m_RespawnPosition.x, m_RespawnPosition.y, m_RespawnPosition.z); } SetVisible(true); @@ -1785,7 +1815,7 @@ void cPlayer::LoadFromDisk() const Vector3i WorldSpawn(static_cast(m_World->GetSpawnX()), static_cast(m_World->GetSpawnY()), static_cast(m_World->GetSpawnZ())); SetPosition(WorldSpawn); - SetBedPos(WorldSpawn, *m_World); + SetRespawnPosition(WorldSpawn, *m_World); m_Inventory.Clear(); m_EnchantmentSeed = GetRandomProvider().RandInt(); // Use a random number to seed the enchantment generator @@ -1892,9 +1922,10 @@ bool cPlayer::LoadFromFile(const AString & a_FileName) m_World = cRoot::Get()->GetDefaultWorld(); } - m_LastBedPos.x = Root.get("SpawnX", m_World->GetSpawnX()).asInt(); - m_LastBedPos.y = Root.get("SpawnY", m_World->GetSpawnY()).asInt(); - m_LastBedPos.z = Root.get("SpawnZ", m_World->GetSpawnZ()).asInt(); + m_RespawnPosition.x = Root.get("SpawnX", m_World->GetSpawnX()).asInt(); + m_RespawnPosition.y = Root.get("SpawnY", m_World->GetSpawnY()).asInt(); + m_RespawnPosition.z = Root.get("SpawnZ", m_World->GetSpawnZ()).asInt(); + m_IsRespawnPointForced = Root.get("SpawnForced", true).asBool(); m_SpawnWorldName = Root.get("SpawnWorld", cRoot::Get()->GetDefaultWorld()->GetName()).asString(); try @@ -2006,6 +2037,7 @@ void cPlayer::SaveToDisk() root["SpawnX"] = GetLastBedPos().x; root["SpawnY"] = GetLastBedPos().y; root["SpawnZ"] = GetLastBedPos().z; + root["SpawnForced"] = m_IsRespawnPointForced; root["SpawnWorld"] = m_SpawnWorldName; root["enchantmentSeed"] = m_EnchantmentSeed; root["world"] = m_CurrentWorldName; @@ -3236,7 +3268,7 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) else if (IsInBed()) { // Check if sleeping is still possible: - if ((GetPosition().Floor() != m_LastBedPos) || (m_World->GetBlock(m_LastBedPos) != E_BLOCK_BED)) + if ((GetPosition().Floor() != m_RespawnPosition) || (m_World->GetBlock(m_RespawnPosition) != E_BLOCK_BED)) { m_ClientHandle->HandleLeaveBed(); } diff --git a/src/Entities/Player.h b/src/Entities/Player.h index d71fedec5..d1dfffa0b 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -513,22 +513,28 @@ public: The custom name will be used in the tab-list, in the player nametag and in the tab-completion. */ void SetCustomName(const AString & a_CustomName); - /** Gets the last position that the player slept in - This is initialised to the world spawn point if the player has not slept in a bed as of yet - */ - Vector3i GetLastBedPos(void) const { return m_LastBedPos; } + /** Gets the player's potential respawn position (named LastBedPos for compatibility reasons). */ + Vector3i GetLastBedPos(void) const { return m_RespawnPosition; } + + /** Returns if the respawn point is unconditionally used. */ + bool IsRespawnPointForced(void) const { return m_IsRespawnPointForced; } - /** Sets the player's bed (home / respawn) position to the specified position. - Sets the respawn world to the player's world. */ - void SetBedPos(const Vector3i a_Position); + /** Sets the player's bed position to the specified position. + Sets the respawn world to the player's world and unforces the respawn point. + The given position will be used subject to bed checks when respawning. */ + void SetBedPos(Vector3i a_Position); - /** Sets the player's bed (home / respawn) position and respawn world to the specified parameters. */ - void SetBedPos(const Vector3i a_Position, const cWorld & a_World); + /** Sets the player's bed position to the specified position. + The spawn point is unforced. The given position will be used subject to bed checks when respawning. */ + void SetBedPos(Vector3i a_Position, const cWorld & a_World); + + /** Sets the player's forced respawn position and world. */ + void SetRespawnPosition(Vector3i a_Position, const cWorld & a_World); // tolua_end - // TODO lua export GetBedPos and GetBedWorld - cWorld * GetBedWorld(); + // TODO lua export GetRespawnWorld + cWorld * GetRespawnWorld(); /** Update movement-related statistics. */ void UpdateMovementStats(const Vector3d & a_DeltaPos, bool a_PreviousIsOnGround); @@ -652,8 +658,9 @@ private: cWindow * m_CurrentWindow; cWindow * m_InventoryWindow; - /** The player's last saved bed position */ - Vector3i m_LastBedPos; + /** The player's potential respawn position, initialised to world spawn by default. + During player respawn from death, if m_IsRespawnPointForced is false and no bed exists here, it will be reset to world spawn. */ + Vector3i m_RespawnPosition; /** The name of the world which the player respawns in upon death. This is stored as a string to enable SaveToDisk to not touch cRoot, and thus can be safely called in the player's destructor. */ @@ -707,6 +714,9 @@ private: /** Was the player frozen manually by a plugin or automatically by the server? */ bool m_IsManuallyFrozen; + /** Whether we unconditionally respawn to m_RespawnPosition, or check if a bed is unobstructed and available first. */ + bool m_IsRespawnPointForced; + /** Flag used by food handling system to determine whether a teleport has just happened. Will not apply food penalties if found to be true; will set to false after processing. */ bool m_IsTeleporting; -- cgit v1.2.3