Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
09e50f0
Initial commit
Xenius97 Nov 23, 2025
08c5610
Fixes
Xenius97 Nov 23, 2025
c329656
Fix process terminate
Xenius97 Nov 23, 2025
25e5f75
Prevent auto updater from updating while secondary client running
Xenius97 Nov 23, 2025
05a0a66
Merge branch 'master' into new/secondary_client
Xenius97 Nov 29, 2025
9c6c2ce
Merge branch 'master' into new/secondary_client
qaisjp Jan 24, 2026
2eba8f2
Implement secondary client checks in process management functions
Xenius97 Jan 24, 2026
3bbad1b
fix
Xenius97 Jan 24, 2026
6a504ce
Refactor server ID management for secondary client compatibility
Xenius97 Jan 24, 2026
d0e5296
clang-format
Xenius97 Jan 24, 2026
e796684
change GUID to 2 because in case we ever use this, the 2 is helpful
qaisjp Jan 25, 2026
08ee124
Disable some sections of code unless MTA_CL2 is enabled
qaisjp Jan 25, 2026
3bd960c
Remove unnecessary variable
qaisjp Jan 25, 2026
156f12b
Disable core hooks unless MTA_CL2 is enabled
qaisjp Jan 25, 2026
4001a98
Tweak comment
qaisjp Jan 25, 2026
c2ab4c2
remove short-circuit
qaisjp Jan 25, 2026
da7b559
remark
qaisjp Jan 25, 2026
67a0395
use g_pCore, add todo
qaisjp Jan 25, 2026
26aaf08
Merge branch 'master' into new/secondary_client
Xenius97 Jan 27, 2026
1569aab
Initial commit
FileEX Feb 1, 2026
d09d873
Duplicated comment
FileEX Feb 1, 2026
f09e0e6
Merge branch 'master' into bugfix/bulletsync
FileEX Feb 1, 2026
3635d1e
Fix condition logic
FileEX Feb 1, 2026
2e79413
Merge branch 'bugfix/bulletsync' into test-bulletsync-secondary
FileEX Feb 1, 2026
300e82c
Update CInstallManager.cpp
FileEX Feb 1, 2026
0b4f43b
Init damage value
FileEX Feb 1, 2026
c548c49
Fix texture mixing between engineRequestModel clones
Dutchman101 Jan 27, 2026
54416b4
Addendum to 188ce44:
Dutchman101 Jan 28, 2026
f84ddd9
Visual Studio Update
MTABot Jan 28, 2026
0410ddc
Optimize model info lookup in texture reapply path.
Dutchman101 Jan 28, 2026
e36304e
Add TXD texture map caching with invalidation (Performance optimizati…
Dutchman101 Jan 28, 2026
ae96b60
Fix server build on RHEL/Fedora with `mysql-devel` package (PR #4672)
AlexTMjugador Jan 29, 2026
0bb121c
New Crowdin updates (PR #4665)
MTABot Jan 30, 2026
54935f0
Visual Studio Update
MTABot Jan 30, 2026
3196009
Visual Studio Update
MTABot Feb 1, 2026
a3bd8ef
Update CInstallManager.cpp
FileEX Feb 1, 2026
6c29f01
Merge branch 'new/secondary_client' of https://github.com/Xenius97/mt…
Xenius97 Feb 1, 2026
cc4a1b7
Revert "Init damage value"
Xenius97 Feb 1, 2026
525e0d5
Revert "Fix condition logic"
Xenius97 Feb 1, 2026
6d0c894
Revert "Duplicated comment"
Xenius97 Feb 1, 2026
3587d06
Revert "Initial commit"
Xenius97 Feb 1, 2026
946521b
Refactor version check logic in CInstallManager::InitSequencer for se…
Xenius97 Feb 1, 2026
4814aeb
Fix mutex handle identifier for CL2 update checks
Xenius97 Feb 1, 2026
a4dc83c
Update mutex handle identifier for CL2 to use MTA_GUID_CL2
Xenius97 Feb 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions Client/core/CConsole.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,13 @@ CConsole::CConsole(CGUI* pManager, CGUIElement* pParent)

m_pHistory->SetTextChangedHandler(GUI_CALLBACK(&CConsole::History_OnTextChanged, this));

// Load the console history from a file
m_pConsoleHistory->LoadFromFile(MTA_CONSOLE_INPUT_LOG_PATH);
// Load the console history from a file - use -cl2 suffix for secondary client
SString strInputLogPath = MTA_CONSOLE_INPUT_LOG_PATH;
if (g_pCore->IsSecondaryClient())
{
strInputLogPath.Replace(".log", "-cl2.log");
}
m_pConsoleHistory->LoadFromFile(strInputLogPath);
}

CConsole::~CConsole()
Expand Down
9 changes: 7 additions & 2 deletions Client/core/CConsoleLogger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@ CConsoleLogger* CSingleton<CConsoleLogger>::m_pSingleton = NULL;

CConsoleLogger::CConsoleLogger()
{
// Create file name
m_strFilename = CalcMTASAPath(MTA_CONSOLE_LOG_PATH);
// Create file name - use -cl2 suffix for secondary client
SString strLogPath = MTA_CONSOLE_LOG_PATH;
if (g_pCore->IsSecondaryClient())
{
strLogPath.Replace(".log", "-cl2.log");
}
m_strFilename = CalcMTASAPath(strLogPath);

// Cycle if over size (100KB, 5 backup files)
CycleFile(m_strFilename, 100, 5);
Expand Down
53 changes: 51 additions & 2 deletions Client/core/CCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,20 @@ const char* CCore::GetModInstallRoot(const char* szModName)
return m_strModInstallRoot;
}

bool CCore::IsSecondaryClient()
{
static bool bChecked = false;
static bool bIsSecondary = false;

if (!bChecked)
{
bIsSecondary = (strstr(GetCommandLine(), "-cl2") != NULL);
bChecked = true;
}

return bIsSecondary;
}

void CCore::ForceCursorVisible(bool bVisible, bool bToggleControls)
{
m_bCursorToggleControls = bToggleControls;
Expand Down Expand Up @@ -879,6 +893,24 @@ void CCore::ApplyHooks()
// Remove useless DirectPlay dependency (dpnhpast.dll) @ 0x745701
// We have to patch here as multiplayer_sa and game_sa are loaded too late
DetourLibraryFunction("kernel32.dll", "LoadLibraryA", Win32LoadLibraryA, SkipDirectPlay_LoadLibraryA);

#ifdef MTA_CL2
// Disable code that disallows multiple instances of GTA:SA
// Disable `if (IsAppAlreadyRunning())` in WinMain
{
DWORD oldProtect;
VirtualProtect(reinterpret_cast<void*>(0x74872D), 9, PAGE_READWRITE, &oldProtect);
memcpy(reinterpret_cast<void*>(0x74872D), "\x90\x90\x90\x90\x90\x90\x90\x90\x90", 9);
VirtualProtect(reinterpret_cast<void*>(0x74872D), 9, oldProtect, &oldProtect);
}
// Create an unnamed semaphore in CdStreamInitThread.
{
DWORD oldProtect;
VirtualProtect(reinterpret_cast<void*>(0x406945), 5, PAGE_READWRITE, &oldProtect);
memcpy(reinterpret_cast<void*>(0x406945), "\x6A\x00\x90\x90\x90", 5);
VirtualProtect(reinterpret_cast<void*>(0x406945), 5, oldProtect, &oldProtect);
}
#endif
}

bool UsingAltD3DSetup()
Expand Down Expand Up @@ -1102,8 +1134,25 @@ void CCore::CreateXML()

if (!m_pConfigFile)
{
// Load config XML file
m_pConfigFile = m_pXML->CreateXML(CalcMTASAPath(MTA_CONFIG_PATH));
// Load config XML file - use -cl2 suffix for secondary client
SString strConfigPath = MTA_CONFIG_PATH;
if (IsSecondaryClient())
{
strConfigPath = strConfigPath.Replace(".xml", "-cl2.xml");

// If CL2 config doesn't exist, copy from the primary config
SString strFullPath = CalcMTASAPath(strConfigPath);
if (!FileExists(strFullPath))
{
SString strPrimaryConfig = CalcMTASAPath(MTA_CONFIG_PATH);
if (FileExists(strPrimaryConfig))
{
FileCopy(strPrimaryConfig, strFullPath);
}
}
}

m_pConfigFile = m_pXML->CreateXML(CalcMTASAPath(strConfigPath));
if (!m_pConfigFile)
{
assert(false);
Expand Down
1 change: 1 addition & 0 deletions Client/core/CCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ class CCore : public CCoreInterface, public CSingleton<CCore>
bool IsOfflineMod() { return m_bIsOfflineMod; }
const char* GetModInstallRoot(const char* szModName);
bool CheckDiskSpace(uint uiResourcesPathMinMB = 10, uint uiDataPathMinMB = 10);
bool IsSecondaryClient();

// Subsystems
void CreateGame();
Expand Down
21 changes: 20 additions & 1 deletion Client/core/CSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4986,7 +4986,26 @@ void CSettings::SetChatColorValues(eChatColorType eType, CColor pColor)

void CSettings::LoadChatPresets()
{
CXMLFile* pPresetsFile = CCore::GetSingleton().GetXML()->CreateXML(CalcMTASAPath(CHAT_PRESETS_PATH));
SString strPresetsPath = CHAT_PRESETS_PATH;
bool bIsSecondary = g_pCore->IsSecondaryClient();

if (bIsSecondary)
{
strPresetsPath = strPresetsPath.Replace(".xml", "-cl2.xml");

// If CL2 presets file doesn't exist, copy from the primary
SString strFullPath = CalcMTASAPath(strPresetsPath);
if (!FileExists(strFullPath))
{
SString strPrimaryPresets = CalcMTASAPath(CHAT_PRESETS_PATH);
if (FileExists(strPrimaryPresets))
{
FileCopy(strPrimaryPresets, strFullPath);
}
}
}

CXMLFile* pPresetsFile = CCore::GetSingleton().GetXML()->CreateXML(CalcMTASAPath(strPresetsPath));
if (pPresetsFile && pPresetsFile->Parse())
{
CXMLNode* pPresetsRoot = pPresetsFile->GetRootNode();
Expand Down
34 changes: 34 additions & 0 deletions Client/core/CVersionUpdater.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "CNewsBrowser.h"
#include "CFilePathTranslator.h"
#include "SharedUtil.Thread.h"
#include "../loader/Main.h"
#include <charconv>

///////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -564,6 +565,21 @@ void CVersionUpdater::DoPulse()
///////////////////////////////////////////////////////////////
void CVersionUpdater::InitiateUpdate(const SString& strType, const SString& strData, const SString& strHost)
{
// Disable server-initiated updates for secondary client
// TODO: or when secondary client is running
if (g_pCore->IsSecondaryClient())
return;

#ifdef MTA_CL2
Comment thread
Xenius97 marked this conversation as resolved.
// Don't allow update if CL2 is running
Comment thread
qaisjp marked this conversation as resolved.
HANDLE hCL2Mutex = OpenMutexA(SYNCHRONIZE, FALSE, MTA_GUID_CL2);
if (hCL2Mutex)
{
CloseHandle(hCL2Mutex);
return;
}
#endif

if (strType == "Mandatory")
{
CCore::GetSingleton().RemoveMessageBox();
Expand Down Expand Up @@ -615,6 +631,24 @@ void CVersionUpdater::InitiateDataFilesFix()
///////////////////////////////////////////////////////////////
void CVersionUpdater::InitiateManualCheck()
{
// Disable update checking for secondary client
if (CCore::GetSingleton().IsSecondaryClient())
{
CCore::GetSingleton().ShowMessageBox(_("Information"), _("Update checking is disabled in secondary client"), MB_BUTTON_OK | MB_ICON_INFO);
return;
}

#ifdef MTA_CL2
// Don't allow update if CL2 is running
HANDLE hCL2Mutex = OpenMutexA(SYNCHRONIZE, FALSE, MTA_GUID_CL2);
if (hCL2Mutex)
{
CloseHandle(hCL2Mutex);
CCore::GetSingleton().ShowMessageBox(_("Information"), _("Can't check for updates while secondary client is running"), MB_BUTTON_OK | MB_ICON_INFO);
return;
}
#endif

if (GetQuestionBox().IsVisible())
{
// Bring to the front
Expand Down
14 changes: 8 additions & 6 deletions Client/core/DXHook/CProxyDirect3D9.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,11 +323,12 @@ HRESULT CProxyDirect3D9::CreateDevice(UINT Adapter, D3DDEVTYPE DeviceType, HWND
WriteDebugEvent(SString(" FullScreen_RefreshRateInHz:%d PresentationInterval:0x%08x", pPresentationParameters->FullScreen_RefreshRateInHz,
pPresentationParameters->PresentationInterval));

// Change the window title to MTA: San Andreas
// Change the window title to MTA: San Andreas
bool bIsSecondaryClient = g_pCore->IsSecondaryClient();
#ifdef MTA_DEBUG
SetWindowTextW(hFocusWindow, MbUTF8ToUTF16("MTA: San Andreas [DEBUG]").c_str());
SetWindowTextW(hFocusWindow, MbUTF8ToUTF16(bIsSecondaryClient ? "MTA: San Andreas [CL2-DEBUG]" : "MTA: San Andreas [DEBUG]").c_str());
#else
SetWindowTextW(hFocusWindow, MbUTF8ToUTF16("MTA: San Andreas").c_str());
SetWindowTextW(hFocusWindow, MbUTF8ToUTF16(bIsSecondaryClient ? "MTA: San Andreas [CL2]" : "MTA: San Andreas").c_str());
#endif

// Set dark titlebar if needed
Expand Down Expand Up @@ -1351,11 +1352,12 @@ HRESULT CCore::OnPostCreateDevice(HRESULT hResult, IDirect3D9* pDirect3D, UINT A
if (hResult == D3D_OK)
AddCapsReport(Adapter, pDirect3D, *ppReturnedDeviceInterface, true);

// Change the window title to MTA: San Andreas
// Change the window title to MTA: San Andreas
bool bIsSecondaryClient = g_pCore->IsSecondaryClient();
#ifdef MTA_DEBUG
SetWindowTextW(hFocusWindow, MbUTF8ToUTF16("MTA: San Andreas [DEBUG]").c_str());
SetWindowTextW(hFocusWindow, MbUTF8ToUTF16(bIsSecondaryClient ? "MTA: San Andreas [CL2-DEBUG]" : "MTA: San Andreas [DEBUG]").c_str());
#else
SetWindowTextW(hFocusWindow, MbUTF8ToUTF16("MTA: San Andreas").c_str());
SetWindowTextW(hFocusWindow, MbUTF8ToUTF16(bIsSecondaryClient ? "MTA: San Andreas [CL2]" : "MTA: San Andreas").c_str());
#endif

// Log graphic card name
Expand Down
30 changes: 27 additions & 3 deletions Client/core/ServerBrowser/CServerCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,25 @@ CServerCache::~CServerCache()
///////////////////////////////////////////////////////////////
bool CServerCache::LoadServerCache()
{
// Load config XML file
CXMLFile* m_pConfigFile = CCore::GetSingleton().GetXML()->CreateXML(CalcMTASAPath(MTA_SERVER_CACHE_PATH));
// Load config XML file - use -cl2 suffix for secondary client
SString strCachePath = MTA_SERVER_CACHE_PATH;
if (g_pCore->IsSecondaryClient())
{
strCachePath = strCachePath.Replace(".xml", "-cl2.xml");

// If CL2 cache doesn't exist, copy from the primary cache
SString strFullPath = CalcMTASAPath(strCachePath);
if (!FileExists(strFullPath))
{
SString strPrimaryCache = CalcMTASAPath(MTA_SERVER_CACHE_PATH);
if (FileExists(strPrimaryCache))
{
FileCopy(strPrimaryCache, strFullPath);
}
}
}

CXMLFile* m_pConfigFile = CCore::GetSingleton().GetXML()->CreateXML(CalcMTASAPath(strCachePath));
if (!m_pConfigFile)
return false;
m_pConfigFile->Parse();
Expand Down Expand Up @@ -273,7 +290,14 @@ DWORD WINAPI CServerCache::StaticThreadProc(LPVOID lpdwThreadParam)
///////////////////////////////////////////////////////////////
void CServerCache::StaticSaveServerCache()
{
CXMLFile* m_pConfigFile = CCore::GetSingleton().GetXML()->CreateXML(CalcMTASAPath(MTA_SERVER_CACHE_PATH));
// Use -cl2 suffix for secondary client
SString strCachePath = MTA_SERVER_CACHE_PATH;
if (g_pCore->IsSecondaryClient())
{
strCachePath = strCachePath.Replace(".xml", "-cl2.xml");
}

CXMLFile* m_pConfigFile = CCore::GetSingleton().GetXML()->CreateXML(CalcMTASAPath(strCachePath));
if (!m_pConfigFile)
return;
m_pConfigFile->Parse();
Expand Down
22 changes: 13 additions & 9 deletions Client/loader/CInstallManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,18 @@ CInstallManager* GetInstallManager()
void CInstallManager::InitSequencer()
{
#define CR "\n"
SString strVersionCheckPart = IsSecondaryClient()
? CR " GOTO launch: " // Skip version check for secondary client
: CR " CALL ProcessGtaVersionCheck " //
CR " IF LastResult == ok GOTO gta_version_end: " //
CR " IF LastResult == quit GOTO do_quit: " //
CR " " //
CR " CALL ChangeToAdmin " // If changes failed, try as admin
CR " IF LastResult == ok GOTO gta_version_check: " //
CR " CALL Quit " //
CR " " //
CR "gta_version_end: "; ////// End of 'gta version check' //////

SString strSource = CR "initial: " // *** Starts here by default
CR " CALL CheckOnRestartCommand " ////// Start of 'update game' //////
CR " IF LastResult != ok GOTO update_end: " //
Expand Down Expand Up @@ -218,15 +230,7 @@ void CInstallManager::InitSequencer()
CR "gta_dll_end: " ////// End of 'gta dll check' //////
CR " " //
CR "gta_version_check:" ////// Start of 'gta version check' //////
CR " CALL ProcessGtaVersionCheck " //
CR " IF LastResult == ok GOTO gta_version_end: " //
CR " IF LastResult == quit GOTO do_quit: " //
CR " " //
CR " CALL ChangeToAdmin " // If changes failed, try as admin
CR " IF LastResult == ok GOTO gta_version_check: " //
CR " CALL Quit " //
CR " " //
CR "gta_version_end: " ////// End of 'gta version check' //////
+ strVersionCheckPart +
CR " " //
CR "service_check: " ////// Start of 'Service checks' //////
CR " CALL ProcessServiceChecks " // Make changes to comply with service requirements
Expand Down
1 change: 1 addition & 0 deletions Client/loader/Main.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#define STEAM_GTA_EXE_NAME "gta-sa.exe"
#define MTA_GTA_KNOWN_FILE_NAME "models\\gta3.img"
#define MTA_GUID "Global\\{4962AF5F-5D82-412D-9CCA-AB8BB9DBD353}"
#define MTA_GUID_CL2 "Global\\{4962AF5F-5D82-412D-9CCA-AB8BB9DBD352}"
#define URI_CONNECT 1
#define MTA_EXE_NAME_RELEASE "Multi Theft Auto.exe"

Expand Down
7 changes: 6 additions & 1 deletion Client/loader/MainFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,8 @@ void HandleNotUsedMainMenu()

// Check current display mode in coreconfig.xml
{
SString strCoreConfigFilename = CalcMTASAPath(PathJoin("mta", "config", "coreconfig.xml"));
SString strCoreConfigName = IsSecondaryClient() ? "coreconfig-cl2.xml" : "coreconfig.xml";
SString strCoreConfigFilename = CalcMTASAPath(PathJoin("mta", "config", strCoreConfigName));
SString strCoreConfig;
FileLoad(strCoreConfigFilename, strCoreConfig);
SString strWindowed = strCoreConfig.SplitRight("<display_windowed>").Left(1);
Expand Down Expand Up @@ -976,6 +977,10 @@ void PostRunWatchDogs(int iReturnCode)
//////////////////////////////////////////////////////////
void HandleIfGTAIsAlreadyRunning()
{
// Skip GTA check for secondary client to allow multiple GTA instances
if (IsSecondaryClient())
return;

if (IsGTARunning())
{
if (MessageBoxUTF8(
Expand Down
Loading
Loading