From c088f7ff0a336703fb19038eef36f736a4e388f7 Mon Sep 17 00:00:00 2001 From: LogicParrot Date: Sat, 27 Aug 2016 09:37:54 +0300 Subject: Proper respawn packets on dimension travel --- src/ClientHandle.cpp | 18 +++++++++++++- src/ClientHandle.h | 3 +++ src/Entities/Entity.cpp | 47 ++++++++++++++++++++++++++++++------- src/Entities/Player.cpp | 9 +++++++ src/Entities/Player.h | 3 +++ src/Protocol/Protocol.h | 2 +- src/Protocol/Protocol18x.cpp | 12 ++-------- src/Protocol/Protocol18x.h | 7 +----- src/Protocol/Protocol19x.cpp | 14 ++--------- src/Protocol/Protocol19x.h | 7 +----- src/Protocol/ProtocolRecognizer.cpp | 4 ++-- src/Protocol/ProtocolRecognizer.h | 2 +- 12 files changed, 80 insertions(+), 48 deletions(-) diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 882c7f283..6febbfc3a 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -62,6 +62,7 @@ int cClientHandle::s_ClientCount = 0; // cClientHandle: cClientHandle::cClientHandle(const AString & a_IPString, int a_ViewDistance) : + m_LastSentDimension(dimNotSet), m_CurrentViewDistance(a_ViewDistance), m_RequestedViewDistance(a_ViewDistance), m_IPString(a_IPString), @@ -368,6 +369,7 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID, // Return a server login packet m_Protocol->SendLogin(*m_Player, *World); + m_LastSentDimension = World->GetDimension(); // Send Weather if raining: if ((World->GetWeather() == 1) || (World->GetWeather() == 2)) @@ -2704,7 +2706,21 @@ void cClientHandle::SendResetTitle() void cClientHandle::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) { - m_Protocol->SendRespawn(a_Dimension, a_ShouldIgnoreDimensionChecks); + // If a_ShouldIgnoreDimensionChecks is true, we must be traveling to the same dimension + ASSERT((!a_ShouldIgnoreDimensionChecks) || (a_Dimension == m_LastSentDimension)); + + if ((!a_ShouldIgnoreDimensionChecks) && (a_Dimension == m_LastSentDimension)) + { + // The client goes crazy if we send a respawn packet with the dimension of the current world + // So we send a temporary one first. + // This is not needed when the player dies, hence the a_ShouldIgnoreDimensionChecks flag. + // a_ShouldIgnoreDimensionChecks is true only at cPlayer::respawn, which is called after + // the player dies. + eDimension TemporaryDimension = (a_Dimension == dimOverworld) ? dimNether : dimOverworld; + m_Protocol->SendRespawn(TemporaryDimension); + } + m_Protocol->SendRespawn(a_Dimension); + m_LastSentDimension = a_Dimension; } diff --git a/src/ClientHandle.h b/src/ClientHandle.h index c49de647f..da59bdea8 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -369,6 +369,9 @@ public: // tolua_export bool IsPlayerChunkSent(); private: + /** The dimension that was last sent to a player in a Respawn or Login packet. + Used to avoid Respawning into the same dimension, which confuses the client. */ + eDimension m_LastSentDimension; friend class cServer; // Needs access to SetSelf() diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 2adbc3142..b2fa56143 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -1433,19 +1433,22 @@ bool cEntity::DetectPortal() } m_PortalCooldownData.m_TicksDelayed = 0; + // Nether portal in the nether if (GetWorld()->GetDimension() == dimNether) { if (GetWorld()->GetLinkedOverworldName().empty()) { return false; } + cWorld * DestinationWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedOverworldName()); + eDimension DestionationDim = DestinationWorld->GetDimension(); m_PortalCooldownData.m_ShouldPreventTeleportation = true; // Stop portals from working on respawn if (IsPlayer()) { // Send a respawn packet before world is loaded / generated so the client isn't left in limbo - (reinterpret_cast(this))->GetClientHandle()->SendRespawn(dimOverworld); + (reinterpret_cast(this))->GetClientHandle()->SendRespawn(DestionationDim); } Vector3d TargetPos = GetPosition(); @@ -1454,23 +1457,30 @@ 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 nether -> overworld"); + LOGD("Jumping %s -> %s", DimensionToString(dimNether).c_str(), DimensionToString(DestionationDim).c_str()); new cNetherPortalScanner(this, TargetWorld, TargetPos, 256); return true; } + // Nether portal in the overworld else { if (GetWorld()->GetLinkedNetherWorldName().empty()) { return false; } + cWorld * DestinationWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedNetherWorldName()); + eDimension DestionationDim = DestinationWorld->GetDimension(); m_PortalCooldownData.m_ShouldPreventTeleportation = true; if (IsPlayer()) { - reinterpret_cast(this)->AwardAchievement(achEnterPortal); - reinterpret_cast(this)->GetClientHandle()->SendRespawn(dimNether); + if (DestionationDim == dimNether) + { + reinterpret_cast(this)->AwardAchievement(achEnterPortal); + } + + reinterpret_cast(this)->GetClientHandle()->SendRespawn(DestionationDim); } Vector3d TargetPos = GetPosition(); @@ -1479,7 +1489,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 overworld -> nether"); + LOGD("Jumping %s -> %s", DimensionToString(dimOverworld).c_str(), DimensionToString(DestionationDim).c_str()); new cNetherPortalScanner(this, TargetWorld, TargetPos, 128); return true; } @@ -1491,6 +1501,7 @@ bool cEntity::DetectPortal() return false; } + // End portal in the end if (GetWorld()->GetDimension() == dimEnd) { @@ -1498,37 +1509,55 @@ bool cEntity::DetectPortal() { return false; } + cWorld * DestinationWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedOverworldName()); + eDimension DestionationDim = DestinationWorld->GetDimension(); + m_PortalCooldownData.m_ShouldPreventTeleportation = true; if (IsPlayer()) { cPlayer * Player = reinterpret_cast(this); - Player->TeleportToCoords(Player->GetLastBedPos().x, Player->GetLastBedPos().y, Player->GetLastBedPos().z); - Player->GetClientHandle()->SendRespawn(dimOverworld); + if (Player->GetBedWorld() == DestinationWorld) + { + Player->TeleportToCoords(Player->GetLastBedPos().x, Player->GetLastBedPos().y, Player->GetLastBedPos().z); + } + else + { + Player->TeleportToCoords(DestinationWorld->GetSpawnX(), DestinationWorld->GetSpawnY(), DestinationWorld->GetSpawnZ()); + } + Player->GetClientHandle()->SendRespawn(DestionationDim); } 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(dimEnd).c_str(), DimensionToString(DestionationDim).c_str()); return MoveToWorld(TargetWorld, false); } + // End portal in the overworld else { if (GetWorld()->GetLinkedEndWorldName().empty()) { return false; } + cWorld * DestinationWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedEndWorldName()); + eDimension DestionationDim = DestinationWorld->GetDimension(); m_PortalCooldownData.m_ShouldPreventTeleportation = true; if (IsPlayer()) { - reinterpret_cast(this)->AwardAchievement(achEnterTheEnd); - reinterpret_cast(this)->GetClientHandle()->SendRespawn(dimEnd); + if (DestionationDim == dimEnd) + { + reinterpret_cast(this)->AwardAchievement(achEnterTheEnd); + } + reinterpret_cast(this)->GetClientHandle()->SendRespawn(DestionationDim); } cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedEndWorldName()); 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()); return MoveToWorld(TargetWorld, false); } diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 50bec3608..f28258969 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -893,6 +893,15 @@ void cPlayer::SetBedPos(const Vector3i & a_Pos, cWorld * a_World) +cWorld * cPlayer::GetBedWorld() +{ + return m_SpawnWorld; +} + + + + + void cPlayer::SetFlying(bool a_IsFlying) { if (a_IsFlying == m_IsFlying) diff --git a/src/Entities/Player.h b/src/Entities/Player.h index f6e9da45e..25796ee50 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -467,6 +467,9 @@ public: // tolua_end + // TODO lua export GetBedPos and GetBedWorld + cWorld * GetBedWorld(); + /** Update movement-related statistics. */ void UpdateMovementStats(const Vector3d & a_DeltaPos, bool a_PreviousIsOnGround); diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index a00923394..1da2a6fd7 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -114,7 +114,7 @@ public: virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) = 0; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) = 0; virtual void SendResetTitle (void) = 0; - virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) = 0; + virtual void SendRespawn (eDimension a_Dimension) = 0; virtual void SendExperience (void) = 0; virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) = 0; virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) = 0; diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp index 88a0757f2..c1018324f 100644 --- a/src/Protocol/Protocol18x.cpp +++ b/src/Protocol/Protocol18x.cpp @@ -107,8 +107,7 @@ cProtocol180::cProtocol180(cClientHandle * a_Client, const AString & a_ServerAdd m_ServerPort(a_ServerPort), m_State(a_State), m_ReceivedData(32 KiB), - m_IsEncrypted(false), - m_LastSentDimension(dimNotSet) + m_IsEncrypted(false) { // BungeeCord handling: @@ -626,7 +625,6 @@ void cProtocol180::SendLogin(const cPlayer & a_Player, const cWorld & a_World) Pkt.WriteString("default"); // Level type - wtf? Pkt.WriteBool(false); // Reduced Debug Info - wtf? } - m_LastSentDimension = a_World.GetDimension(); // Send the spawn position: { @@ -1084,13 +1082,8 @@ void cProtocol180::SendResetTitle(void) -void cProtocol180::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) +void cProtocol180::SendRespawn(eDimension a_Dimension) { - if ((m_LastSentDimension == a_Dimension) && !a_ShouldIgnoreDimensionChecks) - { - // Must not send a respawn for the world with the same dimension, the client goes cuckoo if we do (unless we are respawning from death) - return; - } cPacketizer Pkt(*this, 0x07); // Respawn packet cPlayer * Player = m_Client->GetPlayer(); @@ -1098,7 +1091,6 @@ void cProtocol180::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimens Pkt.WriteBEUInt8(2); // TODO: Difficulty (set to Normal) Pkt.WriteBEUInt8(static_cast(Player->GetEffectiveGameMode())); Pkt.WriteString("default"); - m_LastSentDimension = a_Dimension; } diff --git a/src/Protocol/Protocol18x.h b/src/Protocol/Protocol18x.h index 08a51f342..b8f9675ba 100644 --- a/src/Protocol/Protocol18x.h +++ b/src/Protocol/Protocol18x.h @@ -110,7 +110,7 @@ public: virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override; virtual void SendResetTitle (void) override; - virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override; + virtual void SendRespawn (eDimension a_Dimension) override; virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override; virtual void SendExperience (void) override; virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; @@ -177,11 +177,6 @@ protected: /** The logfile where the comm is logged, when g_ShouldLogComm is true */ cFile m_CommLogFile; - /** The dimension that was last sent to a player in a Respawn or Login packet. - Used to avoid Respawning into the same dimension, which confuses the client. */ - eDimension m_LastSentDimension; - - /** Adds the received (unencrypted) data to m_ReceivedData, parses complete packets */ void AddReceivedData(const char * a_Data, size_t a_Size); diff --git a/src/Protocol/Protocol19x.cpp b/src/Protocol/Protocol19x.cpp index d8c86cf6b..456ca8a91 100644 --- a/src/Protocol/Protocol19x.cpp +++ b/src/Protocol/Protocol19x.cpp @@ -117,8 +117,7 @@ cProtocol190::cProtocol190(cClientHandle * a_Client, const AString & a_ServerAdd m_ServerPort(a_ServerPort), m_State(a_State), m_ReceivedData(32 KiB), - m_IsEncrypted(false), - m_LastSentDimension(dimNotSet) + m_IsEncrypted(false) { // BungeeCord handling: @@ -640,7 +639,6 @@ void cProtocol190::SendLogin(const cPlayer & a_Player, const cWorld & a_World) Pkt.WriteString("default"); // Level type - wtf? Pkt.WriteBool(false); // Reduced Debug Info - wtf? } - m_LastSentDimension = a_World.GetDimension(); // Send the spawn position: { @@ -1110,21 +1108,14 @@ void cProtocol190::SendResetTitle(void) -void cProtocol190::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) +void cProtocol190::SendRespawn(eDimension a_Dimension) { - if ((m_LastSentDimension == a_Dimension) && !a_ShouldIgnoreDimensionChecks) - { - // Must not send a respawn for the world with the same dimension, the client goes cuckoo if we do (unless we are respawning from death) - return; - } - cPacketizer Pkt(*this, 0x33); // Respawn packet cPlayer * Player = m_Client->GetPlayer(); Pkt.WriteBEInt32(static_cast(a_Dimension)); Pkt.WriteBEUInt8(2); // TODO: Difficulty (set to Normal) Pkt.WriteBEUInt8(static_cast(Player->GetEffectiveGameMode())); Pkt.WriteString("default"); - m_LastSentDimension = a_Dimension; } @@ -4058,7 +4049,6 @@ void cProtocol191::SendLogin(const cPlayer & a_Player, const cWorld & a_World) Pkt.WriteString("default"); // Level type - wtf? Pkt.WriteBool(false); // Reduced Debug Info - wtf? } - m_LastSentDimension = a_World.GetDimension(); // Send the spawn position: { diff --git a/src/Protocol/Protocol19x.h b/src/Protocol/Protocol19x.h index 2bf8df4f5..9124a5422 100644 --- a/src/Protocol/Protocol19x.h +++ b/src/Protocol/Protocol19x.h @@ -116,7 +116,7 @@ public: virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override; virtual void SendResetTitle (void) override; - virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override; + virtual void SendRespawn (eDimension a_Dimension) override; virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override; virtual void SendExperience (void) override; virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; @@ -183,11 +183,6 @@ protected: /** The logfile where the comm is logged, when g_ShouldLogComm is true */ cFile m_CommLogFile; - /** The dimension that was last sent to a player in a Respawn or Login packet. - Used to avoid Respawning into the same dimension, which confuses the client. */ - eDimension m_LastSentDimension; - - /** Adds the received (unencrypted) data to m_ReceivedData, parses complete packets */ void AddReceivedData(const char * a_Data, size_t a_Size); diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 4e950bb7f..8b05ce768 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -635,10 +635,10 @@ void cProtocolRecognizer::SendResetTitle(void) -void cProtocolRecognizer::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) +void cProtocolRecognizer::SendRespawn(eDimension a_Dimension) { ASSERT(m_Protocol != nullptr); - m_Protocol->SendRespawn(a_Dimension, a_ShouldIgnoreDimensionChecks); + m_Protocol->SendRespawn(a_Dimension); } diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h index ec8f562a7..5f0fa2dcb 100644 --- a/src/Protocol/ProtocolRecognizer.h +++ b/src/Protocol/ProtocolRecognizer.h @@ -100,7 +100,7 @@ public: virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override; virtual void SendResetTitle (void) override; - virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override; + virtual void SendRespawn (eDimension a_Dimension) override; virtual void SendExperience (void) override; virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override; -- cgit v1.2.3