Refactor skin refreshing in gameclient, fix crash in skin settings

Add `CGameClient::RefreshSkins` function to refresh skins. This function reloads all skins by calling `CSkins::Refresh` and then notifies all gameclient components about the skins being refreshed by calling the new `CComponent::OnRefreshSkins` function, so the components can properly invalidate their current skin texture handles. The existing `RefindSkins` functions are changed to `OnRefreshSkins`.

Additionally, `OnRefreshSkins` is overridden in `CMenus` to set the flag so the skin list will be updated before it is rendered the next time, to fix the client crashing when changing skin related config variables via the console. Closes #7891.
This commit is contained in:
Robert Müller 2024-02-01 20:13:51 +01:00
parent 2ed899a259
commit c40d809282
12 changed files with 54 additions and 60 deletions

View file

@ -182,6 +182,10 @@ public:
* Called when the window has been resized.
*/
virtual void OnWindowResize() {}
/**
* Called when skins have been invalidated and must be updated.
*/
virtual void OnRefreshSkins() {}
/**
* Called when the component should get rendered.
*

View file

@ -923,7 +923,7 @@ void CChat::AddLine(int ClientID, int Team, const char *pLine)
}
}
void CChat::RefindSkins()
void CChat::OnRefreshSkins()
{
for(auto &Line : m_aLines)
{

View file

@ -165,8 +165,8 @@ public:
void OnWindowResize() override;
void OnConsoleInit() override;
void OnStateChange(int NewState, int OldState) override;
void OnRefreshSkins() override;
void OnRender() override;
void RefindSkins();
void OnPrepareLines(float y);
void Reset();
void OnRelease() override;

View file

@ -684,7 +684,7 @@ int CGhost::GetLastRaceTick() const
return m_LastRaceTick;
}
void CGhost::RefindSkins()
void CGhost::OnRefreshSkins()
{
const auto &&RefindSkin = [&](auto &Ghost) {
if(Ghost.Empty())

View file

@ -155,6 +155,7 @@ public:
virtual void OnRender() override;
virtual void OnConsoleInit() override;
virtual void OnReset() override;
virtual void OnRefreshSkins() override;
virtual void OnMessage(int MsgType, void *pRawMsg) override;
virtual void OnMapLoad() override;
virtual void OnShutdown() override;
@ -175,8 +176,6 @@ public:
class IGhostRecorder *GhostRecorder() const { return m_pGhostRecorder; }
int GetLastRaceTick() const;
void RefindSkins();
};
#endif

View file

@ -536,7 +536,7 @@ void CInfoMessages::OnRender()
}
}
void CInfoMessages::RefindSkins()
void CInfoMessages::OnRefreshSkins()
{
for(auto &InfoMsg : m_aInfoMsgs)
{

View file

@ -72,12 +72,11 @@ public:
virtual int Sizeof() const override { return sizeof(*this); }
virtual void OnWindowResize() override;
virtual void OnRefreshSkins() override;
virtual void OnReset() override;
virtual void OnRender() override;
virtual void OnMessage(int MsgType, void *pRawMsg) override;
virtual void OnInit() override;
void RefindSkins();
};
#endif

View file

@ -814,12 +814,6 @@ void CMenus::OnInit()
Console()->Chain("cl_asset_hud", ConchainAssetHud, this);
Console()->Chain("cl_asset_extras", ConchainAssetExtras, this);
Console()->Chain("cl_skin_download_url", ConchainReloadSkins, this);
Console()->Chain("cl_skin_community_download_url", ConchainReloadSkins, this);
Console()->Chain("cl_download_skins", ConchainReloadSkins, this);
Console()->Chain("cl_download_community_skins", ConchainReloadSkins, this);
Console()->Chain("cl_vanilla_skins_only", ConchainReloadSkins, this);
m_TextureBlob = Graphics()->LoadTexture("blob.png", IStorage::TYPE_ALL);
// setup load amount

View file

@ -81,8 +81,7 @@ class CMenus : public CComponent
void DoJoystickAxisPicker(CUIRect View);
void DoJoystickBar(const CUIRect *pRect, float Current, float Tolerance, bool Active);
void RefreshSkins();
bool m_SkinListNeedsUpdate = false;
void RandomSkin();
// menus_settings_assets.cpp
@ -595,8 +594,6 @@ protected:
class CMenuBackground *m_pBackground;
static void ConchainReloadSkins(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
public:
void RenderBackground();
@ -619,6 +616,7 @@ public:
virtual void OnStateChange(int NewState, int OldState) override;
virtual void OnWindowResize() override;
virtual void OnRefreshSkins() override;
virtual void OnReset() override;
virtual void OnRender() override;
virtual bool OnInput(const IInput::CEvent &Event) override;

View file

@ -389,20 +389,9 @@ struct CUISkin
bool operator==(const char *pOther) const { return !str_comp_nocase(m_pSkin->GetName(), pOther); }
};
void CMenus::RefreshSkins()
void CMenus::OnRefreshSkins()
{
auto SkinStartLoadTime = time_get_nanoseconds();
m_pClient->m_Skins.Refresh([&](int) {
// if skin refreshing takes to long, swap to a loading screen
if(time_get_nanoseconds() - SkinStartLoadTime > 500ms)
{
RenderLoading(Localize("Loading skin files"), "", 0, false);
}
});
if(Client()->State() == IClient::STATE_ONLINE || Client()->State() == IClient::STATE_DEMOPLAYBACK)
{
m_pClient->RefindSkins();
}
m_SkinListNeedsUpdate = true;
}
void CMenus::RandomSkin()
@ -560,29 +549,25 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
Checkboxes.VSplitRight(20.0f, &Checkboxes, nullptr);
// Checkboxes
static bool s_InitSkinlist = true;
Checkboxes.HSplitTop(20.0f, &Button, &Checkboxes);
if(DoButton_CheckBox(&g_Config.m_ClDownloadSkins, Localize("Download skins"), g_Config.m_ClDownloadSkins, &Button))
{
g_Config.m_ClDownloadSkins ^= 1;
RefreshSkins();
s_InitSkinlist = true;
m_pClient->RefreshSkins();
}
Checkboxes.HSplitTop(20.0f, &Button, &Checkboxes);
if(DoButton_CheckBox(&g_Config.m_ClDownloadCommunitySkins, Localize("Download community skins"), g_Config.m_ClDownloadCommunitySkins, &Button))
{
g_Config.m_ClDownloadCommunitySkins ^= 1;
RefreshSkins();
s_InitSkinlist = true;
m_pClient->RefreshSkins();
}
Checkboxes.HSplitTop(20.0f, &Button, &Checkboxes);
if(DoButton_CheckBox(&g_Config.m_ClVanillaSkinsOnly, Localize("Vanilla skins only"), g_Config.m_ClVanillaSkinsOnly, &Button))
{
g_Config.m_ClVanillaSkinsOnly ^= 1;
RefreshSkins();
s_InitSkinlist = true;
m_pClient->RefreshSkins();
}
Checkboxes.HSplitTop(20.0f, &Button, &Checkboxes);
@ -768,7 +753,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
// be nice to the CPU
static auto s_SkinLastRebuildTime = time_get_nanoseconds();
const auto CurTime = time_get_nanoseconds();
if(s_InitSkinlist || m_pClient->m_Skins.Num() != s_SkinCount || m_SkinFavoritesChanged || (m_pClient->m_Skins.IsDownloadingSkins() && (CurTime - s_SkinLastRebuildTime > 500ms)))
if(m_SkinListNeedsUpdate || m_pClient->m_Skins.Num() != s_SkinCount || m_SkinFavoritesChanged || (m_pClient->m_Skins.IsDownloadingSkins() && (CurTime - s_SkinLastRebuildTime > 500ms)))
{
s_SkinLastRebuildTime = CurTime;
s_vSkinList.clear();
@ -813,7 +798,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
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 = false;
m_SkinListNeedsUpdate = false;
}
const auto &&RenderFavIcon = [&](const CUIRect &FavIcon, bool AsFav, bool Hot) {
@ -890,7 +875,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
{
m_SkinFavorites.emplace(pSkinToBeDraw->GetName());
}
s_InitSkinlist = true;
m_SkinListNeedsUpdate = true;
}
}
}
@ -919,7 +904,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
}
s_SkinFilterInput.SetEmptyText(Localize("Search"));
if(UI()->DoClearableEditBox(&s_SkinFilterInput, &QuickSearch, 14.0f))
s_InitSkinlist = true;
m_SkinListNeedsUpdate = true;
}
static CButtonContainer s_SkinDatabaseButton;
@ -952,8 +937,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
// reset render flags for possible loading screen
TextRender()->SetRenderFlags(0);
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
RefreshSkins();
s_InitSkinlist = true;
m_pClient->RefreshSkins();
}
TextRender()->SetRenderFlags(0);
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
@ -3453,13 +3437,3 @@ void CMenus::RenderSettingsDDNet(CUIRect MainView)
}
#endif
}
void CMenus::ConchainReloadSkins(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
CMenus *pThis = (CMenus *)pUserData;
pfnCallback(pResult, pCallbackUserData);
if(pResult->NumArguments() && (pThis->Client()->State() == IClient::STATE_ONLINE || pThis->Client()->State() == IClient::STATE_DEMOPLAYBACK))
{
pThis->RefreshSkins();
}
}

View file

@ -212,6 +212,12 @@ void CGameClient::OnConsoleInit()
Console()->Chain("dummy_color_feet", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("dummy_skin", ConchainSpecialDummyInfoupdate, this);
Console()->Chain("cl_skin_download_url", ConchainRefreshSkins, this);
Console()->Chain("cl_skin_community_download_url", ConchainRefreshSkins, this);
Console()->Chain("cl_download_skins", ConchainRefreshSkins, this);
Console()->Chain("cl_download_community_skins", ConchainRefreshSkins, this);
Console()->Chain("cl_vanilla_skins_only", ConchainRefreshSkins, this);
Console()->Chain("cl_dummy", ConchainSpecialDummy, this);
Console()->Chain("cl_text_entities_size", ConchainClTextEntitiesSize, this);
@ -3371,8 +3377,17 @@ void CGameClient::LoadExtrasSkin(const char *pPath, bool AsDir)
}
}
void CGameClient::RefindSkins()
void CGameClient::RefreshSkins()
{
const auto SkinStartLoadTime = time_get_nanoseconds();
m_Skins.Refresh([&](int) {
// if skin refreshing takes to long, swap to a loading screen
if(time_get_nanoseconds() - SkinStartLoadTime > 500ms)
{
m_Menus.RenderLoading(Localize("Loading skin files"), "", 0, false);
}
});
for(auto &Client : m_aClients)
{
Client.m_SkinInfo.m_OriginalRenderSkin.Reset();
@ -3390,9 +3405,19 @@ void CGameClient::RefindSkins()
}
Client.UpdateRenderInfo(IsTeamPlay());
}
m_Ghost.RefindSkins();
m_Chat.RefindSkins();
m_InfoMessages.RefindSkins();
for(auto &pComponent : m_vpAll)
pComponent->OnRefreshSkins();
}
void CGameClient::ConchainRefreshSkins(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
CGameClient *pThis = static_cast<CGameClient *>(pUserData);
pfnCallback(pResult, pCallbackUserData);
if(pResult->NumArguments() && pThis->m_Menus.IsInit())
{
pThis->RefreshSkins();
}
}
static bool UnknownMapSettingCallback(const char *pCommand, void *pUser)

View file

@ -207,6 +207,7 @@ private:
static void ConchainLanguageUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainSpecialDummyInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainRefreshSkins(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainSpecialDummy(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainClTextEntitiesSize(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
@ -499,6 +500,8 @@ public:
void OnLanguageChange();
void HandleLanguageChanged();
void RefreshSkins();
void RenderShutdownMessage();
const char *GetItemName(int Type) const override;
@ -561,8 +564,6 @@ public:
void LoadHudSkin(const char *pPath, bool AsDir = false);
void LoadExtrasSkin(const char *pPath, bool AsDir = false);
void RefindSkins();
struct SClientGameSkin
{
// health armor hud