From a97a59759255d806e9f17d9f094e9e10966cc1a4 Mon Sep 17 00:00:00 2001 From: heinrich5991 Date: Tue, 11 Oct 2022 14:28:41 +0200 Subject: [PATCH 1/4] Inline `Is(GameType)` functions They are used for two purposes, coloring gametypes in the serverbrowser and enabling backward compatibility. These are independent, we shouldn't add more stuff to the backward compatibility, hence I split them up and inlined them. --- src/engine/client/client.cpp | 2 +- src/engine/client/serverbrowser.cpp | 65 +------------------- src/engine/serverbrowser.h | 12 ---- src/game/client/components/menus_browser.cpp | 16 ++--- src/game/client/gameclient.cpp | 19 +++--- 5 files changed, 20 insertions(+), 94 deletions(-) diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 479e90f62..a2bf22c36 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -1578,7 +1578,7 @@ bool CClient::ShouldSendChatTimeoutCodeHeuristic() { return false; } - return IsDDNet(&m_CurrentServerInfo); + return str_find_nocase(m_CurrentServerInfo.m_aGameType, "ddracenet") || str_find_nocase(m_CurrentServerInfo.m_aGameType, "ddnet"); } static CServerCapabilities GetServerCapabilities(int Version, int Flags) diff --git a/src/engine/client/serverbrowser.cpp b/src/engine/client/serverbrowser.cpp index f5046c293..996d4916b 100644 --- a/src/engine/client/serverbrowser.cpp +++ b/src/engine/client/serverbrowser.cpp @@ -1564,70 +1564,7 @@ bool CServerInfo::ParseLocation(int *pResult, const char *pString) return true; } -bool IsVanilla(const CServerInfo *pInfo) -{ - return !str_comp(pInfo->m_aGameType, "DM") || !str_comp(pInfo->m_aGameType, "TDM") || !str_comp(pInfo->m_aGameType, "CTF"); -} - -bool IsCatch(const CServerInfo *pInfo) -{ - return str_find_nocase(pInfo->m_aGameType, "catch"); -} - -bool IsInsta(const CServerInfo *pInfo) -{ - return str_find_nocase(pInfo->m_aGameType, "idm") || str_find_nocase(pInfo->m_aGameType, "itdm") || str_find_nocase(pInfo->m_aGameType, "ictf"); -} - -bool IsFNG(const CServerInfo *pInfo) -{ - return str_find_nocase(pInfo->m_aGameType, "fng"); -} - -bool IsRace(const CServerInfo *pInfo) -{ - return str_find_nocase(pInfo->m_aGameType, "race") || str_find_nocase(pInfo->m_aGameType, "fastcap"); -} - -bool IsFastCap(const CServerInfo *pInfo) -{ - return str_find_nocase(pInfo->m_aGameType, "fastcap"); -} - -bool IsBlockInfectionZ(const CServerInfo *pInfo) -{ - return str_find_nocase(pInfo->m_aGameType, "blockz") || - str_find_nocase(pInfo->m_aGameType, "infectionz"); -} - -bool IsBlockWorlds(const CServerInfo *pInfo) -{ - return (str_startswith(pInfo->m_aGameType, "bw ")) || (str_comp_nocase(pInfo->m_aGameType, "bw") == 0); -} - -bool IsCity(const CServerInfo *pInfo) -{ - return str_find_nocase(pInfo->m_aGameType, "city"); -} - -bool IsDDRace(const CServerInfo *pInfo) -{ - return str_find_nocase(pInfo->m_aGameType, "ddrace") || str_find_nocase(pInfo->m_aGameType, "mkrace"); -} - -bool IsDDNet(const CServerInfo *pInfo) -{ - return str_find_nocase(pInfo->m_aGameType, "ddracenet") || str_find_nocase(pInfo->m_aGameType, "ddnet"); -} - -// other - bool Is64Player(const CServerInfo *pInfo) { - return str_find(pInfo->m_aGameType, "64") || str_find(pInfo->m_aName, "64") || IsDDNet(pInfo); -} - -bool IsPlus(const CServerInfo *pInfo) -{ - return str_find(pInfo->m_aGameType, "+"); + return str_find(pInfo->m_aGameType, "64") || str_find(pInfo->m_aName, "64") || str_find_nocase(pInfo->m_aGameType, "ddracenet") || str_find_nocase(pInfo->m_aGameType, "ddnet"); } diff --git a/src/engine/serverbrowser.h b/src/engine/serverbrowser.h index c11a4b110..409079639 100644 --- a/src/engine/serverbrowser.h +++ b/src/engine/serverbrowser.h @@ -85,19 +85,7 @@ public: static bool ParseLocation(int *pResult, const char *pString); }; -bool IsVanilla(const CServerInfo *pInfo); -bool IsCatch(const CServerInfo *pInfo); -bool IsInsta(const CServerInfo *pInfo); -bool IsFNG(const CServerInfo *pInfo); -bool IsRace(const CServerInfo *pInfo); -bool IsFastCap(const CServerInfo *pInfo); -bool IsDDRace(const CServerInfo *pInfo); -bool IsDDNet(const CServerInfo *pInfo); -bool IsBlockWorlds(const CServerInfo *pInfo); -bool IsCity(const CServerInfo *pInfo); - bool Is64Player(const CServerInfo *pInfo); -bool IsPlus(const CServerInfo *pInfo); class IServerBrowser : public IInterface { diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index d27868253..6fec08c97 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -464,19 +464,19 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) { ColorHSLA hsl = ColorHSLA(1.0f, 1.0f, 1.0f); - if(IsVanilla(pItem)) + if(str_comp(pItem->m_aGameType, "DM") == 0 || str_comp(pItem->m_aGameType, "TDM") == 0 || str_comp(pItem->m_aGameType, "CTF") == 0) hsl = ColorHSLA(0.33f, 1.0f, 0.75f); - else if(IsCatch(pItem)) + else if(str_find_nocase(pItem->m_aGameType, "catch")) hsl = ColorHSLA(0.17f, 1.0f, 0.75f); - else if(IsInsta(pItem)) + else if(str_find_nocase(pItem->m_aGameType, "idm") || str_find_nocase(pItem->m_aGameType, "itdm") || str_find_nocase(pItem->m_aGameType, "ictf")) hsl = ColorHSLA(0.00f, 1.0f, 0.75f); - else if(IsFNG(pItem)) + else if(str_find_nocase(pItem->m_aGameType, "fng")) hsl = ColorHSLA(0.83f, 1.0f, 0.75f); - else if(IsDDNet(pItem)) + else if(str_find_nocase(pItem->m_aGameType, "ddracenet") || str_find_nocase(pItem->m_aGameType, "ddnet")) hsl = ColorHSLA(0.58f, 1.0f, 0.75f); - else if(IsDDRace(pItem)) + else if(str_find_nocase(pItem->m_aGameType, "ddrace") || str_find_nocase(pItem->m_aGameType, "mkrace")) hsl = ColorHSLA(0.75f, 1.0f, 0.75f); - else if(IsRace(pItem)) + else if(str_find_nocase(pItem->m_aGameType, "race") || str_find_nocase(pItem->m_aGameType, "fastcap")) hsl = ColorHSLA(0.46f, 1.0f, 0.75f); ColorRGBA rgb = color_cast(hsl); @@ -1167,7 +1167,7 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View) if(!pSelectedServer->m_aClients[i].m_Player) str_copy(aTemp, "SPEC"); - else if(IsRace(pSelectedServer) && g_Config.m_ClDDRaceScoreBoard) + else if((str_find_nocase(pSelectedServer->m_aGameType, "race") || str_find_nocase(pSelectedServer->m_aGameType, "fastcap")) && g_Config.m_ClDDRaceScoreBoard) { if(pSelectedServer->m_aClients[i].m_Score == -9999 || pSelectedServer->m_aClients[i].m_Score == 0) aTemp[0] = 0; diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 9e4de7a66..376b95014 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -1009,15 +1009,16 @@ static CGameInfo GetGameInfo(const CNetObj_GameInfoEx *pInfoEx, int InfoExSize, bool FDDrace; if(Version < 1) { - Race = IsRace(pFallbackServerInfo); - FastCap = IsFastCap(pFallbackServerInfo); - FNG = IsFNG(pFallbackServerInfo); - DDRace = IsDDRace(pFallbackServerInfo); - DDNet = IsDDNet(pFallbackServerInfo); - BlockWorlds = IsBlockWorlds(pFallbackServerInfo); - City = IsCity(pFallbackServerInfo); - Vanilla = IsVanilla(pFallbackServerInfo); - Plus = IsPlus(pFallbackServerInfo); + const char *pGameType = pFallbackServerInfo->m_aGameType; + Race = str_find_nocase(pGameType, "race") || str_find_nocase(pGameType, "fastcap"); + FastCap = str_find_nocase(pGameType, "fastcap"); + FNG = str_find_nocase(pGameType, "fng"); + DDRace = str_find_nocase(pGameType, "ddrace") || str_find_nocase(pGameType, "mkrace"); + DDNet = str_find_nocase(pGameType, "ddracenet") || str_find_nocase(pGameType, "ddnet"); + BlockWorlds = str_startswith(pGameType, "bw ") || str_comp_nocase(pGameType, "bw") == 0; + City = str_find_nocase(pGameType, "city"); + Vanilla = str_comp(pGameType, "DM") == 0 || str_comp(pGameType, "TDM") == 0 || str_comp(pGameType, "CTF") == 0; + Plus = str_find(pGameType, "+"); FDDrace = false; } else From 150485e3b0f1cd76ebfed7ca1b9f06aac531e678 Mon Sep 17 00:00:00 2001 From: heinrich5991 Date: Tue, 11 Oct 2022 14:35:15 +0200 Subject: [PATCH 2/4] Remove support for legacy 64 player info protocol It was only being used for "Leak IP" favorites and LAN servers. Since KoG has upgraded to latest DDNet, there aren't much servers left that don't understand the new 64 player info protocol. Keywords: fstd, dtsf --- src/engine/client/client.cpp | 15 ++------ src/engine/client/serverbrowser.cpp | 55 +---------------------------- src/engine/client/serverbrowser.h | 2 -- src/engine/serverbrowser.h | 2 -- 4 files changed, 3 insertions(+), 71 deletions(-) diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index a2bf22c36..291410da9 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -1345,8 +1345,6 @@ void CClient::ProcessConnlessPacket(CNetChunk *pPacket) int Type = -1; if(mem_comp(pPacket->m_pData, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)) == 0) Type = SERVERINFO_VANILLA; - else if(mem_comp(pPacket->m_pData, SERVERBROWSE_INFO_64_LEGACY, sizeof(SERVERBROWSE_INFO_64_LEGACY)) == 0) - Type = SERVERINFO_64_LEGACY; else if(mem_comp(pPacket->m_pData, SERVERBROWSE_INFO_EXTENDED, sizeof(SERVERBROWSE_INFO_EXTENDED)) == 0) Type = SERVERINFO_EXTENDED; else if(mem_comp(pPacket->m_pData, SERVERBROWSE_INFO_EXTENDED_MORE, sizeof(SERVERBROWSE_INFO_EXTENDED_MORE)) == 0) @@ -1375,8 +1373,7 @@ void CClient::ProcessServerInfo(int RawType, NETADDR *pFrom, const void *pData, CServerInfo Info = {0}; int SavedType = SavedServerInfoType(RawType); - if((SavedType == SERVERINFO_64_LEGACY || SavedType == SERVERINFO_EXTENDED) && - pEntry && pEntry->m_GotInfo && SavedType == pEntry->m_Info.m_Type) + if(SavedType == SERVERINFO_EXTENDED && pEntry && pEntry->m_GotInfo && SavedType == pEntry->m_Info.m_Type) { Info = pEntry->m_Info; } @@ -1391,7 +1388,6 @@ void CClient::ProcessServerInfo(int RawType, NETADDR *pFrom, const void *pData, #define GET_STRING(array) str_copy(array, Up.GetString(CUnpacker::SANITIZE_CC | CUnpacker::SKIP_START_WHITESPACES), sizeof(array)) #define GET_INT(integer) (integer) = str_toint(Up.GetString()) - int Offset = 0; // Only used for SavedType == SERVERINFO_64_LEGACY int Token; int PacketNo = 0; // Only used if SavedType == SERVERINFO_EXTENDED @@ -1449,13 +1445,6 @@ void CClient::ProcessServerInfo(int RawType, NETADDR *pFrom, const void *pData, dbg_assert(false, "unknown serverinfo type"); } - if(SavedType == SERVERINFO_64_LEGACY) - Offset = Up.GetInt(); - - // Check for valid offset. - if(Offset < 0) - return; - if(SavedType == SERVERINFO_EXTENDED) PacketNo = 0; } @@ -1478,7 +1467,7 @@ void CClient::ProcessServerInfo(int RawType, NETADDR *pFrom, const void *pData, } bool IgnoreError = false; - for(int i = Offset; i < MAX_CLIENTS && Info.m_NumReceivedClients < MAX_CLIENTS && !Up.Error(); i++) + for(int i = 0; i < MAX_CLIENTS && Info.m_NumReceivedClients < MAX_CLIENTS && !Up.Error(); i++) { CServerInfo::CClient *pClient = &Info.m_aClients[Info.m_NumReceivedClients]; GET_STRING(pClient->m_aName); diff --git a/src/engine/client/serverbrowser.cpp b/src/engine/client/serverbrowser.cpp index 996d4916b..aadff40dd 100644 --- a/src/engine/client/serverbrowser.cpp +++ b/src/engine/client/serverbrowser.cpp @@ -720,12 +720,6 @@ void CServerBrowser::OnServerInfoUpdate(const NETADDR &Addr, int Token, const CS { SetInfo(pEntry, *pInfo); pEntry->m_Info.m_Latency = minimum(static_cast((time_get() - m_BroadcastTime) * 1000 / time_freq()), 999); - if(pInfo->m_Type == SERVERINFO_VANILLA && Is64Player(pInfo)) - { - pEntry->m_Request64Legacy = true; - // Force a quick update. - RequestImpl64(Addr, pEntry); - } } else if(pEntry->m_RequestTime > 0) { @@ -747,16 +741,6 @@ void CServerBrowser::OnServerInfoUpdate(const NETADDR &Addr, int Token, const CS SetLatency(Addr, Latency); } pEntry->m_RequestTime = -1; // Request has been answered - - if(!pEntry->m_RequestIgnoreInfo) - { - if(pInfo->m_Type == SERVERINFO_VANILLA && Is64Player(pInfo)) - { - pEntry->m_Request64Legacy = true; - // Force a quick update. - RequestImpl64(Addr, pEntry); - } - } } RemoveRequest(pEntry); @@ -872,35 +856,6 @@ void CServerBrowser::RequestImpl(const NETADDR &Addr, CServerEntry *pEntry, int pEntry->m_RequestTime = time_get(); } -void CServerBrowser::RequestImpl64(const NETADDR &Addr, CServerEntry *pEntry) const -{ - unsigned char aBuffer[sizeof(SERVERBROWSE_GETINFO_64_LEGACY) + 1]; - CNetChunk Packet; - - if(g_Config.m_Debug) - { - char aAddrStr[NETADDR_MAXSTRSIZE]; - net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr), true); - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "requesting server info 64 from %s", aAddrStr); - m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", aBuf); - } - - mem_copy(aBuffer, SERVERBROWSE_GETINFO_64_LEGACY, sizeof(SERVERBROWSE_GETINFO_64_LEGACY)); - aBuffer[sizeof(SERVERBROWSE_GETINFO_64_LEGACY)] = GetBasicToken(GenerateToken(Addr)); - - Packet.m_ClientID = -1; - Packet.m_Address = Addr; - Packet.m_Flags = NETSENDFLAG_CONNLESS; - Packet.m_DataSize = sizeof(aBuffer); - Packet.m_pData = aBuffer; - - m_pNetClient->Send(&Packet); - - if(pEntry) - pEntry->m_RequestTime = time_get(); -} - void CServerBrowser::RequestCurrentServer(const NETADDR &Addr) const { RequestImpl(Addr, nullptr, nullptr, nullptr, false); @@ -1123,10 +1078,7 @@ void CServerBrowser::Update(bool ForceResort) if(pEntry->m_RequestTime == 0) { - if(pEntry->m_Request64Legacy) - RequestImpl64(pEntry->m_Info.m_aAddresses[0], pEntry); - else - RequestImpl(pEntry->m_Info.m_aAddresses[0], pEntry, nullptr, nullptr, false); + RequestImpl(pEntry->m_Info.m_aAddresses[0], pEntry, nullptr, nullptr, false); } Count++; @@ -1563,8 +1515,3 @@ bool CServerInfo::ParseLocation(int *pResult, const char *pString) } return true; } - -bool Is64Player(const CServerInfo *pInfo) -{ - return str_find(pInfo->m_aGameType, "64") || str_find(pInfo->m_aName, "64") || str_find_nocase(pInfo->m_aGameType, "ddracenet") || str_find_nocase(pInfo->m_aGameType, "ddnet"); -} diff --git a/src/engine/client/serverbrowser.h b/src/engine/client/serverbrowser.h index ac617c5f6..a21d34474 100644 --- a/src/engine/client/serverbrowser.h +++ b/src/engine/client/serverbrowser.h @@ -32,7 +32,6 @@ public: int64_t m_RequestTime; bool m_RequestIgnoreInfo; int m_GotInfo; - bool m_Request64Legacy; CServerInfo m_Info; CServerEntry *m_pPrevReq; // request list @@ -131,7 +130,6 @@ public: void SetBaseInfo(class CNetClient *pClient, const char *pNetVersion); void OnInit(); - void RequestImpl64(const NETADDR &Addr, CServerEntry *pEntry) const; void QueueRequest(CServerEntry *pEntry); CServerEntry *Find(const NETADDR &Addr); int GetCurrentType() override { return m_ServerlistType; } diff --git a/src/engine/serverbrowser.h b/src/engine/serverbrowser.h index 409079639..1e9032036 100644 --- a/src/engine/serverbrowser.h +++ b/src/engine/serverbrowser.h @@ -85,8 +85,6 @@ public: static bool ParseLocation(int *pResult, const char *pString); }; -bool Is64Player(const CServerInfo *pInfo); - class IServerBrowser : public IInterface { MACRO_INTERFACE("serverbrowser", 0) From a595936fb5baea84ee4be81a692ca570a841e7c6 Mon Sep 17 00:00:00 2001 From: heinrich5991 Date: Tue, 11 Oct 2022 16:07:43 +0200 Subject: [PATCH 3/4] Remove chat timeout code fallback heuristic KoG is now on the newest version, we no longer need this. --- src/engine/client/client.cpp | 11 +---------- src/engine/client/client.h | 2 -- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 291410da9..5111603eb 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -1561,15 +1561,6 @@ void CClient::ProcessServerInfo(int RawType, NETADDR *pFrom, const void *pData, #undef GET_INT } -bool CClient::ShouldSendChatTimeoutCodeHeuristic() -{ - if(m_ServerSentCapabilities) - { - return false; - } - return str_find_nocase(m_CurrentServerInfo.m_aGameType, "ddracenet") || str_find_nocase(m_CurrentServerInfo.m_aGameType, "ddnet"); -} - static CServerCapabilities GetServerCapabilities(int Version, int Flags) { CServerCapabilities Result; @@ -2156,7 +2147,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy) if(m_aReceivedSnapshots[Conn] > 50 && !m_aCodeRunAfterJoin[Conn]) { - if(m_ServerCapabilities.m_ChatTimeoutCode || ShouldSendChatTimeoutCodeHeuristic()) + if(m_ServerCapabilities.m_ChatTimeoutCode) { CNetMsg_Cl_Say MsgP; MsgP.m_Team = 0; diff --git a/src/engine/client/client.h b/src/engine/client/client.h index 2f79ca3d6..d327f7c5d 100644 --- a/src/engine/client/client.h +++ b/src/engine/client/client.h @@ -251,8 +251,6 @@ class CClient : public IClient, public CDemoPlayer::IListener bool m_ServerSentCapabilities; CServerCapabilities m_ServerCapabilities; - bool ShouldSendChatTimeoutCodeHeuristic(); - CServerInfo m_CurrentServerInfo; int64_t m_CurrentServerInfoRequestTime; // >= 0 should request, == -1 got info From a804c3ca5bb7d45eb417d7fe541ed3a6151eaabb Mon Sep 17 00:00:00 2001 From: Jupeyy Date: Wed, 12 Oct 2022 19:25:59 +0200 Subject: [PATCH 4/4] Get away from vector for skins most of the time it uses the index just to get the skin, downloaded skins change the index. Now its simply a heap object and downloaded skins load directly. Also the loading might be a bit faster bcs it had a loop lookup Also O(1) lookup --- src/game/client/components/chat.cpp | 2 +- src/game/client/components/ghost.cpp | 14 +- src/game/client/components/menus_settings.cpp | 65 +++---- src/game/client/components/players.cpp | 8 +- src/game/client/components/skins.cpp | 159 +++++++++--------- src/game/client/components/skins.h | 35 ++-- src/game/client/gameclient.cpp | 4 +- src/game/client/skin.h | 15 +- 8 files changed, 154 insertions(+), 148 deletions(-) diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp index 36607f960..6fe27b422 100644 --- a/src/game/client/components/chat.cpp +++ b/src/game/client/components/chat.cpp @@ -928,7 +928,7 @@ void CChat::RefindSkins() { if(Line.m_HasRenderTee) { - const CSkin *pSkin = m_pClient->m_Skins.Get(m_pClient->m_Skins.Find(Line.m_aSkinName)); + const CSkin *pSkin = m_pClient->m_Skins.Find(Line.m_aSkinName); if(Line.m_CustomColoredSkin) Line.m_RenderSkin = pSkin->m_ColorableSkin; else diff --git a/src/game/client/components/ghost.cpp b/src/game/client/components/ghost.cpp index 5be45edca..aefb66e1f 100644 --- a/src/game/client/components/ghost.cpp +++ b/src/game/client/components/ghost.cpp @@ -352,15 +352,14 @@ void CGhost::OnRender() if(Player.m_Weapon == WEAPON_NINJA && g_Config.m_ClShowNinja) { // change the skin for the ghost to the ninja - int Skin = m_pClient->m_Skins.Find("x_ninja"); - if(Skin != -1) + const auto *pSkin = m_pClient->m_Skins.FindOrNullptr("x_ninja"); + if(pSkin != nullptr) { bool IsTeamplay = false; if(m_pClient->m_Snap.m_pGameInfoObj) IsTeamplay = (m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags & GAMEFLAG_TEAMS) != 0; GhostNinjaRenderInfo = Ghost.m_RenderInfo; - const CSkin *pSkin = m_pClient->m_Skins.Get(Skin); GhostNinjaRenderInfo.m_OriginalRenderSkin = pSkin->m_OriginalSkin; GhostNinjaRenderInfo.m_ColorableRenderSkin = pSkin->m_ColorableSkin; GhostNinjaRenderInfo.m_BloodColor = pSkin->m_BloodColor; @@ -387,8 +386,7 @@ void CGhost::InitRenderInfos(CGhostItem *pGhost) IntsToStr(&pGhost->m_Skin.m_Skin0, 6, aSkinName); CTeeRenderInfo *pRenderInfo = &pGhost->m_RenderInfo; - int SkinId = m_pClient->m_Skins.Find(aSkinName); - const CSkin *pSkin = m_pClient->m_Skins.Get(SkinId); + const CSkin *pSkin = m_pClient->m_Skins.Find(aSkinName); pRenderInfo->m_OriginalRenderSkin = pSkin->m_OriginalSkin; pRenderInfo->m_ColorableRenderSkin = pSkin->m_ColorableSkin; pRenderInfo->m_BloodColor = pSkin->m_BloodColor; @@ -679,8 +677,7 @@ void CGhost::RefindSkin() { CTeeRenderInfo *pRenderInfo = &Ghost.m_RenderInfo; - int SkinId = m_pClient->m_Skins.Find(aSkinName); - const CSkin *pSkin = m_pClient->m_Skins.Get(SkinId); + const CSkin *pSkin = m_pClient->m_Skins.Find(aSkinName); pRenderInfo->m_OriginalRenderSkin = pSkin->m_OriginalSkin; pRenderInfo->m_ColorableRenderSkin = pSkin->m_ColorableSkin; pRenderInfo->m_SkinMetrics = pSkin->m_Metrics; @@ -691,8 +688,7 @@ void CGhost::RefindSkin() { CTeeRenderInfo *pRenderInfo = &m_CurGhost.m_RenderInfo; - int SkinId = m_pClient->m_Skins.Find(aSkinName); - const CSkin *pSkin = m_pClient->m_Skins.Get(SkinId); + const CSkin *pSkin = m_pClient->m_Skins.Find(aSkinName); pRenderInfo->m_OriginalRenderSkin = pSkin->m_OriginalSkin; pRenderInfo->m_ColorableRenderSkin = pSkin->m_ColorableSkin; pRenderInfo->m_SkinMetrics = pSkin->m_Metrics; diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index 044b817ac..e5275ca62 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -404,10 +405,10 @@ struct CUISkin CUISkin(const CSkin *pSkin) : m_pSkin(pSkin) {} - bool operator<(const CUISkin &Other) const { return str_comp_nocase(m_pSkin->m_aName, Other.m_pSkin->m_aName) < 0; } + bool operator<(const CUISkin &Other) const { return str_comp_nocase(m_pSkin->GetName(), Other.m_pSkin->GetName()) < 0; } - bool operator<(const char *pOther) const { return str_comp_nocase(m_pSkin->m_aName, pOther) < 0; } - bool operator==(const char *pOther) const { return !str_comp_nocase(m_pSkin->m_aName, pOther); } + bool operator<(const char *pOther) const { return str_comp_nocase(m_pSkin->GetName(), pOther) < 0; } + bool operator==(const char *pOther) const { return !str_comp_nocase(m_pSkin->GetName(), pOther); } }; void CMenus::RefreshSkins() @@ -571,7 +572,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView) // which invalidates the skin // skin info CTeeRenderInfo OwnSkinInfo; - const CSkin *pSkin = m_pClient->m_Skins.Get(m_pClient->m_Skins.Find(pSkinName)); + const CSkin *pSkin = m_pClient->m_Skins.Find(pSkinName); OwnSkinInfo.m_OriginalRenderSkin = pSkin->m_OriginalSkin; OwnSkinInfo.m_ColorableRenderSkin = pSkin->m_ColorableSkin; OwnSkinInfo.m_SkinMetrics = pSkin->m_Metrics; @@ -718,15 +719,14 @@ void CMenus::RenderSettingsTee(CUIRect MainView) // a downloading skin s_SkinCount = m_pClient->m_Skins.Num(); m_SkinFavoritesChanged = false; - bool RequiresRebuild = false; auto &&SkinNotFiltered = [&](const CSkin *pSkinToBeSelected) { // filter quick search - if(g_Config.m_ClSkinFilterString[0] != '\0' && !str_utf8_find_nocase(pSkinToBeSelected->m_aName, g_Config.m_ClSkinFilterString)) + if(g_Config.m_ClSkinFilterString[0] != '\0' && !str_utf8_find_nocase(pSkinToBeSelected->GetName(), g_Config.m_ClSkinFilterString)) return false; // no special skins - if((pSkinToBeSelected->m_aName[0] == 'x' && pSkinToBeSelected->m_aName[1] == '_')) + if((pSkinToBeSelected->GetName()[0] == 'x' && pSkinToBeSelected->GetName()[1] == '_')) return false; if(pSkinToBeSelected == 0) @@ -737,38 +737,27 @@ void CMenus::RenderSettingsTee(CUIRect MainView) for(const auto &it : m_SkinFavorites) { - const auto FirstSkinIndex = m_pClient->m_Skins.Find(it.c_str()); - // second call is intended, our implemention doesnt return the index in the call where the download finished - const auto SkinIndex = m_pClient->m_Skins.Find(it.c_str()); - if(SkinIndex == -1) - continue; - if(FirstSkinIndex == -1 && SkinIndex != -1) - { - // skin list changed, rebuild next frame - RequiresRebuild = true; - } - const CSkin *pSkinToBeSelected = m_pClient->m_Skins.Get(SkinIndex); + const CSkin *pSkinToBeSelected = m_pClient->m_Skins.FindOrNullptr(it.c_str()); - if(!SkinNotFiltered(pSkinToBeSelected)) + if(pSkinToBeSelected == nullptr || !SkinNotFiltered(pSkinToBeSelected)) continue; s_vFavoriteSkinListHelper.emplace_back(pSkinToBeSelected); } - for(int i = 0; i < m_pClient->m_Skins.Num(); ++i) + for(const auto &SkinIt : m_pClient->m_Skins.GetSkinsUnsafe()) { - const CSkin *pSkinToBeSelected = m_pClient->m_Skins.Get(i); - - if(!SkinNotFiltered(pSkinToBeSelected)) + const auto &pSkinToBeSelected = SkinIt.second; + if(!SkinNotFiltered(pSkinToBeSelected.get())) continue; - if(std::find(m_SkinFavorites.begin(), m_SkinFavorites.end(), pSkinToBeSelected->m_aName) == m_SkinFavorites.end()) - s_vSkinListHelper.emplace_back(pSkinToBeSelected); + if(std::find(m_SkinFavorites.begin(), m_SkinFavorites.end(), pSkinToBeSelected->GetName()) == m_SkinFavorites.end()) + s_vSkinListHelper.emplace_back(pSkinToBeSelected.get()); } std::sort(s_vSkinListHelper.begin(), s_vSkinListHelper.end()); std::sort(s_vFavoriteSkinListHelper.begin(), s_vFavoriteSkinListHelper.end()); s_vSkinList = s_vFavoriteSkinListHelper; s_vSkinList.insert(s_vSkinList.end(), s_vSkinListHelper.begin(), s_vSkinListHelper.end()); - s_InitSkinlist = RequiresRebuild; + s_InitSkinlist = false; } auto &&RenderFavIcon = [&](const CUIRect &FavIcon, bool AsFav) { @@ -793,7 +782,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView) { const CSkin *pSkinToBeDraw = s_vSkinList[i].m_pSkin; - if(str_comp(pSkinToBeDraw->m_aName, pSkinName) == 0) + if(str_comp(pSkinToBeDraw->GetName(), pSkinName) == 0) OldSelected = i; CListboxItem Item = UiDoListboxNextItem(pSkinToBeDraw, OldSelected >= 0 && (size_t)OldSelected == i); @@ -816,7 +805,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView) { SLabelProperties Props; Props.m_MaxWidth = Item.m_Rect.w; - UI()->DoLabel(&Item.m_Rect, pSkinToBeDraw->m_aName, 12.0f, TEXTALIGN_LEFT, Props); + UI()->DoLabel(&Item.m_Rect, pSkinToBeDraw->GetName(), 12.0f, TEXTALIGN_LEFT, Props); } if(g_Config.m_Debug) { @@ -831,7 +820,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView) // render skin favorite icon { - const auto SkinItFav = m_SkinFavorites.find(pSkinToBeDraw->m_aName); + const auto SkinItFav = m_SkinFavorites.find(pSkinToBeDraw->GetName()); const auto IsFav = SkinItFav != m_SkinFavorites.end(); CUIRect FavIcon; OriginalRect.HSplitTop(20.0f, &FavIcon, nullptr); @@ -847,7 +836,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView) RenderFavIcon(FavIcon, IsFav); } } - if(UI()->DoButtonLogic(pSkinToBeDraw->m_aName, 0, &FavIcon)) + if(UI()->DoButtonLogic(pSkinToBeDraw->GetName(), 0, &FavIcon)) { if(IsFav) { @@ -855,7 +844,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView) } else { - m_SkinFavorites.emplace(pSkinToBeDraw->m_aName); + m_SkinFavorites.emplace(pSkinToBeDraw->GetName()); } s_InitSkinlist = true; } @@ -866,7 +855,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView) const int NewSelected = UiDoListboxEnd(&s_ScrollValue, 0); if(OldSelected != NewSelected) { - mem_copy(pSkinName, s_vSkinList[NewSelected].m_pSkin->m_aName, sizeof(g_Config.m_ClPlayerSkin)); + mem_copy(pSkinName, s_vSkinList[NewSelected].m_pSkin->GetName(), sizeof(g_Config.m_ClPlayerSkin)); SetNeedSendInfo(); } @@ -2900,7 +2889,7 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView) // Load skins - int DefaultInd = GameClient()->m_Skins.Find("default"); + const auto *pDefaultSkin = GameClient()->m_Skins.Find("default"); for(auto &Info : aRenderInfo) { @@ -2908,13 +2897,13 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView) Info.m_CustomColoredSkin = false; } - int ind = -1; + const CSkin *pSkin = nullptr; int pos = 0; - aRenderInfo[pos++].m_OriginalRenderSkin = GameClient()->m_Skins.Get(DefaultInd)->m_OriginalSkin; - aRenderInfo[pos++].m_OriginalRenderSkin = (ind = GameClient()->m_Skins.Find("pinky")) != -1 ? GameClient()->m_Skins.Get(ind)->m_OriginalSkin : aRenderInfo[0].m_OriginalRenderSkin; - aRenderInfo[pos++].m_OriginalRenderSkin = (ind = GameClient()->m_Skins.Find("cammostripes")) != -1 ? GameClient()->m_Skins.Get(ind)->m_OriginalSkin : aRenderInfo[0].m_OriginalRenderSkin; - aRenderInfo[pos++].m_OriginalRenderSkin = (ind = GameClient()->m_Skins.Find("beast")) != -1 ? GameClient()->m_Skins.Get(ind)->m_OriginalSkin : aRenderInfo[0].m_OriginalRenderSkin; + aRenderInfo[pos++].m_OriginalRenderSkin = pDefaultSkin->m_OriginalSkin; + aRenderInfo[pos++].m_OriginalRenderSkin = (pSkin = GameClient()->m_Skins.FindOrNullptr("pinky")) != nullptr ? pSkin->m_OriginalSkin : aRenderInfo[0].m_OriginalRenderSkin; + aRenderInfo[pos++].m_OriginalRenderSkin = (pSkin = GameClient()->m_Skins.FindOrNullptr("cammostripes")) != nullptr ? pSkin->m_OriginalSkin : aRenderInfo[0].m_OriginalRenderSkin; + aRenderInfo[pos++].m_OriginalRenderSkin = (pSkin = GameClient()->m_Skins.FindOrNullptr("beast")) != nullptr ? pSkin->m_OriginalSkin : aRenderInfo[0].m_OriginalRenderSkin; } // System diff --git a/src/game/client/components/players.cpp b/src/game/client/components/players.cpp index 5c7664695..ed875dc73 100644 --- a/src/game/client/components/players.cpp +++ b/src/game/client/components/players.cpp @@ -769,10 +769,9 @@ void CPlayers::OnRender() if((CharacterInfo.m_Cur.m_Weapon == WEAPON_NINJA || (CharacterInfo.m_HasExtendedData && CharacterInfo.m_ExtendedData.m_FreezeEnd != 0)) && g_Config.m_ClShowNinja) { // change the skin for the player to the ninja - int Skin = m_pClient->m_Skins.Find("x_ninja"); - if(Skin != -1) + const auto *pSkin = m_pClient->m_Skins.FindOrNullptr("x_ninja"); + if(pSkin != nullptr) { - const CSkin *pSkin = m_pClient->m_Skins.Get(Skin); m_aRenderInfo[i].m_OriginalRenderSkin = pSkin->m_OriginalSkin; m_aRenderInfo[i].m_ColorableRenderSkin = pSkin->m_ColorableSkin; m_aRenderInfo[i].m_BloodColor = pSkin->m_BloodColor; @@ -786,8 +785,7 @@ void CPlayers::OnRender() } } } - int Skin = m_pClient->m_Skins.Find("x_spec"); - const CSkin *pSkin = m_pClient->m_Skins.Get(Skin); + const CSkin *pSkin = m_pClient->m_Skins.Find("x_spec"); m_RenderInfoSpec.m_OriginalRenderSkin = pSkin->m_OriginalSkin; m_RenderInfoSpec.m_ColorableRenderSkin = pSkin->m_ColorableSkin; m_RenderInfoSpec.m_BloodColor = pSkin->m_BloodColor; diff --git a/src/game/client/components/skins.cpp b/src/game/client/components/skins.cpp index d759410a8..2938e8402 100644 --- a/src/game/client/components/skins.cpp +++ b/src/game/client/components/skins.cpp @@ -70,18 +70,14 @@ int CSkins::SkinScan(const char *pName, int IsDir, int DirType, void *pUser) // Don't add duplicate skins (one from user's config directory, other from // client itself) - for(int i = 0; i < pSelf->Num(); i++) - { - const char *pExName = pSelf->Get(i)->m_aName; - if(str_comp(pExName, aNameWithoutPng) == 0) - return 0; - } + if(pSelf->m_Skins.find(aNameWithoutPng) != pSelf->m_Skins.end()) + return 0; char aBuf[IO_MAX_PATH_LENGTH]; str_format(aBuf, sizeof(aBuf), "skins/%s", pName); - auto SkinID = pSelf->LoadSkin(aNameWithoutPng, aBuf, DirType); - pUserReal->m_SkinLoadedFunc(SkinID); - return SkinID; + pSelf->LoadSkin(aNameWithoutPng, aBuf, DirType); + pUserReal->m_SkinLoadedFunc((int)pSelf->m_Skins.size()); + return 0; } static void CheckMetrics(CSkin::SSkinMetricVariable &Metrics, uint8_t *pImg, int ImgWidth, int ImgX, int ImgY, int CheckWidth, int CheckHeight) @@ -119,7 +115,7 @@ static void CheckMetrics(CSkin::SSkinMetricVariable &Metrics, uint8_t *pImg, int Metrics.m_MaxHeight = CheckHeight; } -int CSkins::LoadSkin(const char *pName, const char *pPath, int DirType) +const CSkin *CSkins::LoadSkin(const char *pName, const char *pPath, int DirType) { CImageInfo Info; if(!LoadSkinPNG(Info, pName, pPath, DirType)) @@ -139,7 +135,7 @@ bool CSkins::LoadSkinPNG(CImageInfo &Info, const char *pName, const char *pPath, return true; } -int CSkins::LoadSkin(const char *pName, CImageInfo &Info) +const CSkin *CSkins::LoadSkin(const char *pName, CImageInfo &Info) { char aBuf[512]; @@ -147,16 +143,16 @@ int CSkins::LoadSkin(const char *pName, CImageInfo &Info) { str_format(aBuf, sizeof(aBuf), "skin failed image divisibility: %s", pName); Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "game", aBuf); - return 0; + return nullptr; } if(!Graphics()->IsImageFormatRGBA(pName, Info)) { str_format(aBuf, sizeof(aBuf), "skin format is not RGBA: %s", pName); Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "game", aBuf); - return 0; + return nullptr; } - CSkin Skin; + CSkin Skin{pName}; Skin.m_OriginalSkin.m_Body = Graphics()->LoadSpriteTexture(Info, &g_pData->m_aSprites[SPRITE_TEE_BODY]); Skin.m_OriginalSkin.m_BodyOutline = Graphics()->LoadSpriteTexture(Info, &g_pData->m_aSprites[SPRITE_TEE_BODY_OUTLINE]); Skin.m_OriginalSkin.m_Feet = Graphics()->LoadSpriteTexture(Info, &g_pData->m_aSprites[SPRITE_TEE_FOOT]); @@ -194,7 +190,7 @@ int CSkins::LoadSkin(const char *pName, CImageInfo &Info) int BodyWidth = g_pData->m_aSprites[SPRITE_TEE_BODY].m_W * (Info.m_Width / g_pData->m_aSprites[SPRITE_TEE_BODY].m_pSet->m_Gridx); // body width int BodyHeight = g_pData->m_aSprites[SPRITE_TEE_BODY].m_H * (Info.m_Height / g_pData->m_aSprites[SPRITE_TEE_BODY].m_pSet->m_Gridy); // body height if(BodyWidth > Info.m_Width || BodyHeight > Info.m_Height) - return 0; + return nullptr; unsigned char *pData = (unsigned char *)Info.m_pData; const int PixelStep = 4; int Pitch = Info.m_Width * PixelStep; @@ -290,16 +286,16 @@ int CSkins::LoadSkin(const char *pName, CImageInfo &Info) Graphics()->FreePNG(&Info); // set skin data - str_copy(Skin.m_aName, pName); if(g_Config.m_Debug) { - str_format(aBuf, sizeof(aBuf), "load skin %s", Skin.m_aName); + str_format(aBuf, sizeof(aBuf), "load skin %s", Skin.GetName()); Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "game", aBuf); } - m_vSkins.insert(std::lower_bound(m_vSkins.begin(), m_vSkins.end(), Skin), Skin); + auto &&pSkin = std::make_unique(std::move(Skin)); + const auto SkinInsertIt = m_Skins.insert({pSkin->GetName(), std::move(pSkin)}); - return 0; + return SkinInsertIt.first->second.get(); } void CSkins::OnInit() @@ -319,132 +315,132 @@ void CSkins::OnInit() } // load skins; - Refresh([this](int SkinID) { + Refresh([this](int SkinCounter) { GameClient()->m_Menus.RenderLoading(Localize("Loading DDNet Client"), Localize("Loading skin files"), 0); }); } void CSkins::Refresh(TSkinLoadedCBFunc &&SkinLoadedFunc) { - for(auto &Skin : m_vSkins) + for(const auto &SkinIt : m_Skins) { - Graphics()->UnloadTexture(&Skin.m_OriginalSkin.m_Body); - Graphics()->UnloadTexture(&Skin.m_OriginalSkin.m_BodyOutline); - Graphics()->UnloadTexture(&Skin.m_OriginalSkin.m_Feet); - Graphics()->UnloadTexture(&Skin.m_OriginalSkin.m_FeetOutline); - Graphics()->UnloadTexture(&Skin.m_OriginalSkin.m_Hands); - Graphics()->UnloadTexture(&Skin.m_OriginalSkin.m_HandsOutline); - for(auto &Eye : Skin.m_OriginalSkin.m_aEyes) + const auto &pSkin = SkinIt.second; + Graphics()->UnloadTexture(&pSkin->m_OriginalSkin.m_Body); + Graphics()->UnloadTexture(&pSkin->m_OriginalSkin.m_BodyOutline); + Graphics()->UnloadTexture(&pSkin->m_OriginalSkin.m_Feet); + Graphics()->UnloadTexture(&pSkin->m_OriginalSkin.m_FeetOutline); + Graphics()->UnloadTexture(&pSkin->m_OriginalSkin.m_Hands); + Graphics()->UnloadTexture(&pSkin->m_OriginalSkin.m_HandsOutline); + for(auto &Eye : pSkin->m_OriginalSkin.m_aEyes) Graphics()->UnloadTexture(&Eye); - Graphics()->UnloadTexture(&Skin.m_ColorableSkin.m_Body); - Graphics()->UnloadTexture(&Skin.m_ColorableSkin.m_BodyOutline); - Graphics()->UnloadTexture(&Skin.m_ColorableSkin.m_Feet); - Graphics()->UnloadTexture(&Skin.m_ColorableSkin.m_FeetOutline); - Graphics()->UnloadTexture(&Skin.m_ColorableSkin.m_Hands); - Graphics()->UnloadTexture(&Skin.m_ColorableSkin.m_HandsOutline); - for(auto &Eye : Skin.m_ColorableSkin.m_aEyes) + Graphics()->UnloadTexture(&pSkin->m_ColorableSkin.m_Body); + Graphics()->UnloadTexture(&pSkin->m_ColorableSkin.m_BodyOutline); + Graphics()->UnloadTexture(&pSkin->m_ColorableSkin.m_Feet); + Graphics()->UnloadTexture(&pSkin->m_ColorableSkin.m_FeetOutline); + Graphics()->UnloadTexture(&pSkin->m_ColorableSkin.m_Hands); + Graphics()->UnloadTexture(&pSkin->m_ColorableSkin.m_HandsOutline); + for(auto &Eye : pSkin->m_ColorableSkin.m_aEyes) Graphics()->UnloadTexture(&Eye); } - m_vSkins.clear(); - m_vDownloadSkins.clear(); + m_Skins.clear(); + m_DownloadSkins.clear(); m_DownloadingSkins = 0; SSkinScanUser SkinScanUser; SkinScanUser.m_pThis = this; SkinScanUser.m_SkinLoadedFunc = SkinLoadedFunc; Storage()->ListDirectory(IStorage::TYPE_ALL, "skins", SkinScan, &SkinScanUser); - if(m_vSkins.empty()) + if(m_Skins.empty()) { Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "gameclient", "failed to load skins. folder='skins/'"); - CSkin DummySkin; - str_copy(DummySkin.m_aName, "dummy"); + CSkin DummySkin{"dummy"}; DummySkin.m_BloodColor = ColorRGBA(1.0f, 1.0f, 1.0f); - m_vSkins.push_back(DummySkin); + auto &&pDummySkin = std::make_unique(std::move(DummySkin)); + m_Skins.insert({pDummySkin->GetName(), std::move(pDummySkin)}); } } int CSkins::Num() { - return m_vSkins.size(); + return m_Skins.size(); } -const CSkin *CSkins::Get(int Index) +const CSkin *CSkins::Find(const char *pName) { - if(Index < 0) + const auto *pSkin = FindOrNullptr(pName); + if(pSkin == nullptr) { - Index = Find("default"); - - if(Index < 0) - Index = 0; + pSkin = FindOrNullptr("default"); + if(pSkin == nullptr) + return m_Skins.begin()->second.get(); + else + return pSkin; + } + else + { + return pSkin; } - return &m_vSkins[Index % m_vSkins.size()]; } -int CSkins::Find(const char *pName) +const CSkin *CSkins::FindOrNullptr(const char *pName) { const char *pSkinPrefix = m_aEventSkinPrefix[0] ? m_aEventSkinPrefix : g_Config.m_ClSkinPrefix; if(g_Config.m_ClVanillaSkinsOnly && !IsVanillaSkin(pName)) { - return -1; + return nullptr; } else if(pSkinPrefix && pSkinPrefix[0]) { char aBuf[24]; str_format(aBuf, sizeof(aBuf), "%s_%s", pSkinPrefix, pName); // If we find something, use it, otherwise fall back to normal skins. - int Result = FindImpl(aBuf); - if(Result != -1) + const auto *pResult = FindImpl(aBuf); + if(pResult != nullptr) { - return Result; + return pResult; } } return FindImpl(pName); } -int CSkins::FindImpl(const char *pName) +const CSkin *CSkins::FindImpl(const char *pName) { - CSkin Needle; - mem_zero(&Needle, sizeof(Needle)); - str_copy(Needle.m_aName, pName); - auto Range = std::equal_range(m_vSkins.begin(), m_vSkins.end(), Needle); - if(std::distance(Range.first, Range.second) == 1) - return Range.first - m_vSkins.begin(); + auto SkinIt = m_Skins.find(pName); + if(SkinIt != m_Skins.end()) + return SkinIt->second.get(); if(str_comp(pName, "default") == 0) - return -1; + return nullptr; if(!g_Config.m_ClDownloadSkins) - return -1; + return nullptr; if(str_find(pName, "/") != 0) - return -1; + return nullptr; - CDownloadSkin DownloadNeedle; - mem_zero(&DownloadNeedle, sizeof(DownloadNeedle)); - str_copy(DownloadNeedle.m_aName, pName); - const auto &[RangeBegin, RangeEnd] = std::equal_range(m_vDownloadSkins.begin(), m_vDownloadSkins.end(), DownloadNeedle); - if(std::distance(RangeBegin, RangeEnd) == 1) + const auto SkinDownloadIt = m_DownloadSkins.find(pName); + if(SkinDownloadIt != m_DownloadSkins.end()) { - if(RangeBegin->m_pTask && RangeBegin->m_pTask->State() == HTTP_DONE) + if(SkinDownloadIt->second->m_pTask && SkinDownloadIt->second->m_pTask->State() == HTTP_DONE) { char aPath[IO_MAX_PATH_LENGTH]; - str_format(aPath, sizeof(aPath), "downloadedskins/%s.png", RangeBegin->m_aName); - Storage()->RenameFile(RangeBegin->m_aPath, aPath, IStorage::TYPE_SAVE); - LoadSkin(RangeBegin->m_aName, RangeBegin->m_pTask->m_Info); - RangeBegin->m_pTask = nullptr; + str_format(aPath, sizeof(aPath), "downloadedskins/%s.png", SkinDownloadIt->second->GetName()); + Storage()->RenameFile(SkinDownloadIt->second->m_aPath, aPath, IStorage::TYPE_SAVE); + const auto *pSkin = LoadSkin(SkinDownloadIt->second->GetName(), SkinDownloadIt->second->m_pTask->m_Info); + SkinDownloadIt->second->m_pTask = nullptr; --m_DownloadingSkins; + return pSkin; } - if(RangeBegin->m_pTask && (RangeBegin->m_pTask->State() == HTTP_ERROR || RangeBegin->m_pTask->State() == HTTP_ABORTED)) + if(SkinDownloadIt->second->m_pTask && (SkinDownloadIt->second->m_pTask->State() == HTTP_ERROR || SkinDownloadIt->second->m_pTask->State() == HTTP_ABORTED)) { - RangeBegin->m_pTask = nullptr; + SkinDownloadIt->second->m_pTask = nullptr; --m_DownloadingSkins; } - return -1; + return nullptr; } - CDownloadSkin Skin; - str_copy(Skin.m_aName, pName); + CDownloadSkin Skin{pName}; char aUrl[IO_MAX_PATH_LENGTH]; char aEscapedName[256]; @@ -454,7 +450,8 @@ int CSkins::FindImpl(const char *pName) str_format(Skin.m_aPath, sizeof(Skin.m_aPath), "downloadedskins/%s", IStorage::FormatTmpPath(aBuf, sizeof(aBuf), pName)); Skin.m_pTask = std::make_shared(this, aUrl, Storage(), Skin.m_aPath); m_pClient->Engine()->AddJob(Skin.m_pTask); - m_vDownloadSkins.insert(std::lower_bound(m_vDownloadSkins.begin(), m_vDownloadSkins.end(), Skin), std::move(Skin)); + auto &&pDownloadSkin = std::make_unique(std::move(Skin)); + m_DownloadSkins.insert({pDownloadSkin->GetName(), std::move(pDownloadSkin)}); ++m_DownloadingSkins; - return -1; + return nullptr; } diff --git a/src/game/client/components/skins.h b/src/game/client/components/skins.h index d089ecab6..36fede652 100644 --- a/src/game/client/components/skins.h +++ b/src/game/client/components/skins.h @@ -3,14 +3,18 @@ #ifndef GAME_CLIENT_COMPONENTS_SKINS_H #define GAME_CLIENT_COMPONENTS_SKINS_H +#include #include #include #include -#include +#include +#include class CSkins : public CComponent { public: + CSkins() = default; + class CGetPngFile : public CHttpRequest { CSkins *m_pSkins; @@ -25,12 +29,18 @@ public: struct CDownloadSkin { - std::shared_ptr m_pTask; - char m_aPath[IO_MAX_PATH_LENGTH]; + private: char m_aName[24]; + public: + std::shared_ptr m_pTask; + char m_aPath[IO_MAX_PATH_LENGTH]; + CDownloadSkin(CDownloadSkin &&Other) = default; - CDownloadSkin() = default; + CDownloadSkin(const char *pName) + { + str_copy(m_aName, pName); + } ~CDownloadSkin() { @@ -42,6 +52,8 @@ public: bool operator==(const char *pOther) const { return !str_comp(m_aName, pOther); } CDownloadSkin &operator=(CDownloadSkin &&Other) = default; + + const char *GetName() const { return m_aName; } }; typedef std::function TSkinLoadedCBFunc; @@ -51,21 +63,22 @@ public: void Refresh(TSkinLoadedCBFunc &&SkinLoadedFunc); int Num(); - const CSkin *Get(int Index); - int Find(const char *pName); + std::unordered_map> &GetSkinsUnsafe() { return m_Skins; } + const CSkin *FindOrNullptr(const char *pName); + const CSkin *Find(const char *pName); bool IsDownloadingSkins() { return m_DownloadingSkins; } private: - std::vector m_vSkins; - std::vector m_vDownloadSkins; + std::unordered_map> m_Skins; + std::unordered_map> m_DownloadSkins; size_t m_DownloadingSkins = 0; char m_aEventSkinPrefix[24]; bool LoadSkinPNG(CImageInfo &Info, const char *pName, const char *pPath, int DirType); - int LoadSkin(const char *pName, const char *pPath, int DirType); - int LoadSkin(const char *pName, CImageInfo &Info); - int FindImpl(const char *pName); + const CSkin *LoadSkin(const char *pName, const char *pPath, int DirType); + const CSkin *LoadSkin(const char *pName, CImageInfo &Info); + const CSkin *FindImpl(const char *pName); static int SkinScan(const char *pName, int IsDir, int DirType, void *pUser); }; #endif diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 9e4de7a66..dc25648d1 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -1227,7 +1227,7 @@ void CGameClient::OnNewSnapshot() pClient->m_SkinInfo.m_Size = 64; // find new skin - const CSkin *pSkin = m_Skins.Get(m_Skins.Find(pClient->m_aSkinName)); + const CSkin *pSkin = m_Skins.Find(pClient->m_aSkinName); pClient->m_SkinInfo.m_OriginalRenderSkin = pSkin->m_OriginalSkin; pClient->m_SkinInfo.m_ColorableRenderSkin = pSkin->m_ColorableSkin; pClient->m_SkinInfo.m_SkinMetrics = pSkin->m_Metrics; @@ -3185,7 +3185,7 @@ void CGameClient::RefindSkins() Client.m_SkinInfo.m_ColorableRenderSkin.Reset(); if(Client.m_aSkinName[0] != '\0') { - const CSkin *pSkin = m_Skins.Get(m_Skins.Find(Client.m_aSkinName)); + const CSkin *pSkin = m_Skins.Find(Client.m_aSkinName); Client.m_SkinInfo.m_OriginalRenderSkin = pSkin->m_OriginalSkin; Client.m_SkinInfo.m_ColorableRenderSkin = pSkin->m_ColorableSkin; Client.UpdateRenderInfo(IsTeamPlay()); diff --git a/src/game/client/skin.h b/src/game/client/skin.h index e40341829..655166011 100644 --- a/src/game/client/skin.h +++ b/src/game/client/skin.h @@ -1,6 +1,7 @@ #ifndef GAME_CLIENT_SKIN_H #define GAME_CLIENT_SKIN_H #include +#include #include #include #include @@ -8,6 +9,10 @@ // do this better and nicer struct CSkin { +private: + char m_aName[24]; + +public: struct SSkinTextures { IGraphics::CTextureHandle m_Body; @@ -36,7 +41,6 @@ struct CSkin SSkinTextures m_OriginalSkin; SSkinTextures m_ColorableSkin; - char m_aName[24]; ColorRGBA m_BloodColor; template @@ -129,6 +133,15 @@ struct CSkin bool operator<(const CSkin &Other) const { return str_comp(m_aName, Other.m_aName) < 0; } bool operator==(const CSkin &Other) const { return !str_comp(m_aName, Other.m_aName); } + + CSkin(const char *pName) + { + str_copy(m_aName, pName); + } + CSkin(CSkin &&) = default; + CSkin &operator=(CSkin &&) = default; + + const char *GetName() const { return m_aName; } }; #endif