From c40d809282ab652fa16d5ba97b10d92266ed0c1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Thu, 1 Feb 2024 20:13:51 +0100 Subject: [PATCH] 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. --- src/game/client/component.h | 4 ++ src/game/client/components/chat.cpp | 2 +- src/game/client/components/chat.h | 2 +- src/game/client/components/ghost.cpp | 2 +- src/game/client/components/ghost.h | 3 +- src/game/client/components/infomessages.cpp | 2 +- src/game/client/components/infomessages.h | 3 +- src/game/client/components/menus.cpp | 6 --- src/game/client/components/menus.h | 6 +-- src/game/client/components/menus_settings.cpp | 46 ++++--------------- src/game/client/gameclient.cpp | 33 +++++++++++-- src/game/client/gameclient.h | 5 +- 12 files changed, 54 insertions(+), 60 deletions(-) diff --git a/src/game/client/component.h b/src/game/client/component.h index edf2c6a70..93616b216 100644 --- a/src/game/client/component.h +++ b/src/game/client/component.h @@ -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. * diff --git a/src/game/client/components/chat.cpp b/src/game/client/components/chat.cpp index fb47ea979..a53bbe53b 100644 --- a/src/game/client/components/chat.cpp +++ b/src/game/client/components/chat.cpp @@ -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) { diff --git a/src/game/client/components/chat.h b/src/game/client/components/chat.h index cf23efc93..41b690b02 100644 --- a/src/game/client/components/chat.h +++ b/src/game/client/components/chat.h @@ -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; diff --git a/src/game/client/components/ghost.cpp b/src/game/client/components/ghost.cpp index 2a55688f1..87066d7fa 100644 --- a/src/game/client/components/ghost.cpp +++ b/src/game/client/components/ghost.cpp @@ -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()) diff --git a/src/game/client/components/ghost.h b/src/game/client/components/ghost.h index c7582523e..e1c263677 100644 --- a/src/game/client/components/ghost.h +++ b/src/game/client/components/ghost.h @@ -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 diff --git a/src/game/client/components/infomessages.cpp b/src/game/client/components/infomessages.cpp index 053325266..d5892c37e 100644 --- a/src/game/client/components/infomessages.cpp +++ b/src/game/client/components/infomessages.cpp @@ -536,7 +536,7 @@ void CInfoMessages::OnRender() } } -void CInfoMessages::RefindSkins() +void CInfoMessages::OnRefreshSkins() { for(auto &InfoMsg : m_aInfoMsgs) { diff --git a/src/game/client/components/infomessages.h b/src/game/client/components/infomessages.h index 680c46c65..0722e8b15 100644 --- a/src/game/client/components/infomessages.h +++ b/src/game/client/components/infomessages.h @@ -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 diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp index 90e44866a..39e3acaeb 100644 --- a/src/game/client/components/menus.cpp +++ b/src/game/client/components/menus.cpp @@ -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 diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index 541623113..2d4b7fa14 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -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; diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index 849f087cc..edc560dcd 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -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(); - } -} diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index dd81dc24f..8a9a59d30 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -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(pUserData); + pfnCallback(pResult, pCallbackUserData); + if(pResult->NumArguments() && pThis->m_Menus.IsInit()) + { + pThis->RefreshSkins(); + } } static bool UnknownMapSettingCallback(const char *pCommand, void *pUser) diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index ac1129edf..46109308e 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -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