Skip to content

Commit 7a0bf7e

Browse files
Synchronize changes from 1.6 branch [ci skip]
0fbaef3 Resolve CLocalization / CGUI memory leaks (#4527)
2 parents 1ab309d + 0fbaef3 commit 7a0bf7e

File tree

6 files changed

+117
-60
lines changed

6 files changed

+117
-60
lines changed

Client/core/CGUI.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ void CLocalGUI::ChangeLocale(const char* szName)
134134

135135
m_LastLocaleName = strCanonicalLocale;
136136

137+
// Attempt CEGUI cleanup
138+
if (CGUI* pGUI = CCore::GetSingleton().GetGUI())
139+
pGUI->Cleanup();
140+
137141
if (guiWasLoaded)
138142
{
139143
CreateWindows(guiWasLoaded);

Client/core/CLocalization.cpp

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,7 @@ CLocalization::CLocalization(const SString& strLocale, const SString& strLocaleP
4141

4242
CLocalization::~CLocalization()
4343
{
44-
for (auto iter : m_LanguageMap)
45-
{
46-
delete iter.second;
47-
}
44+
m_pCurrentLang = nullptr;
4845
}
4946

5047
//
@@ -99,29 +96,32 @@ void CLocalization::SetCurrentLanguage(SString strLocale)
9996
CLanguage* CLocalization::GetLanguage(SString strLocale)
10097
{
10198
strLocale = ValidateLocale(strLocale);
102-
CLanguage* pLanguage = MapFindRef(m_LanguageMap, strLocale);
103-
if (!pLanguage)
99+
auto iter = m_LanguageMap.find(strLocale);
100+
if (iter != m_LanguageMap.end())
101+
{
102+
return iter->second.get();
103+
}
104+
105+
Language Lang = Language::from_name(strLocale);
106+
Lang = Lang ? Lang : Language::from_name("en_US");
107+
108+
try
109+
{
110+
std::unique_ptr<CLanguage> pLanguage = std::make_unique<CLanguage>(m_DictManager.get_dictionary(Lang, MTA_LOCALE_TEXTDOMAIN), Lang.str(), Lang.get_name());
111+
CLanguage* pLanguagePtr = pLanguage.get();
112+
m_LanguageMap.emplace(strLocale, std::move(pLanguage));
113+
return pLanguagePtr;
114+
}
115+
catch (const std::exception& ex)
116+
{
117+
WriteDebugEvent(SString("Localization failed to load dictionary for '%s': %s", strLocale.c_str(), ex.what()));
118+
return (strLocale != "en_US") ? GetLanguage("en_US") : nullptr;
119+
}
120+
catch (...)
104121
{
105-
Language Lang = Language::from_name(strLocale);
106-
Lang = Lang ? Lang : Language::from_name("en_US");
107-
108-
try
109-
{
110-
pLanguage = new CLanguage(m_DictManager.get_dictionary(Lang, MTA_LOCALE_TEXTDOMAIN), Lang.str(), Lang.get_name());
111-
MapSet(m_LanguageMap, strLocale, pLanguage);
112-
}
113-
catch (const std::exception& ex)
114-
{
115-
WriteDebugEvent(SString("Localization failed to load dictionary for '%s': %s", strLocale.c_str(), ex.what()));
116-
return (strLocale != "en_US") ? GetLanguage("en_US") : nullptr;
117-
}
118-
catch (...)
119-
{
120-
WriteDebugEvent(SString("Localization failed to load dictionary for '%s': unknown error", strLocale.c_str()));
121-
return (strLocale != "en_US") ? GetLanguage("en_US") : nullptr;
122-
}
122+
WriteDebugEvent(SString("Localization failed to load dictionary for '%s': unknown error", strLocale.c_str()));
123+
return (strLocale != "en_US") ? GetLanguage("en_US") : nullptr;
123124
}
124-
return pLanguage;
125125
}
126126

127127
//

Client/core/CLocalization.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ using namespace tinygettext;
1313

1414
#include <core/CLocalizationInterface.h>
1515
#include "CLanguage.h"
16-
#define MTA_LOCALE_DIR "MTA/locale/"
16+
#include <memory>
17+
#define MTA_LOCALE_DIR "MTA/locale/"
1718

1819
#pragma once
1920

@@ -42,7 +43,7 @@ class CLocalization : public CLocalizationInterface
4243
static void LogCallback(const std::string& str);
4344

4445
private:
45-
DictionaryManager m_DictManager;
46-
std::map<SString, CLanguage*> m_LanguageMap;
47-
CLanguage* m_pCurrentLang{};
46+
DictionaryManager m_DictManager;
47+
std::map<SString, std::unique_ptr<CLanguage>> m_LanguageMap;
48+
CLanguage* m_pCurrentLang{};
4849
};

Client/gui/CGUI_Impl.cpp

Lines changed: 76 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,16 @@ CGUI_Impl::~CGUI_Impl()
144144
// DO NOT delete m_pRenderer - it's already deleted by System destructor
145145
}
146146

147+
void CGUI_Impl::CreateRootWindow()
148+
{
149+
if (!m_pWindowManager || !m_pSystem)
150+
return;
151+
152+
// Create dummy GUI root
153+
m_pTop = reinterpret_cast<CEGUI::DefaultWindow*>(m_pWindowManager->createWindow("DefaultWindow", "guiroot"));
154+
m_pSystem->setGUISheet(m_pTop);
155+
}
156+
147157
void CGUI_Impl::SetSkin(const char* szName)
148158
{
149159
if (m_HasSchemeLoaded)
@@ -162,12 +172,8 @@ void CGUI_Impl::SetSkin(const char* szName)
162172

163173
CEGUI::System::getSingleton().setDefaultMouseCursor("CGUI-Images", "MouseArrow");
164174

165-
// Destroy any windows we already have
166-
CEGUI::WindowManager::getSingleton().destroyAllWindows();
167-
168-
// Create dummy GUI root
169-
m_pTop = reinterpret_cast<CEGUI::DefaultWindow*>(m_pWindowManager->createWindow("DefaultWindow", "guiroot"));
170-
m_pSystem->setGUISheet(m_pTop);
175+
// Clean up CEGUI - this also re-creates the root window
176+
Cleanup();
171177

172178
// Disable single click timeouts
173179
m_pSystem->setSingleClickTimeout(100000000.0f);
@@ -327,28 +333,31 @@ bool CGUI_Impl::GetGUIInputEnabled()
327333
break;
328334
case INPUTMODE_NO_BINDS_ON_EDIT:
329335
{
330-
CEGUI::Window* pActiveWindow = m_pTop->getActiveChild();
331-
if (!pActiveWindow || pActiveWindow == m_pTop || !pActiveWindow->isVisible())
332-
{
333-
return false;
334-
}
335-
if (pActiveWindow->getType() == "CGUI/Editbox")
336-
{
337-
CEGUI::Editbox* pEditBox = reinterpret_cast<CEGUI::Editbox*>(pActiveWindow);
338-
return (!pEditBox->isReadOnly() && pEditBox->hasInputFocus());
339-
}
340-
else if (pActiveWindow->getType() == "CGUI/MultiLineEditbox")
341-
{
342-
CEGUI::MultiLineEditbox* pMultiLineEditBox = reinterpret_cast<CEGUI::MultiLineEditbox*>(pActiveWindow);
343-
return (!pMultiLineEditBox->isReadOnly() && pMultiLineEditBox->hasInputFocus());
344-
}
345-
else if (pActiveWindow->getType() == CGUIWEBBROWSER_NAME)
336+
if (m_pTop)
346337
{
347-
auto pElement = reinterpret_cast<CGUIElement_Impl*>(pActiveWindow->getUserData());
348-
if (pElement->GetType() == CGUI_WEBBROWSER)
338+
CEGUI::Window* pActiveWindow = m_pTop->getActiveChild();
339+
if (!pActiveWindow || pActiveWindow == m_pTop || !pActiveWindow->isVisible())
340+
{
341+
return false;
342+
}
343+
if (pActiveWindow->getType() == "CGUI/Editbox")
344+
{
345+
CEGUI::Editbox* pEditBox = reinterpret_cast<CEGUI::Editbox*>(pActiveWindow);
346+
return (!pEditBox->isReadOnly() && pEditBox->hasInputFocus());
347+
}
348+
else if (pActiveWindow->getType() == "CGUI/MultiLineEditbox")
349+
{
350+
CEGUI::MultiLineEditbox* pMultiLineEditBox = reinterpret_cast<CEGUI::MultiLineEditbox*>(pActiveWindow);
351+
return (!pMultiLineEditBox->isReadOnly() && pMultiLineEditBox->hasInputFocus());
352+
}
353+
else if (pActiveWindow->getType() == CGUIWEBBROWSER_NAME)
349354
{
350-
auto pWebBrowser = reinterpret_cast<CGUIWebBrowser_Impl*>(pElement);
351-
return pWebBrowser->HasInputFocus();
355+
auto pElement = reinterpret_cast<CGUIElement_Impl*>(pActiveWindow->getUserData());
356+
if (pElement->GetType() == CGUI_WEBBROWSER)
357+
{
358+
auto pWebBrowser = reinterpret_cast<CGUIWebBrowser_Impl*>(pElement);
359+
return pWebBrowser->HasInputFocus();
360+
}
352361
}
353362
}
354363
return false;
@@ -580,6 +589,9 @@ eCursorType CGUI_Impl::GetCursorType()
580589

581590
void CGUI_Impl::AddChild(CGUIElement_Impl* pChild)
582591
{
592+
if (!m_pTop)
593+
return;
594+
583595
m_pTop->addChildWindow(pChild->GetWindow());
584596
}
585597

@@ -1153,12 +1165,15 @@ bool CGUI_Impl::Event_MouseButtonDown(const CEGUI::EventArgs& Args)
11531165
pElement->Event_OnMouseButtonDown();
11541166
else
11551167
{
1156-
// If there's no element, we're probably dealing with the root element
1157-
CEGUI::Window* pActiveWindow = m_pTop->getActiveChild();
1158-
if (m_pTop == wnd && pActiveWindow)
1168+
if (m_pTop)
11591169
{
1160-
// Deactivate active window to trigger onClientGUIBlur
1161-
pActiveWindow->deactivate();
1170+
// If there's no element, we're probably dealing with the root element
1171+
CEGUI::Window* pActiveWindow = m_pTop->getActiveChild();
1172+
if (m_pTop == wnd && pActiveWindow)
1173+
{
1174+
// Deactivate active window to trigger onClientGUIBlur
1175+
pActiveWindow->deactivate();
1176+
}
11621177
}
11631178
}
11641179

@@ -1797,3 +1812,33 @@ CEGUI::Window* CGUI_Impl::GetMasterWindow(CEGUI::Window* wnd)
17971812
}
17981813
return wnd;
17991814
}
1815+
1816+
void CGUI_Impl::Cleanup()
1817+
{
1818+
try
1819+
{
1820+
CleanDeadPool();
1821+
1822+
m_pTop = nullptr;
1823+
1824+
if (m_pWindowManager)
1825+
m_pWindowManager->destroyAllWindows();
1826+
1827+
// Clear redraw structures that may reference old elements
1828+
m_RedrawQueue.clear();
1829+
m_RedrawRegistry.clear();
1830+
1831+
// Recreate the root window (destroyed above via destroyAllWindows)
1832+
CreateRootWindow();
1833+
}
1834+
catch (const std::exception& e)
1835+
{
1836+
WriteDebugEvent(SString("CGUI_Impl::Cleanup - Exception: %s", e.what()));
1837+
m_pTop = nullptr;
1838+
}
1839+
catch (...)
1840+
{
1841+
WriteDebugEvent("CGUI_Impl::Cleanup() failed with unknown exception");
1842+
m_pTop = nullptr;
1843+
}
1844+
}

Client/gui/CGUI_Impl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,9 @@ class CGUI_Impl : public CGUI, public CGUITabList
283283
CGUIWindow* LoadLayout(CGUIElement* pParent, const SString& strFilename);
284284
bool LoadImageset(const SString& strFilename);
285285

286+
// Cleanup CEGUI active resources (dead pool)
287+
void Cleanup();
288+
286289
private:
287290
friend class CGUIElement_Impl;
288291
CGUIButton* _CreateButton(CGUIElement_Impl* pParent = NULL, const char* szCaption = "");
@@ -361,4 +364,6 @@ class CGUI_Impl : public CGUI, public CGUITabList
361364
bool m_HasSchemeLoaded;
362365
SString m_CurrentSchemeName;
363366
CElapsedTime m_RenderOkTimer;
367+
368+
void CreateRootWindow();
364369
};

Client/sdk/gui/CGUI.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,4 +170,6 @@ class CGUI
170170

171171
virtual CGUIWindow* LoadLayout(CGUIElement* pParent, const SString& strFilename) = 0;
172172
virtual bool LoadImageset(const SString& strFilename) = 0;
173+
174+
virtual void Cleanup() = 0;
173175
};

0 commit comments

Comments
 (0)