Skip to content

Commit f9f14d1

Browse files
authored
Merge branch 'multitheftauto:master' into feature/remove-all-domains
2 parents 208fa48 + 1660597 commit f9f14d1

File tree

170 files changed

+17839
-12242
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

170 files changed

+17839
-12242
lines changed

.git-blame-ignore-revs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,3 +247,9 @@ b42518e64803762d27fe6277b36044d1a71fc2c4
247247
# Date: Mon Jun 12 19:01:59 2023 +0300
248248
#
249249
# Apply win-apply-clang-format.bat
250+
251+
466c162fbd1af45b290849cc0771fab66f55ba34
252+
# Author: Dutchman101 <[email protected]>
253+
# Date: Wed April 9 19:01:39 2025 +0300
254+
#
255+
# Eliminate the use of http:// across whole project
Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,62 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2-
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:v3="urn:schemas-microsoft-com:asm.v3">
3-
<assemblyIdentity type="win32" version="1.0.0.0" processorArchitecture="*" name="CEFLauncher" />
2+
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
3+
xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"
4+
manifestVersion="1.0">
5+
<assemblyIdentity type="win32"
6+
name="CEFLauncher"
7+
version="1.0.0.0"
8+
processorArchitecture="x86" />
49

5-
<v3:application>
6-
<windowsSettings>
7-
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
8-
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
9-
</windowsSettings>
10-
</v3:application>
10+
<description>MTA:SA CEF (Chromium Embedded Framework) Launcher</description>
1111

1212
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
1313
<application>
14-
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" /> <!-- Windows 10 and Windows 11 -->
15-
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" /> <!-- Windows 8.1 -->
16-
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" /> <!-- Windows 8 -->
17-
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" /> <!-- Windows 7 -->
14+
<!-- Windows 11 and Windows 10 -->
15+
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
16+
<!-- Windows 8.1 -->
17+
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
18+
<!-- Windows 8 -->
19+
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
20+
<!-- Windows 7 -->
21+
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
1822
</application>
1923
</compatibility>
2024

25+
<asmv3:application>
26+
<asmv3:windowsSettings
27+
xmlns:ws2005="http://schemas.microsoft.com/SMI/2005/WindowsSettings"
28+
xmlns:ws2013="http://schemas.microsoft.com/SMI/2013/WindowsSettings"
29+
xmlns:ws2016="http://schemas.microsoft.com/SMI/2016/WindowsSettings"
30+
xmlns:ws2017="http://schemas.microsoft.com/SMI/2017/WindowsSettings"
31+
xmlns:ws2019="http://schemas.microsoft.com/SMI/2019/WindowsSettings"
32+
xmlns:ws2020="http://schemas.microsoft.com/SMI/2020/WindowsSettings"
33+
xmlns:ws2024="http://schemas.microsoft.com/SMI/2024/WindowsSettings">
34+
<ws2005:dpiAware>true/pm</ws2005:dpiAware>
35+
<ws2016:dpiAwareness>PerMonitorV2, PerMonitor</ws2016:dpiAwareness>
36+
<ws2013:ultraHighResolutionScrollingAware>true</ws2013:ultraHighResolutionScrollingAware>
37+
<ws2016:longPathAware>true</ws2016:longPathAware>
38+
<ws2019:activeCodePage>UTF-8</ws2019:activeCodePage>
39+
<ws2020:heapType>SegmentHeap</ws2020:heapType>
40+
<ws2024:supportedArchitectures>x86</ws2024:supportedArchitectures>
41+
</asmv3:windowsSettings>
42+
</asmv3:application>
43+
2144
<dependency optional="yes">
2245
<dependentAssembly>
23-
<!-- Automatically use ComCtl32.dll version 6 or later. -->
24-
<assemblyIdentity
25-
type="win32"
26-
name="Microsoft.Windows.Common-Controls"
27-
version="6.0.0.0"
28-
processorArchitecture="*"
29-
publicKeyToken="6595b64144ccf1df"
30-
language="*" />
46+
<assemblyIdentity type="win32"
47+
name="Microsoft.Windows.Common-Controls"
48+
version="6.0.0.0"
49+
processorArchitecture="*"
50+
publicKeyToken="6595b64144ccf1df"
51+
language="*" />
3152
</dependentAssembly>
3253
</dependency>
54+
55+
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
56+
<security>
57+
<requestedPrivileges>
58+
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
59+
</requestedPrivileges>
60+
</security>
61+
</trustInfo>
3362
</assembly>

Client/ceflauncher/Main.cpp

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,30 @@
2020
(set flag.new_cef_exe on the build server to generate new exe)
2121
*/
2222

23-
int _declspec(dllimport) InitCEF();
23+
#include <cstdlib>
24+
#include <functional>
2425

25-
using HINSTANCE = struct HINSTANCE__*;
26+
struct HINSTANCE__;
27+
using HINSTANCE = HINSTANCE__*;
2628
using LPSTR = char*;
2729

28-
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdShow, int nCmdShow)
30+
extern "C" [[nodiscard("InitCEF return value must be used")]] __declspec(dllimport) int __cdecl InitCEF();
31+
32+
// Users are faced with vague crashes in CEFLauncher.exe, so rather than over-engineering all this is intended
33+
// Do note that CEFLauncher.exe ends up hosting any GPU rendering processes of CEF
34+
int __stdcall WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
2935
{
30-
return InitCEF();
36+
const auto init = std::function<int()>{InitCEF};
37+
const auto safe_invoke = [&]() noexcept -> int
38+
{
39+
try
40+
{
41+
return std::invoke_r<int>(init);
42+
}
43+
catch (...)
44+
{
45+
return EXIT_FAILURE;
46+
}
47+
};
48+
return safe_invoke();
3149
}

Client/ceflauncher/premake5.lua

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,29 @@ project "CEFLauncher"
2020
"*.manifest",
2121
}
2222

23+
filter "system:windows"
24+
buildoptions {
25+
"/Zc:inline",
26+
"/Zc:throwingNew",
27+
"/diagnostics:caret",
28+
"/sdl",
29+
"/guard:cf"
30+
}
31+
editandcontinue "Off"
32+
linkoptions { "/guard:cf" }
33+
34+
filter {"system:windows", "configurations:Debug"}
35+
defines { "_DEBUG" }
36+
runtime "Debug"
37+
38+
filter {"system:windows", "configurations:Release"}
39+
optimize "Speed"
40+
defines { "NDEBUG" }
41+
42+
filter {"system:windows", "configurations:Nightly"}
43+
optimize "Speed"
44+
defines { "NDEBUG" }
45+
2346
filter "architecture:not x86"
2447
flags { "ExcludeFromBuild" }
2548

Client/ceflauncher_DLL/Main.cpp

Lines changed: 126 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -9,106 +9,170 @@
99
*
1010
*****************************************************************************/
1111

12+
// #define CEF_ENABLE_SANDBOX
13+
14+
#include <atomic>
15+
1216
#define WIN32_NO_STATUS
1317
#define WIN32_LEAN_AND_MEAN
1418
#include <Windows.h>
19+
1520
#undef WIN32_NO_STATUS
1621
#include <ntstatus.h>
17-
#include <winnt.h>
1822
#include <winternl.h>
1923
#include <delayimp.h>
24+
2025
#include "CCefApp.h"
21-
#include <string>
22-
#include <cef3/cef/include/cef_sandbox_win.h>
26+
#include "SharedUtil.h"
2327

24-
// #define CEF_ENABLE_SANDBOX
2528
#ifdef CEF_ENABLE_SANDBOX
26-
#pragma comment(lib, "cef_sandbox.lib")
29+
#include <cef3/cef/include/cef_sandbox_win.h>
30+
#pragma comment(lib, "cef_sandbox.lib")
2731
#endif
2832

29-
DWORD WINAPI CheckParentProcessAliveness(LPVOID);
33+
// Return codes
34+
inline constexpr int CEF_INIT_SUCCESS = 0;
35+
inline constexpr int CEF_INIT_ERROR_NO_BASE_DIR = -1;
36+
inline constexpr int CEF_INIT_ERROR_DLL_LOAD_FAILED = -2;
3037

31-
int _declspec(dllexport) InitCEF()
32-
{
33-
// Get absolute CEFLauncher.exe path
34-
TCHAR buffer[MAX_PATH];
35-
GetModuleFileName(NULL, buffer, MAX_PATH);
36-
std::wstring currentFileName(buffer);
38+
inline constexpr DWORD CEF_PARENT_CHECK_INTERVAL = 1000;
39+
inline constexpr const char* CEF_DLL_NAME = "libcef.dll";
40+
inline constexpr const char* CEF_MTA_SUBDIR = "MTA";
3741

38-
// Extract MTA path and set DLL directory (absolute path is required here)
39-
size_t pos = currentFileName.find_last_of(L'\\');
40-
std::wstring mtaPath = currentFileName.substr(0, pos - 3); // Strip "CEF"
41-
SetDllDirectory(mtaPath.c_str());
42+
inline constexpr DWORD PARENT_CHECK_ERROR_NO_QUERY_FUNC = 1;
43+
inline constexpr DWORD PARENT_CHECK_ERROR_QUERY_FAILED = 2;
44+
inline constexpr DWORD PARENT_CHECK_ERROR_OPEN_FAILED = 3;
4245

43-
// Load libcef.dll from the DLL directory
44-
assert(SUCCEEDED(__HrLoadAllImportsForDll("libcef.dll")));
46+
using NtQueryInformationProcessFunc = NTSTATUS(NTAPI*)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
4547

46-
// Load CEF
47-
CefMainArgs mainArgs(GetModuleHandle(NULL));
48-
CefRefPtr<CCefApp> app{new CCefApp};
48+
// Safe parent monitor thread shutdown
49+
std::atomic<bool> g_bShouldTerminateMonitor{false};
50+
std::atomic<HANDLE> g_hMonitorThread{nullptr};
4951

50-
void* sandboxInfo = nullptr;
51-
#ifdef CEF_ENABLE_SANDBOX
52-
CefScopedSandboxInfo scopedSandbox;
53-
sandboxInfo = scopedSandbox.sandbox_info();
54-
#endif
52+
namespace
53+
{
54+
[[nodiscard]] auto GetNtQueryInformationProcess() noexcept -> NtQueryInformationProcessFunc
55+
{
56+
const auto ntdll = GetModuleHandleW(L"ntdll.dll");
57+
if (!ntdll)
58+
return nullptr;
59+
60+
const auto procAddr = GetProcAddress(ntdll, "NtQueryInformationProcess");
61+
if (!procAddr)
62+
return nullptr;
63+
64+
return reinterpret_cast<NtQueryInformationProcessFunc>(procAddr);
65+
}
5566

56-
const HANDLE parentCheckThread = CreateThread(nullptr, 0, CheckParentProcessAliveness, nullptr, 0, nullptr);
67+
[[nodiscard]] auto GetParentProcessId(NtQueryInformationProcessFunc queryFunc) noexcept -> DWORD
68+
{
69+
PROCESS_BASIC_INFORMATION info{};
70+
ULONG returnLength = 0;
71+
72+
if (const auto status = queryFunc(GetCurrentProcess(), ProcessBasicInformation, &info, sizeof(info), &returnLength);
73+
!NT_SUCCESS(status) || returnLength < sizeof(PROCESS_BASIC_INFORMATION))
74+
{
75+
return 0;
76+
}
5777

58-
const int exitCode = CefExecuteProcess(mainArgs, app, sandboxInfo);
78+
return static_cast<DWORD>(reinterpret_cast<ULONG_PTR>(info.Reserved3));
79+
}
5980

60-
if (parentCheckThread != nullptr)
81+
void MonitorParentProcess(HANDLE parentProcess) noexcept
6182
{
62-
TerminateThread(parentCheckThread, 0);
63-
CloseHandle(parentCheckThread);
83+
while (!g_bShouldTerminateMonitor.load(std::memory_order_acquire))
84+
{
85+
const DWORD result = WaitForSingleObject(parentProcess, CEF_PARENT_CHECK_INTERVAL);
86+
87+
if (result == WAIT_OBJECT_0)
88+
{
89+
DWORD exitCode = 0;
90+
if (GetExitCodeProcess(parentProcess, &exitCode))
91+
ExitProcess(exitCode);
92+
else
93+
ExitProcess(0);
94+
}
95+
else if (result == WAIT_FAILED)
96+
{
97+
// Wine/Proton compatibility: Exit thread instead of terminating process
98+
// Wine's handle implementation dont support all wait operations reliably
99+
break;
100+
}
101+
}
64102
}
103+
} // namespace
65104

66-
return exitCode;
67-
}
105+
DWORD WINAPI CheckParentProcessAliveness(LPVOID) noexcept;
68106

69-
static DWORD WINAPI CheckParentProcessAliveness(LPVOID)
107+
BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, [[maybe_unused]] LPVOID lpReserved)
70108
{
71-
NTSTATUS(NTAPI * queryInformation)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG) = nullptr;
72-
73-
if (HMODULE const ntdll = GetModuleHandleW(L"ntdll.dll"); ntdll != nullptr)
109+
if (dwReason == DLL_PROCESS_ATTACH)
110+
{
111+
DisableThreadLibraryCalls(hModule);
112+
g_bShouldTerminateMonitor.store(false, std::memory_order_relaxed);
113+
g_hMonitorThread.store(nullptr, std::memory_order_relaxed);
114+
}
115+
else if (dwReason == DLL_PROCESS_DETACH)
74116
{
75-
queryInformation = reinterpret_cast<decltype(queryInformation)>(GetProcAddress(ntdll, "NtQueryInformationProcess"));
117+
g_bShouldTerminateMonitor.store(true, std::memory_order_release);
76118
}
119+
120+
return TRUE;
121+
}
77122

78-
if (queryInformation == nullptr)
79-
return 1;
123+
extern "C" [[nodiscard]] __declspec(dllexport) auto InitCEF() noexcept -> int
124+
{
125+
const auto baseDir = SharedUtil::GetMTAProcessBaseDir();
126+
if (baseDir.empty())
127+
return CEF_INIT_ERROR_NO_BASE_DIR;
128+
129+
const auto mtaDir = SharedUtil::PathJoin(baseDir, CEF_MTA_SUBDIR);
130+
SetDllDirectoryW(SharedUtil::FromUTF8(mtaDir));
80131

81-
PROCESS_BASIC_INFORMATION info{};
132+
if (FAILED(__HrLoadAllImportsForDll(CEF_DLL_NAME)))
133+
return CEF_INIT_ERROR_DLL_LOAD_FAILED;
82134

83-
ULONG returnLength = 0;
84-
NTSTATUS status = queryInformation(GetCurrentProcess(), ProcessBasicInformation, &info, sizeof(info), &returnLength);
135+
const CefMainArgs mainArgs(GetModuleHandleW(nullptr));
136+
const CefRefPtr<CCefApp> app{new CCefApp};
85137

86-
if (!NT_SUCCESS(status) || returnLength < sizeof(PROCESS_BASIC_INFORMATION))
87-
return 2;
138+
void* sandboxInfo = nullptr;
139+
#ifdef CEF_ENABLE_SANDBOX
140+
const CefScopedSandboxInfo scopedSandbox;
141+
sandboxInfo = scopedSandbox.sandbox_info();
142+
#endif
88143

89-
const auto parentProcessId = static_cast<DWORD>(reinterpret_cast<ULONG_PTR>(info.Reserved3));
90-
const HANDLE parentProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION, FALSE, parentProcessId);
144+
const auto hThread = CreateThread(nullptr, 0, CheckParentProcessAliveness, nullptr, 0, nullptr);
145+
if (hThread)
146+
g_hMonitorThread.store(hThread, std::memory_order_release);
91147

92-
if (parentProcess == nullptr)
93-
{
94-
if (GetLastError() == ERROR_INVALID_PARAMETER)
95-
ExitProcess(0);
148+
return CefExecuteProcess(mainArgs, app, sandboxInfo);
149+
}
96150

97-
return 3;
98-
}
151+
static auto WINAPI CheckParentProcessAliveness([[maybe_unused]] LPVOID) noexcept -> DWORD
152+
{
153+
const auto queryFunc = GetNtQueryInformationProcess();
154+
if (!queryFunc)
155+
return PARENT_CHECK_ERROR_NO_QUERY_FUNC;
99156

100-
while (true)
101-
{
102-
DWORD exitCode{};
157+
const auto parentProcessId = GetParentProcessId(queryFunc);
158+
if (parentProcessId == 0)
159+
return PARENT_CHECK_ERROR_QUERY_FAILED;
103160

104-
if (!GetExitCodeProcess(parentProcess, &exitCode) || exitCode != STILL_ACTIVE)
161+
const auto parentProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION, false, parentProcessId);
162+
if (!parentProcess)
163+
{
164+
// Wine/Proton fallback: PROCESS_QUERY_LIMITED_INFORMATION may not be implemented
165+
const auto parentProcessFallback = OpenProcess(SYNCHRONIZE, false, parentProcessId);
166+
if (parentProcessFallback)
105167
{
106-
CloseHandle(parentProcess);
107-
ExitProcess(exitCode);
168+
MonitorParentProcess(parentProcessFallback);
169+
CloseHandle(parentProcessFallback);
170+
return CEF_INIT_SUCCESS;
108171
}
109-
110-
Sleep(1000);
172+
return PARENT_CHECK_ERROR_OPEN_FAILED;
111173
}
112174

113-
return 0;
175+
MonitorParentProcess(parentProcess);
176+
CloseHandle(parentProcess);
177+
return CEF_INIT_SUCCESS;
114178
}

0 commit comments

Comments
 (0)