Opublikowano 12 Stycznia 20251 r Inaczej mała optymalizacja załadowania map w wersji rozszerzonej, na devie jest tylko podstawowa od amuna src: - commonDefines.h dodaj: #define ENTITY_PRELOADING char_manager.cpp poszukaj: LPCHARACTER CHARACTER_MANAGER::SpawnMob(DWORD dwVnum, long lMapIndex, long x, long y, long z, bool bSpawnMotion, int iRot, bool bShow) przed: return (ch); dodaj: #ifdef ENTITY_PRELOADING //@Amun: or be specific, like if(ch->ispet, mount, whatever) if (!ch->IsPC() && !ch->IsGoto() && !ch->IsWarp() && !ch->IsDoor()) SECTREE_MANAGER::Instance().ExtendPreloadedEntitiesMap(lMapIndex, pkMob->m_table.dwVnum); #endif input_login.cpp poszukaj: } void CInputLogin::Empire(LPDESC d, const char * c_pData) dodaj nad: #ifdef ENTITY_PRELOADING SECTREE_MANAGER::Instance().SendPreloadEntitiesPacket(ch); #endif packet.h poszukaj: HEADER_GC_RESPOND_CHANNELSTATUS = 210, dodaj pod: // jak masz zajęte miejsce 211 to zmień na inne byle żeby pasowało w src i bin. #ifdef ENTITY_PRELOADING HEADER_GC_PRELOAD_ENTITIES = 211, #endif przed: #pragma pack() #endif dodaj: #ifdef ENTITY_PRELOADING /*** HEADER_GC_PRELOAD_ENTITIES ***/ typedef struct packet_preload_entities { uint8_t header; uint16_t size; uint16_t count; } TPacketGCPreloadEntities; #endif sectree_manager.cpp poszukaj: pkMapSectree->Build(); dodaj pod: #ifdef ENTITY_PRELOADING GeneratePreloadedEntitiesMap(iIndex, pkMapSectree); #endif poszukaj: size_t SECTREE_MANAGER::GetMonsterCountInMap(long lMapIndex, DWORD dwVnum) { LPSECTREE_MAP sectree = SECTREE_MANAGER::instance().GetMap(lMapIndex); if (NULL != sectree) { struct FCountSpecifiedMonster f(dwVnum); sectree->for_each( f ); return f.cnt; } return 0; } dodaj pod: #ifdef ENTITY_PRELOADING void SECTREE_MANAGER::GeneratePreloadedEntitiesMap(int32_t mapIndex, LPSECTREE_MAP lpMapSectree) { if (m_preloadedEntities.find(mapIndex) == m_preloadedEntities.end()) { m_preloadedEntities.insert({ mapIndex, {} }); auto lmbd = [&mapIndex, this](LPENTITY ent) { this->m_preloadedEntities.at(mapIndex).emplace((((LPCHARACTER)ent)->GetRaceNum())); }; lpMapSectree->for_each(lmbd); // log unique races for (int32_t i : m_preloadedEntities.at(mapIndex)) sys_log(0, "ENTITY_PRELOADING: [map: %d], [entity race: %d]", mapIndex, i); } } void SECTREE_MANAGER::ExtendPreloadedEntitiesMap(int32_t mapIndex, uint32_t mob_race) { auto it = m_preloadedEntities.find(mapIndex); if (it == m_preloadedEntities.end())// don't create new maps if they're not available from the beginning return; if ((it->second.emplace(mob_race)).second) sys_log(0, "ENTITY_PRELOADING: extended [map: %d], with [entity race: %d]", mapIndex, mob_race); } void SECTREE_MANAGER::SendPreloadEntitiesPacket(LPCHARACTER ch) { LPDESC d = ch->GetDesc(); if (!d) return; long lMapIndex = ch->GetMapIndex(); if (m_preloadedEntities.find(lMapIndex) == m_preloadedEntities.end()) return; std::unordered_set<uint32_t>& s = m_preloadedEntities.at(lMapIndex); if (s.empty()) return; { TEMP_BUFFER buf; TPacketGCPreloadEntities pack{}; pack.header = HEADER_GC_PRELOAD_ENTITIES; pack.count = s.size(); for (const auto& it : s) buf.write(&it, sizeof(uint32_t)); pack.size = sizeof(pack) + buf.size(); d->BufferedPacket(&pack, sizeof(TPacketGCPreloadEntities)); d->Packet(buf.read_peek(), buf.size()); } } #endif sectree_manager.h w includach daj: #ifdef ENTITY_PRELOADING #include <unordered_map> #include <unordered_set> #endif poszukaj: size_t GetMonsterCountInMap(long lMpaIndex, DWORD dwVnum); dodaj pod: #ifdef ENTITY_PRELOADING void GeneratePreloadedEntitiesMap(int32_t mapIndex, LPSECTREE_MAP lpMapSectree); void ExtendPreloadedEntitiesMap(int32_t mapIndex, uint32_t mob_race); void SendPreloadEntitiesPacket(LPCHARACTER ch); #endif szukaj: PrivateIndexMapType next_private_index_map_; dodaj pod: #ifdef ENTITY_PRELOADING std::unordered_map<int32_t, std::unordered_set<uint32_t>> m_preloadedEntities; #endif przechodzimy teraz do bin w locale_inc.h daj: #define ENTITY_PRELOADING packet.h szukaj: HEADER_GC_RESPOND_CHANNELSTATUS = 210, dodaj pod: #ifdef ENTITY_PRELOADING HEADER_GC_PRELOAD_ENTITIES = 211, #endif przed: #pragma pack(pop) dodaj nad: #ifdef ENTITY_PRELOADING /*** HEADER_GC_PRELOAD_ENTITIES ***/ typedef struct packet_preload_entities { uint8_t header; uint16_t size; uint16_t count; } TPacketGCPreloadEntities; #endif PythonNetworkStream.cpp szukaj: Set(HEADER_GC_DRAGON_SOUL_REFINE, CNetworkPacketHeaderMap::TPacketType(sizeof(TPacketGCDragonSoulRefine), STATIC_SIZE_PACKET)); dodaj pod: #ifdef ENTITY_PRELOADING Set(HEADER_GC_PRELOAD_ENTITIES, CNetworkPacketHeaderMap::TPacketType(sizeof(TPacketGCPreloadEntities), DYNAMIC_SIZE_PACKET)); #endif PythonNetworkStream.h szukaj: bool RecvWarpPacket(); dodaj pod: #ifdef ENTITY_PRELOADING bool RecvPreloadEntitiesPacket(); #endif PythonNetworkStreamPhaseGame.cpp szukaj: case HEADER_GC_DRAGON_SOUL_REFINE: ret = RecvDragonSoulRefine(); break; dodaj pod: #ifdef ENTITY_PRELOADING case HEADER_GC_PRELOAD_ENTITIES: ret = RecvPreloadEntitiesPacket(); break; #endif dodaj na koncu: #ifdef ENTITY_PRELOADING bool CPythonNetworkStream::RecvPreloadEntitiesPacket() { TPacketGCPreloadEntities pack{}; if (!Recv(sizeof(pack), &pack)) return false; assert(int32_t(pack.size) - sizeof(pack) == pack.count * sizeof(uint32_t) && "HEADER_GC_PRELOAD_ENTITIES"); uint32_t dwEntityRace = 0; CInstanceBase::SCreateData d{}; CPythonCharacterManager& rkChrMgr = CPythonCharacterManager::Instance(); for (uint16_t i = 0; i < pack.count; i++) { if (!Recv(sizeof(uint32_t), &dwEntityRace)) return false; #ifdef _DEBUG TraceError("Preloading %d", dwEntityRace); #endif d.m_dwRace = dwEntityRace; d.m_dwVID = dwEntityRace; if (rkChrMgr.CreateInstance(d))//create the entity, force it to load absolutely everything rkChrMgr.DeleteInstance(dwEntityRace);// now get rid of it else { //TraceError("Failed to preload race %d", dwEntityRace); } } return true; } #endif
Opublikowano 2 Lutego 20251 r W dniu 12.01.2025 o 16:07, Azurri napisał(a): Inaczej mała optymalizacja załadowania map w wersji rozszerzonej, na devie jest tylko podstawowa od amuna Szczerze nie dopatrzyłem się zmian względem wersji Anuma Github Trochę edytowałem główną funkcje aby lepiej wpasowywała się w nowe standardy c++ użyto dynamic_cast, który sprawdza, czy rzutowanie jest możliwe. Jeśli nie, zwraca nullptr, co zapobiega błędom. Lepsza inicjalizacja mapy użyto prostszego zapisu m_preloadedEntities[mapIndex] = std::unordered_set<uint32_t>() Dane były zapisywane bezpośrednio do TEMP_BUFFER, co mogło prowadzić do wielokrotnej alokacji pamięci. W nowej wersji użyto std::vector<uint8_t>, który najpierw zbiera dane, a potem przekazuje je do TEMP_BUFFER (w moim przypadku było 950 rekordów zapisu Entities) Dodano warunek ' if ' Nowa wersja unika potencjalnych problemów z pamięcią dzięki użyciu std::vector i dynamic_cast, co zmniejsza ryzyko wycieków pamięci i błędów. Server/game/src/sectree_manager.cpp #ifdef ENABLE_ENTITY_PRELOADING void SECTREE_MANAGER::GeneratePreloadedEntitiesMap(int32_t mapIndex, LPSECTREE_MAP lpMapSectree) { if (m_preloadedEntities.find(mapIndex) == m_preloadedEntities.end()) { m_preloadedEntities[mapIndex] = std::unordered_set<uint32_t>(); auto lmbd = [&mapIndex, this](LPENTITY ent) { if (auto ch = dynamic_cast<LPCHARACTER>(ent)) { this->m_preloadedEntities.at(mapIndex).emplace(ch->GetRaceNum()); } }; lpMapSectree->for_each(lmbd); for (const auto& race : m_preloadedEntities.at(mapIndex)) { sys_log(0, "ENTITY_PRELOADING: [map: %d], [entity race: %d]", mapIndex, race); } } } void SECTREE_MANAGER::ExtendPreloadedEntitiesMap(int32_t mapIndex, uint32_t mob_race) { auto it = m_preloadedEntities.find(mapIndex); if (it == m_preloadedEntities.end()) return; if (it->second.emplace(mob_race).second) { sys_log(0, "ENTITY_PRELOADING: extended [map: %d], with [entity race: %d]", mapIndex, mob_race); } } void SECTREE_MANAGER::SendPreloadEntitiesPacket(LPCHARACTER ch) { if (!ch) return; auto d = ch->GetDesc(); if (!d) return; auto lMapIndex = ch->GetMapIndex(); auto it = m_preloadedEntities.find(lMapIndex); if (it == m_preloadedEntities.end() || it->second.empty()) return; // rezerwacja pamięci std::vector<uint8_t> tempBuffer; tempBuffer.reserve(it->second.size() * sizeof(uint32_t)); // Zapisz dane do bufora for (const auto& race : it->second) { const uint8_t* raceBytes = reinterpret_cast<const uint8_t*>(&race); tempBuffer.insert(tempBuffer.end(), raceBytes, raceBytes + sizeof(uint32_t)); } TEMP_BUFFER buf; buf.write(tempBuffer.data(), tempBuffer.size()); TPacketGCPreloadEntities pack{}; pack.header = HEADER_GC_PRELOAD_ENTITIES; pack.count = static_cast<uint16_t>(it->second.size()); pack.size = sizeof(pack) + buf.size(); d->BufferedPacket(&pack, sizeof(pack)); d->Packet(buf.read_peek(), buf.size()); } #endif UserInterface/PythonNetworkStreamPhaseGame.cpp #ifdef ENABLE_ENTITY_PRELOADING bool CPythonNetworkStream::RecvPreloadEntitiesPacket() { TPacketGCPreloadEntities pack{}; if (!Recv(sizeof(pack), &pack)) { Tracef("Failed to receive preload entities packet header"); return false; } if (static_cast<int32_t>(pack.size) - sizeof(pack) != pack.count * sizeof(uint32_t)) { Tracef("Invalid preload entities packet size"); return false; } uint32_t dwEntityRace = 0; CInstanceBase::SCreateData d{}; auto& rkChrMgr = CPythonCharacterManager::Instance(); for (uint16_t i = 0; i < pack.count; ++i) { if (!Recv(sizeof(uint32_t), &dwEntityRace)) { Tracef("Failed to receive entity race data"); return false; } #ifdef _DEBUG TraceError("Preloading %d", dwEntityRace); #endif d.m_dwRace = dwEntityRace; d.m_dwVID = dwEntityRace; if (rkChrMgr.CreateInstance(d)) { rkChrMgr.DeleteInstance(dwEntityRace); } else { #ifdef _DEBUG TraceError("Failed to preload race %d", dwEntityRace); #endif } } return true; } #endif
Jeśli chcesz dodać odpowiedź, zaloguj się lub zarejestruj nowe konto