5933: Inline `Is(GameType)` functions and remove support for legacy 64 player info protocol r=def- a=heinrich5991

## Checklist

- [ ] Tested the change ingame
- [ ] Provided screenshots if it is a visual change
- [ ] Tested in combination with possibly related configuration options
- [ ] Written a unit test (especially base/) or added coverage to integration test
- [ ] Considered possible null pointers and out of bounds array indexing
- [ ] Changed no physics that affect existing maps
- [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional)


5941: Get away from vector for skins r=def- a=Jupeyy

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

not 100% tested. also fixes a bug with favorite skins hopefully
## Checklist

- [x] Tested the change ingame
- [ ] Provided screenshots if it is a visual change
- [ ] Tested in combination with possibly related configuration options
- [ ] Written a unit test (especially base/) or added coverage to integration test
- [ ] Considered possible null pointers and out of bounds array indexing
- [ ] Changed no physics that affect existing maps
- [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional)


Co-authored-by: heinrich5991 <heinrich5991@gmail.com>
Co-authored-by: Jupeyy <jupjopjap@gmail.com>
This commit is contained in:
bors[bot] 2022-10-12 22:08:17 +00:00 committed by GitHub
commit 6c3d0e999b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 176 additions and 323 deletions

View file

@ -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);
@ -1572,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 IsDDNet(&m_CurrentServerInfo);
}
static CServerCapabilities GetServerCapabilities(int Version, int Flags)
{
CServerCapabilities Result;
@ -2167,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;

View file

@ -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

View file

@ -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<int>((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,9 +1078,6 @@ 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);
}
@ -1563,71 +1515,3 @@ 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, "+");
}

View file

@ -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; }

View file

@ -85,20 +85,6 @@ 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
{
MACRO_INTERFACE("serverbrowser", 0)

View file

@ -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

View file

@ -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;

View file

@ -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<ColorRGBA>(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;

View file

@ -18,6 +18,7 @@
#include <game/client/components/sounds.h>
#include <game/client/gameclient.h>
#include <game/client/render.h>
#include <game/client/skin.h>
#include <game/client/ui.h>
#include <game/client/ui_scrollregion.h>
#include <game/localization.h>
@ -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

View file

@ -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;

View file

@ -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)
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<CSkin>(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<CSkin>(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<CGetPngFile>(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<CDownloadSkin>(std::move(Skin));
m_DownloadSkins.insert({pDownloadSkin->GetName(), std::move(pDownloadSkin)});
++m_DownloadingSkins;
return -1;
return nullptr;
}

View file

@ -3,14 +3,18 @@
#ifndef GAME_CLIENT_COMPONENTS_SKINS_H
#define GAME_CLIENT_COMPONENTS_SKINS_H
#include <base/system.h>
#include <engine/shared/http.h>
#include <game/client/component.h>
#include <game/client/skin.h>
#include <vector>
#include <string_view>
#include <unordered_map>
class CSkins : public CComponent
{
public:
CSkins() = default;
class CGetPngFile : public CHttpRequest
{
CSkins *m_pSkins;
@ -25,12 +29,18 @@ public:
struct CDownloadSkin
{
std::shared_ptr<CSkins::CGetPngFile> m_pTask;
char m_aPath[IO_MAX_PATH_LENGTH];
private:
char m_aName[24];
public:
std::shared_ptr<CSkins::CGetPngFile> 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<void(int)> 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<std::string_view, std::unique_ptr<CSkin>> &GetSkinsUnsafe() { return m_Skins; }
const CSkin *FindOrNullptr(const char *pName);
const CSkin *Find(const char *pName);
bool IsDownloadingSkins() { return m_DownloadingSkins; }
private:
std::vector<CSkin> m_vSkins;
std::vector<CDownloadSkin> m_vDownloadSkins;
std::unordered_map<std::string_view, std::unique_ptr<CSkin>> m_Skins;
std::unordered_map<std::string_view, std::unique_ptr<CDownloadSkin>> 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

View file

@ -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
@ -1227,7 +1228,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 +3186,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());

View file

@ -1,6 +1,7 @@
#ifndef GAME_CLIENT_SKIN_H
#define GAME_CLIENT_SKIN_H
#include <base/color.h>
#include <base/system.h>
#include <base/vmath.h>
#include <engine/graphics.h>
#include <limits>
@ -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<bool IsSizeType>
@ -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