From d7de4533368a36b77dbc2dbe21269690d8dc5ee5 Mon Sep 17 00:00:00 2001
From: Andrei Karas <akaras@inbox.ru>
Date: Wed, 18 May 2016 23:57:51 +0300
Subject: Fix random one tile desync in SMSG_BEING_MOVE3 packet.

---
 src/being/being.cpp           |  6 +++--
 src/being/being.h             | 15 +++++++++++-
 src/net/ea/beingrecv.cpp      | 57 +++++++++++++++++++++++++++++++++++++++++++
 src/net/eathena/beingrecv.cpp |  8 ++++--
 src/net/tmwa/beingrecv.cpp    |  4 ++-
 5 files changed, 84 insertions(+), 6 deletions(-)

diff --git a/src/being/being.cpp b/src/being/being.cpp
index d7b00e1c6..f020de3da 100644
--- a/src/being/being.cpp
+++ b/src/being/being.cpp
@@ -214,6 +214,8 @@ Being::Being(const BeingId id,
 #endif
     mX(0),
     mY(0),
+    mCachedX(0),
+    mCachedY(0),
     mSortOffsetY(0),
     mPixelOffsetY(0),
     mFixedOffsetY(0),
@@ -528,9 +530,9 @@ void Being::setPixelPositionF(const Vector &restrict pos) restrict2
     }
 }
 
-void Being::setDestination(const int dstX, const int dstY) restrict2
+void Being::setDestination(const int dstX,
+                           const int dstY) restrict2
 {
-    // We can't calculate anything without a map anyway.
     if (!mMap)
         return;
 
diff --git a/src/being/being.h b/src/being/being.h
index 0386f3290..296038876 100644
--- a/src/being/being.h
+++ b/src/being/being.h
@@ -136,7 +136,18 @@ class Being notfinal : public ActorSprite,
         /**
          * Creates a path for the being from current position to ex and ey
          */
-        void setDestination(const int dstX, const int dstY) restrict2;
+        void setDestination(const int dstX,
+                            const int dstY) restrict2;
+
+        void setCachedDestination(const int dstX,
+                                  const int dstY) restrict2
+        { mCachedX = dstX; mCachedY = dstY; }
+
+        int getCachedX() const A_WARN_UNUSED
+        { return mCachedX; }
+
+        int getCachedY() const A_WARN_UNUSED
+        { return mCachedY; }
 
         /**
          * Returns the destination for this being.
@@ -1170,6 +1181,8 @@ class Being notfinal : public ActorSprite,
 
         int mX;             // position in tiles
         int mY;             // position in tiles
+        int mCachedX;
+        int mCachedY;
         int mSortOffsetY;  // caculated offset in pixels based on mPixelOffsetY
         int mPixelOffsetY;  // tile height offset in pixels
                             // calculated between tiles
diff --git a/src/net/ea/beingrecv.cpp b/src/net/ea/beingrecv.cpp
index fa1f47c69..0a8168c6c 100644
--- a/src/net/ea/beingrecv.cpp
+++ b/src/net/ea/beingrecv.cpp
@@ -45,6 +45,8 @@
 #include "net/messagein.h"
 #include "net/serverfeatures.h"
 
+#include "utils/checkutils.h"
+
 #include "debug.h"
 
 namespace Ea
@@ -440,6 +442,9 @@ void BeingRecv::processBeingMove3(Net::MessageIn &msg)
     dstBeing->setAction(BeingAction::STAND, 0);
     dstBeing->setTileCoords(x, y);
 
+    if (dstBeing == localPlayer)
+        return;
+
     const unsigned char *moves = msg.readBytes(len, "moving path");
     Path path;
     if (moves)
@@ -458,8 +463,60 @@ void BeingRecv::processBeingMove3(Net::MessageIn &msg)
                 logger->log("bad move packet: %d", dir);
             }
         }
+        const int x1 = dstBeing->getCachedX();
+        const int y1 = dstBeing->getCachedY();
+        if (x1 != x || y1 != y)
+        {
+//            reportAlways("Found desync being move. "
+//                "Calculated target (%d,%d). "
+//                "Actual target (%d,%d).",
+//                static_cast<int>(x),
+//                static_cast<int>(y),
+//                static_cast<int>(x1),
+//                static_cast<int>(y1));
+            if (len > 0)
+            {
+                const unsigned char dir = moves[0];
+                uint16_t x0 = x;
+                uint16_t y0 = y;
+
+                if (dir <= 7)
+                {
+                    x0 -= dirx[0];
+                    y0 -= diry[0];
+                    if (x1 == x0 && y1 == y0)
+                    {
+                        path.erase(path.begin());
+                        logger->log("Fixed being moving desync by "
+                            "removing one tile");
+                    }
+                    else
+                    {
+                        x0 = x;
+                        y0 = y;
+                        if (abs(x0 - x1) < 2 && abs(y0 - y1) < 2)
+                        {
+                            path.push_back(Position(x1, y1));
+                            logger->log("Fixed being moving desync by "
+                                "adding one tile");
+                        }
+                        else
+                        {
+                            reportAlways("Error: being moving desync. "
+                                "Calculated target (%d,%d). "
+                                "Actual target (%d,%d).",
+                                static_cast<int>(x),
+                                static_cast<int>(y),
+                                static_cast<int>(x1),
+                                static_cast<int>(y1));
+                        }
+                    }
+                }
+            }
+        }
         delete [] moves;
     }
+
     dstBeing->setPath(path);
     BLOCK_END("BeingRecv::processBeingMove3")
 }
diff --git a/src/net/eathena/beingrecv.cpp b/src/net/eathena/beingrecv.cpp
index 85c276e46..5c5e18ce8 100644
--- a/src/net/eathena/beingrecv.cpp
+++ b/src/net/eathena/beingrecv.cpp
@@ -597,7 +597,9 @@ void BeingRecv::processBeingMove(Net::MessageIn &msg)
     dstBeing->setTileCoords(srcX, srcY);
     if (localPlayer)
         localPlayer->followMoveTo(dstBeing, srcX, srcY, dstX, dstY);
-    if (!serverFeatures->haveMove3())
+    if (serverFeatures->haveMove3())
+        dstBeing->setCachedDestination(dstX, dstY);
+    else
         dstBeing->setDestination(dstX, dstY);
 
     // because server don't send direction in move packet, we fixing it
@@ -956,7 +958,9 @@ void BeingRecv::processBeingMove2(Net::MessageIn &msg)
     dstBeing->setTileCoords(srcX, srcY);
     if (localPlayer)
         localPlayer->followMoveTo(dstBeing, srcX, srcY, dstX, dstY);
-    if (!serverFeatures->haveMove3())
+    if (serverFeatures->haveMove3())
+        dstBeing->setCachedDestination(dstX, dstY);
+    else
         dstBeing->setDestination(dstX, dstY);
     if (dstBeing->getType() == ActorType::Player)
         dstBeing->setMoveTime();
diff --git a/src/net/tmwa/beingrecv.cpp b/src/net/tmwa/beingrecv.cpp
index d5f1dbd6f..04c4a0444 100644
--- a/src/net/tmwa/beingrecv.cpp
+++ b/src/net/tmwa/beingrecv.cpp
@@ -1019,7 +1019,9 @@ void BeingRecv::processBeingMove(Net::MessageIn &msg)
     {
         dstBeing->setAction(BeingAction::STAND, 0);
         dstBeing->setTileCoords(srcX, srcY);
-        if (!serverFeatures->haveMove3())
+        if (serverFeatures->haveMove3())
+            dstBeing->setCachedDestination(dstX, dstY);
+        else
             dstBeing->setDestination(dstX, dstY);
     }
 
-- 
cgit v1.2.3-70-g09d2