Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 09/01/20 in all areas

  1. 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
    1 point
  2. M2 Download Center Download Here ( Internal ) Hey guys, I just programmed this feature for my server but I thought it can be really useful for everyone so now I release it. This stuff is about how you can load images, etc. in the game without directly packing it into the client but uploading them to a web-server. In this tutorial we will make it work for images, but you can extend it to any type of file you want. There are not much requirements we only use up to C++11 features and you have to have libcurl library. Open up EterLib/ResourceManager.h and add add the following to the end of the class (don't forget to include <future> and <utility>): private: std::list<std::future<CResource*>> ongoingDownloads; public: void AddDownload(std::future<CResource*>&& f) { ongoingDownloads.emplace_back(std::forward<std::future<CResource*>>(f)); } Go to EterLib/ResourceManager.cpp and find the CResourceManager::Update function, add the following to the end of it: for (auto it = ongoingDownloads.begin(); it != ongoingDownloads.end();) { if (it->wait_for(std::chrono::seconds(0)) == std::future_status::ready) { it->get()->LoadDownloadedData(); it = ongoingDownloads.erase(it); } else { ++it; } } Next, open EterLib/Resource.h, find the constructor and modify it like: CResource(const char* c_szFileName, bool _loadFromNetwork = false); After add the following to the end of the class: protected: bool loadFromNetwork; private: std::vector<BYTE> downloadedData; public: void LoadDownloadedData(); Then go to EterLib/Resource.cpp, find the constructor and also modify it like: CResource::CResource(const char* c_szFileName, bool _loadFromNetwork) : me_state(STATE_EMPTY) , loadFromNetwork(_loadFromNetwork) { SetFileName(c_szFileName); } In the same file, add this to the beginning right after the includes: #include <curl/curl.h> #include "ResourceManager.h" #define ASSET_SERVER "[Hidden Content]" static size_t CurlWriteCallback(void* contents, size_t size, size_t nmemb, void* userp) { if (nullptr != userp) { std::vector<BYTE>& vec = *((std::vector<BYTE>*)userp); vec.reserve(vec.size() + (size * nmemb)); for (size_t i = 0; i < size * nmemb; ++i) { vec.push_back(((BYTE*)contents)[i]); } } return size * nmemb; } void CResource::LoadDownloadedData() { if (downloadedData.empty()) return; Clear(); if (OnLoad(downloadedData.size(), downloadedData.data())) { me_state = STATE_EXIST; } else { Tracef("CResource::Load Error %s\n", GetFileName()); me_state = STATE_ERROR; } downloadedData.clear(); } Now, - still in the same file - find CResource::Load function and modify it like this: void CResource::Load() { if (me_state != STATE_EMPTY) return; std::string fileName = GetFileName(); if (loadFromNetwork && downloadedData.empty()) { CResourceManager::instance().AddDownload(std::move(std::async(std::launch::async | std::launch::deferred, [this, fileName]() { std::string url = ASSET_SERVER; url += fileName; CURL* curl = curl_easy_init(); if (curl) { curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &downloadedData); curl_easy_perform(curl); curl_easy_cleanup(curl); } return this; }))); fileName = "d:/ymir work/ui/placeholder.tga"; } DWORD dwStart = ELTimer_GetMSec(); CMappedFile file; LPCVOID fileData; //Tracenf("Load %s", c_szFileName); if (CEterPackManager::Instance().Get(file, fileName.c_str(), &fileData)) { m_dwLoadCostMiliiSecond = ELTimer_GetMSec() - dwStart; //Tracef("CResource::Load %s (%d bytes) in %d ms\n", c_szFileName, file.Size(), m_dwLoadCostMiliiSecond); if (OnLoad(file.Size(), fileData)) { me_state = STATE_EXIST; } else { Tracef("CResource::Load Error %s\n", fileName.c_str()); me_state = STATE_ERROR; return; } } else { if (OnLoad(0, NULL)) me_state = STATE_EXIST; else { Tracef("CResource::Load file not exist %s\n", fileName.c_str()); me_state = STATE_ERROR; } } } Still in Resource.cpp, find the CResource::Reload function and modify like: void CResource::Reload() { Tracef("CResource::Reload %s\n", GetFileName()); if (loadFromNetwork) { if (downloadedData.empty()) { std::string fileName = GetFileName(); CResourceManager::instance().AddDownload(std::move(std::async(std::launch::async | std::launch::deferred, [this, fileName]() { std::string url = ASSET_SERVER; url += "/"; url += fileName; CURL* curl = curl_easy_init(); if (curl) { curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &downloadedData); curl_easy_perform(curl); curl_easy_cleanup(curl); } return this; }))); } } else { Clear(); CMappedFile file; LPCVOID fileData; if (CEterPackManager::Instance().Get(file, GetFileName(), &fileData)) { if (OnLoad(file.Size(), fileData)) { me_state = STATE_EXIST; } else { me_state = STATE_ERROR; return; } } else { if (OnLoad(0, NULL)) me_state = STATE_EXIST; else { me_state = STATE_ERROR; } } } } Open EterLib/GrpImage.h and modify the constructor: CGraphicImage(const char* c_szFileName, DWORD dwFilter = D3DX_FILTER_LINEAR, bool loadFromNetwork = false); In EterLib/GrpImage.cpp also modify it: CGraphicImage::CGraphicImage(const char * c_szFileName, DWORD dwFilter, bool loadFromNetwork) : CResource(c_szFileName, loadFromNetwork) , m_dwFilter(dwFilter) { m_rect.bottom = m_rect.right = m_rect.top = m_rect.left = 0; } Finally open ScriptLib/Resource.cpp and add this somewhere the beginning: CResource* NewOnlineImage(const char* c_szFileName) { return new CGraphicImage(c_szFileName, D3DX_FILTER_LINEAR, true); } Go down where you see m_resManager.RegisterResourceNewFunctionPointer("jpg", NewImage); and add after: m_resManager.RegisterResourceNewFunctionPointer("oimg", NewOnlineImage); We're done! Now if you use *.oimg extension anywhere it will load them from what you define as ASSET_SERVER in EterLib/Resource.cpp ([Hidden Content]filename.oimg). You have to rename the image you upload from the original extension to oimg! Put a placeholder image at "d:\ymir work\ui\placeholder.tga" that it will load while waiting for bigger images. GIF in action (normal size image, the pic in the right bottom corner): GIF of loading a big (10MB image) that takes more time: Hope you like it!
    1 point
  3. reupload: [Hidden Content] [Hidden Content]
    1 point
  4. You need to add the additional file/ files to the project. Richtclick on UserInterface. Add->existing->choose the file
    1 point
  5. M2 Download Center Download Here ( Internal ) Hello. Demo: Mega.nz
    1 point
  6. btw instead of extern u can use vcpkg
    1 point
×
×
  • Create New...

Important Information

Terms of Use / Privacy Policy / Guidelines / We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.