Developer Ikarus_ 2424 Posted August 29, 2020 Developer Share Posted August 29, 2020 (edited) WARNING: If your teleporter panel in lua doesn't use pc.can_warp you must add it obv ! It's important to check if a player can warp using pc.can_warp before to call pc.warp Hello community, I have always seen people go crazy for this problem which is solved with 2 lines of code, in particular with the various offlineshops (mine, or the free release great and ken) that are unfairly blamed for having duplication bugs. No! I believe that if you warp a player on another core without performing any checks you can't blame those who made the other systems which are then used to take advantage of the vulnerability you caused. If you implement something you need to make sure there are adequate checks to avoid causing vulnerabilities. In my opinion the cause of the duplication is not the various offlineshops, but the channel switcher which does not perform not even half checks before executing the warp. Anyway, no more chatter. I will explain here how to fix this problem. The first thing to check is if the channel switcher is using CHARACTER :: CanWarp to check if the player can be connected on a new channel or not. here i wanna paste a commonly seen function channel switcher to connect the player (char.cpp): void CHARACTER::ChannelSwitch(int iNewChannel){ long lAddr; long lMapIndex; WORD wPort; long x = this->GetX(); long y = this->GetY(); if (!CMapLocation::instance().Get(x, y, lMapIndex, lAddr, wPort)) { return; } if(lMapIndex >= 10000){ return; } std::map<WORD, int>ch; for(int i = 0; i < 4; i++){ for(int i2 = 1; i2 < 9; i2++){ ch[30*1000 + i*100 + i2] = i+1; } } int chan; if(ch.find(wPort) != ch.end()){ chan = ch[wPort]; }else{return;} Stop(); Save(); if(GetSectree()){ GetSectree()->RemoveEntity(this); ViewCleanup(); EncodeRemovePacket(this); } TPacketGCWarp p; p.bHeader = HEADER_GC_WARP; p.lX = x; p.lY = y; p.lAddr = lAddr; p.wPort = (wPort - 100*(chan-1) + 100*(iNewChannel-1)); GetDesc()->Packet(&p, sizeof(TPacketGCWarp)); } How you can see, no checks are performed to check if the character are using any systems which may give problems. So Here what we need is to add CanWarp check at the beginning of the method. void CHARACTER::ChannelSwitch(int iNewChannel){ //* START DUPLICATION FIX //* prevent problems about duplication of items //* using safebox/exchange/shop/acce if(!CanWarp()){ return; } //* END DUPLICATION FIX long lAddr; long lMapIndex; WORD wPort; long x = this->GetX(); long y = this->GetY(); if (!CMapLocation::instance().Get(x, y, lMapIndex, lAddr, wPort)) { return; } if(lMapIndex >= 10000){ return; } std::map<WORD, int>ch; for(int i = 0; i < 4; i++){ for(int i2 = 1; i2 < 9; i2++){ ch[30*1000 + i*100 + i2] = i+1; } } int chan; if(ch.find(wPort) != ch.end()){ chan = ch[wPort]; }else{return;} Stop(); Save(); if(GetSectree()){ GetSectree()->RemoveEntity(this); ViewCleanup(); EncodeRemovePacket(this); } TPacketGCWarp p; p.bHeader = HEADER_GC_WARP; p.lX = x; p.lY = y; p.lAddr = lAddr; p.wPort = (wPort - 100*(chan-1) + 100*(iNewChannel-1)); GetDesc()->Packet(&p, sizeof(TPacketGCWarp)); } This is enoght to solve 100% of the problems if your CanWarp is a complete check about all your system installed. If not, you just need to adapt CanWarp with the systems you have installed. I will make an example with my offlineshop so that the speech is clear to anyone. Let's take a look to the Default CanWarp method. The first thing to note is that its purpose is obviously to return true if the player is able to reconnect without any problems. bool CHARACTER::CanWarp() const { const int iPulse = thecore_pulse(); const int limit_time = PASSES_PER_SEC(g_nPortalLimitTime); if ((iPulse - GetSafeboxLoadTime()) < limit_time) return false; if ((iPulse - GetExchangeTime()) < limit_time) return false; if ((iPulse - GetMyShopTime()) < limit_time) return false; if ((iPulse - GetRefineTime()) < limit_time) return false; if (GetExchange() || GetMyShop() || GetShopOwner() || IsOpenSafebox() || IsCubeOpen()) return false; return true; } How you can see here you can find a lot of checks which they are checking the last time of every auction may be used to take exploit the warp. What we need to do here is to add new checks, to cover all the systems installed and fix all vulnerabilities. An example about an offlineshop would be: bool CHARACTER::CanWarp() const { const int iPulse = thecore_pulse(); const int limit_time = PASSES_PER_SEC(g_nPortalLimitTime); if ((iPulse - GetSafeboxLoadTime()) < limit_time) return false; if ((iPulse - GetExchangeTime()) < limit_time) return false; if ((iPulse - GetMyShopTime()) < limit_time) return false; if ((iPulse - GetRefineTime()) < limit_time) return false; if (GetExchange() || GetMyShop() || GetShopOwner() || IsOpenSafebox() || IsCubeOpen()) return false; #ifdef __ENABLE_NEW_OFFLINESHOP__ if (iPulse - GetOfflineShopUseTime() < limit_time) return false; if (GetOfflineShopGuest() || GetAuctionGuest()) return false; #endif return true; } Note: I m using GetOfflineShopGuest and GetAuctionGuest which are methods used on my shop to get the pointer to the opened offlineshop/auction but if you are using another offlineshop system you need to find the equivalent way to check if the player is guest into a offline shop. The method GetOfflineShopUseTime is a new method which i m implementing to check the last use time of the system. Let's see how to implement it (char.cpp) //SEARCH void ResetStopTime(); DWORD GetStopTime() const; //ADD UNDER #ifdef __ENABLE_NEW_OFFLINESHOP__ public: int GetOfflineShopUseTime() const {return m_iOfflineShopUseTime;} void SetOfflineShopUseTime(){m_iOfflineShopUseTime = thecore_pulse();} private: int m_iOfflineShopUseTime = 0; #endif Here we are instantiating a new int where we can store the current time when player use the systemUsing SetOfflineShopUseTime the method will update the value of m_iOfflineShopUseTime and using GetOfflineShopUseTime we can check the last time player used the system (as done into CanWarp). What's missing to do? We need to use SetOfflineShopUseTime where the player is using our offlineshop (buy item, open shop, edit shop, close shop, ecc.) , so that the last use time is actually updated when needed, making the check in can warp effective. A similar check can be done for other systems (eg acce system) to make our CanWarp safer and more effective. I hope it's usefull. Bye Edited May 22, 2021 by Ikarus_ 1 1 21 My youtube channel on which you can see my works here Link to comment Share on other sites More sharing options...
HardissonHard 0 Posted August 31, 2020 Share Posted August 31, 2020 (edited) Thanks for this! I had this trouble Edited August 31, 2020 by HardissonHard Link to comment Share on other sites More sharing options...
Flourine 106 Posted September 2, 2020 Share Posted September 2, 2020 (edited) Edited September 2, 2020 by Flourine 1 Link to comment Share on other sites More sharing options...
AKUROS 27 Posted November 3, 2020 Share Posted November 3, 2020 Can you help me insert the fix in great's offline shop please? I didn't understand what to put in bool CHARACTER :: CanWarp () const my char.h define: #ifdef OFFLINE_SHOP public: void OpenMyShop(const char * c_pszSign, TShopItemTable * pTable, BYTE bItemCount, DWORD days); void SendShops(bool isGm = false); void OpenShop(DWORD id, const char *name, bool onboot = false); void SetPrivShop(DWORD shop_id) { bprivShop = shop_id; } BOOL IsPrivShop(void) const { return bprivShop>0; } DWORD GetPrivShop() const { return bprivShop; } void SetPrivShopOwner(DWORD id) { bprivShopOwner = id; } DWORD GetPrivShopOwner() const { return bprivShopOwner; } void DeleteMyShop(); DWORD GetShopTime() const { return dw_ShopTime; } void SetShopTime(DWORD time) { dw_ShopTime = time; } void SetShopSign(const char * name); void LoadPrivShops(); TPrivShop GetPrivShopTable(DWORD id); void RemovePrivShopTable(DWORD id); void UpdatePrivShopTable(DWORD id, TPrivShop shop); void UpdateShopItems(); void SendShopCost(); private: PSHOP_MAP m_mapshops; DWORD bprivShop; DWORD bprivShopOwner; DWORD dw_ShopTime; public: void StartRefreshShopEvent(); protected: LPEVENT m_pkRefreshShopEvent; public: void StartShopEditModeEvent(); void SetShopEditMode(bool val); bool GetShopEditMode() { return m_bShopEditMode; } void SetShopEditModeTick(); DWORD GetShopEditModeTick() { return m_dwShopEditModeTick; } protected: LPEVENT m_pkEditShopEvent; bool m_bShopEditMode; DWORD m_dwShopEditModeTick; #endif #ifdef GIFT_SYSTEM protected: void AddGiftGrid(int page); int AddGiftGridItem(int page, int size); GIFT_MAP m_mapGiftGrid; LPEVENT m_pkGiftRefresh; DWORD m_dwLastGiftPage; public: void StartRefreshGift(); void LoadGiftPage(int page); void RefreshGift(); int GetGiftPages() { return m_mapGiftGrid.size(); } int GetLastGiftPage() { return m_dwLastGiftPage; } #endif Link to comment Share on other sites More sharing options...
Developer Ikarus_ 2424 Posted May 22, 2021 Author Developer Share Posted May 22, 2021 Added an important disclaimer at the beginning! My youtube channel on which you can see my works here Link to comment Share on other sites More sharing options...
Mafuyu 51 Posted May 23, 2021 Share Posted May 23, 2021 (edited) On 5/22/2021 at 8:00 PM, Ikarus_ said: Added an important disclaimer at the beginning! for your disclaimer, should it not be enough if you check the CanWarp directly at the pc_warp function in questlua_pc? so you dont need to add the check to EVERY quest like this if (!ch->CanWarp()) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("CANNOT_WARP_AFTER_TRADING")); lua_pushboolean(L, false); return 1; } Edited May 23, 2021 by Mafuyu Link to comment Share on other sites More sharing options...
Developer Ikarus_ 2424 Posted May 24, 2021 Author Developer Share Posted May 24, 2021 22 hours ago, Mafuyu said: for your disclaimer, should it not be enough if you check the CanWarp directly at the pc_warp function in questlua_pc? so you dont need to add the check to EVERY quest like this if (!ch->CanWarp()) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("CANNOT_WARP_AFTER_TRADING")); lua_pushboolean(L, false); return 1; } I don't like it, but feel free to use it My youtube channel on which you can see my works here Link to comment Share on other sites More sharing options...
Recommended Posts