From 4b6522fce658aa110633102d9d83abc2dd7a98fa Mon Sep 17 00:00:00 2001 From: Fournet Enzo Date: Fri, 9 Jan 2026 18:34:18 +0100 Subject: [PATCH 01/10] Support APPLE platform in CMake build Updated the CMakeLists.txt to treat APPLE as a supported platform alongside UNIX for dependency linking. This ensures proper library linking on macOS systems. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 67e666ef..6d0728be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ if (WIN32) find_package(OpenSSL REQUIRED) target_link_libraries(${PROJECT_NAME} PRIVATE ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto ws2_32 httplib::httplib nlohmann_json::nlohmann_json CURL::libcurl) -elseif (UNIX) +elseif (UNIX OR APPLE) find_package(ZLIB REQUIRED) find_package(OpenSSL REQUIRED) target_link_libraries(${PROJECT_NAME} PRIVATE From 2debb1e2656146f0f1cabd62e3f37f34ff5a555d Mon Sep 17 00:00:00 2001 From: Fournet Enzo Date: Fri, 9 Jan 2026 18:34:29 +0100 Subject: [PATCH 02/10] Add GitHub Actions workflow for CMake MacOS build Introduces a workflow to build the project on macOS using CMake and vcpkg. The workflow installs dependencies, configures and builds the project for arm64 architecture, and uploads the resulting artifact. --- .github/workflows/cmake-macos.yml | 46 +++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .github/workflows/cmake-macos.yml diff --git a/.github/workflows/cmake-macos.yml b/.github/workflows/cmake-macos.yml new file mode 100644 index 00000000..6efce071 --- /dev/null +++ b/.github/workflows/cmake-macos.yml @@ -0,0 +1,46 @@ +name: CMake MacOS Build + +on: [push, pull_request, workflow_dispatch] + +env: + BUILD_TYPE: Release + +jobs: + macos-build: + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + with: + submodules: 'true' + + - name: Restore artifacts, or run vcpkg, build and cache artifacts + uses: lukka/run-vcpkg@v7 + id: runvcpkg + with: + vcpkgArguments: 'zlib:arm64-osx nlohmann-json:arm64-osx openssl:arm64-osx cpp-httplib[openssl]:arm64-osx curl:arm64-osx' + vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg' + vcpkgGitCommitId: '40616a5e954f7be1077ef37db3fbddbd5dcd1ca6' + + - name: Create Build Environment + run: cmake -E make_directory ${{github.workspace}}/build-macos + + - name: Configure CMake + shell: bash + working-directory: ${{github.workspace}}/build-macos + run: | + cmake $GITHUB_WORKSPACE \ + -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ + -DCMAKE_TOOLCHAIN_FILE='${{ runner.workspace }}/b/vcpkg/scripts/buildsystems/vcpkg.cmake' \ + -DCMAKE_OSX_ARCHITECTURES=arm64 + + - name: Build + working-directory: ${{github.workspace}}/build-macos + shell: bash + run: cmake --build . --config $BUILD_TYPE + + - name: Archive artifacts + uses: actions/upload-artifact@v4 + with: + name: BeamMP-Launcher + path: ${{github.workspace}}/build-macos/BeamMP-Launcher \ No newline at end of file From 3610cffc850a4293bbac25a04ecf1614baba6125 Mon Sep 17 00:00:00 2001 From: Fournet Enzo Date: Fri, 9 Jan 2026 18:37:15 +0100 Subject: [PATCH 03/10] Update .gitignore with additional build and system files Added .DS_Store, vcpkg_installed, and platform-specific build directories to .gitignore to prevent accidental commits of system and build artifacts. --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index d1128670..ce6b3b51 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ cmake-build-release /*.sh /*.obj /*.exe +.DS_Store/ .cache/ .https_debug/ Launcher.cfg @@ -13,3 +14,9 @@ bin/ compile_commands.json key out/ +vcpkg_installed/ + +build-linux/ +build-macos/ +build-windows/ + From 020b2138f93208e0962b71665de803ea445663fb Mon Sep 17 00:00:00 2001 From: Fournet Enzo Date: Fri, 9 Jan 2026 19:19:27 +0100 Subject: [PATCH 04/10] Add macOS support and platform checks throughout codebase Extended platform checks to include __APPLE__ for macOS support in multiple files. Added macOS-specific logic for game path detection, legitimacy check, and error handling where game launching is not supported. Updated includes and platform-specific code paths to ensure compatibility with both Linux and macOS. --- include/Network/network.hpp | 6 ++++-- include/Options.h | 2 +- src/GameStart.cpp | 25 ++++++++++++++++++++++- src/Network/Core.cpp | 20 +++++++++---------- src/Network/DNS.cpp | 7 ++++++- src/Network/GlobalHandler.cpp | 2 +- src/Network/Resources.cpp | 3 ++- src/Network/VehicleData.cpp | 4 ++-- src/Network/VehicleEvent.cpp | 3 ++- src/Security/BeamNG.cpp | 37 +++++++++++++++++++++++++++++++++-- src/Startup.cpp | 16 +++++++++------ 11 files changed, 96 insertions(+), 29 deletions(-) diff --git a/include/Network/network.hpp b/include/Network/network.hpp index f58bdad4..e76d2b87 100644 --- a/include/Network/network.hpp +++ b/include/Network/network.hpp @@ -9,11 +9,13 @@ #include #include -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) #include "linuxfixes.h" +#if defined(__linux__) #include -#include #include +#endif +#include #include #endif diff --git a/include/Options.h b/include/Options.h index da56051b..74e3e9e7 100644 --- a/include/Options.h +++ b/include/Options.h @@ -11,7 +11,7 @@ struct Options { #if defined(_WIN32) std::string executable_name = "BeamMP-Launcher.exe"; -#elif defined(__linux__) +#elif defined(__linux__) || defined(__APPLE__) std::string executable_name = "BeamMP-Launcher"; #endif unsigned int port = 4444; diff --git a/src/GameStart.cpp b/src/GameStart.cpp index 06326067..58d57170 100644 --- a/src/GameStart.cpp +++ b/src/GameStart.cpp @@ -6,7 +6,7 @@ #if defined(_WIN32) #include -#elif defined(__linux__) +#elif defined(__linux__) || defined(__APPLE__) #include "vdf_parser.hpp" #include #include @@ -128,6 +128,18 @@ std::filesystem::path GetGamePath() { Path += "current/"; return Path; } +#elif defined(__APPLE__) +std::filesystem::path GetGamePath() { + // Right now only steam is supported + struct passwd* pw = getpwuid(getuid()); + std::string homeDir = pw->pw_dir; + + std::string Path = homeDir + "/Library/Application Support/BeamNG/BeamNG.drive/"; + std::string Ver = CheckVer(GetGameDir()); + Ver = Ver.substr(0, Ver.find('.', Ver.find('.') + 1)); + Path += "current/"; + return Path; +} #endif #if defined(_WIN32) @@ -202,11 +214,22 @@ void StartGame(std::string Dir) { std::this_thread::sleep_for(std::chrono::seconds(5)); exit(2); } +#elif defined(__APPLE__) +// macOS does not support game launching - StartGame is never called +void StartGame(std::string Dir) { + // This function should never be called on macOS + error("Game launching is not supported on macOS"); +} #endif void InitGame(const beammp_fs_string& Dir) { +#if defined(__APPLE__) + // macOS does not support game launching + return; +#else if (!options.no_launch) { std::thread Game(StartGame, Dir); Game.detach(); } +#endif } diff --git a/src/Network/Core.cpp b/src/Network/Core.cpp index 902e1c73..08b2391c 100644 --- a/src/Network/Core.cpp +++ b/src/Network/Core.cpp @@ -13,7 +13,8 @@ #include #include #include -#elif defined(__linux__) +#elif defined(__linux__) || defined(__APPLE__) +#include "linuxfixes.h" #include #include #include @@ -481,19 +482,16 @@ int Handle(EXCEPTION_POINTERS* ep) { [[noreturn]] void CoreNetwork() { while (true) { -#if not defined(__MINGW32__) +#if defined(_WIN32) && not defined(__MINGW32__) __try { -#endif - CoreMain(); - -#if not defined(__MINGW32__) and not defined(__linux__) } __except (Handle(GetExceptionInformation())) { } -#elif not defined(__MINGW32__) and defined(__linux__) - } - catch (...) { - except("(Core) Code : " + std::string(strerror(errno))); - } +#elif defined(__linux__) || defined(__APPLE__) + try { + CoreMain(); + } catch (...) { + except("(Core) Code : " + std::string(strerror(errno))); + } #endif std::this_thread::sleep_for(std::chrono::seconds(1)); diff --git a/src/Network/DNS.cpp b/src/Network/DNS.cpp index f79b34b8..2c65449e 100644 --- a/src/Network/DNS.cpp +++ b/src/Network/DNS.cpp @@ -9,10 +9,11 @@ #if defined(_WIN32) #include -#elif defined(__linux__) +#elif defined(__linux__) || defined(__APPLE__) #include "linuxfixes.h" #include #include +#include #endif #include "Logger.h" @@ -33,10 +34,14 @@ std::string GetAddr(const std::string& IP) { host = gethostbyname(IP.c_str()); if (!host) { error("DNS lookup failed! on " + IP); +#if defined(_WIN32) WSACleanup(); +#endif return "DNS"; } std::string Ret = inet_ntoa(*((struct in_addr*)host->h_addr)); +#if defined(_WIN32) WSACleanup(); +#endif return Ret; } \ No newline at end of file diff --git a/src/Network/GlobalHandler.cpp b/src/Network/GlobalHandler.cpp index 776169ea..57913a99 100644 --- a/src/Network/GlobalHandler.cpp +++ b/src/Network/GlobalHandler.cpp @@ -10,7 +10,7 @@ #if defined(_WIN32) #include #include -#elif defined(__linux__) +#elif defined(__linux__) || defined(__APPLE__) #include "linuxfixes.h" #include #include diff --git a/src/Network/Resources.cpp b/src/Network/Resources.cpp index 1696dde8..5f38dab2 100644 --- a/src/Network/Resources.cpp +++ b/src/Network/Resources.cpp @@ -16,7 +16,8 @@ #if defined(_WIN32) #include -#elif defined(__linux__) +#elif defined(__linux__) || defined(__APPLE__) +#include "linuxfixes.h" #include #include #include diff --git a/src/Network/VehicleData.cpp b/src/Network/VehicleData.cpp index 092eaa46..7ee03fed 100644 --- a/src/Network/VehicleData.cpp +++ b/src/Network/VehicleData.cpp @@ -10,7 +10,7 @@ #if defined(_WIN32) #include -#elif defined(__linux__) +#elif defined(__linux__) || defined(__APPLE__) #include "linuxfixes.h" #include #include @@ -65,7 +65,7 @@ void UDPRcv() { sockaddr_in FromServer {}; #if defined(_WIN32) int clientLength = sizeof(FromServer); -#elif defined(__linux__) +#elif defined(__linux__) || defined(__APPLE__) socklen_t clientLength = sizeof(FromServer); #endif ZeroMemory(&FromServer, clientLength); diff --git a/src/Network/VehicleEvent.cpp b/src/Network/VehicleEvent.cpp index dcbb41e5..a57ac2ef 100644 --- a/src/Network/VehicleEvent.cpp +++ b/src/Network/VehicleEvent.cpp @@ -13,7 +13,8 @@ #if defined(_WIN32) #include -#elif defined(__linux__) +#elif defined(__linux__) || defined(__APPLE__) +#include "linuxfixes.h" #include #include #include diff --git a/src/Security/BeamNG.cpp b/src/Security/BeamNG.cpp index 19f55243..36e9647c 100644 --- a/src/Security/BeamNG.cpp +++ b/src/Security/BeamNG.cpp @@ -8,7 +8,7 @@ #include #if defined(_WIN32) #include -#elif defined(__linux__) +#elif defined(__linux__) || defined(__APPLE__) #include "vdf_parser.hpp" #include #include @@ -18,6 +18,7 @@ #include "Utils.h" #include +#include #include #include @@ -38,7 +39,7 @@ void lowExit(int code) { beammp_fs_string GetGameDir() { #if defined(_WIN32) return GameDir.substr(0, GameDir.find_last_of('\\')); -#elif defined(__linux__) +#elif defined(__linux__) || defined(__APPLE__) return GameDir.substr(0, GameDir.find_last_of('/')); #endif } @@ -278,6 +279,38 @@ void LegitimacyCheck() { error("The game directory was not found."); return; } +#elif defined(__APPLE__) + // On macOS, ask the user to provide the game directory path + info("Please enter the path to your BeamNG.drive installation directory:"); + info("Example: /Users/YourName/Library/Application Support/Steam/steamapps/common/BeamNG.drive"); + std::cout << "Game directory path: "; + + std::string userPath; + std::getline(std::cin, userPath); + + // Remove trailing slash if present + if (!userPath.empty() && (userPath.back() == '/' || userPath.back() == '\\')) { + userPath.pop_back(); + } + + std::filesystem::path gamePath(userPath); + + // Check if the path exists and contains integrity.json + if (!std::filesystem::exists(gamePath)) { + error("The specified path does not exist: " + userPath); + lowExit(8); + return; + } + + std::filesystem::path integrityPath = gamePath / "integrity.json"; + if (!std::filesystem::exists(integrityPath)) { + error("The specified path does not appear to be a valid BeamNG.drive installation (integrity.json not found)."); + lowExit(9); + return; + } + + GameDir = gamePath.string() + "/"; + info("Game directory set to: " + GameDir); #endif } std::string CheckVer(const std::filesystem::path& dir) { diff --git a/src/Startup.cpp b/src/Startup.cpp index 8f1875dd..f28e5bcf 100644 --- a/src/Startup.cpp +++ b/src/Startup.cpp @@ -78,7 +78,7 @@ Version::Version(const std::array& v) beammp_fs_string GetEN() { #if defined(_WIN32) return L"BeamMP-Launcher.exe"; -#elif defined(__linux__) +#elif defined(__linux__) || defined(__APPLE__) return "BeamMP-Launcher"; #endif } @@ -150,7 +150,7 @@ void URelaunch() { std::this_thread::sleep_for(std::chrono::seconds(1)); exit(1); } -#elif defined(__linux__) +#elif defined(__linux__) || defined(__APPLE__) void ReLaunch() { std::string Arg; for (int c = 2; c <= options.argc; c++) { @@ -158,7 +158,11 @@ void ReLaunch() { Arg += " "; } info("Relaunch!"); +#if defined(__linux__) + system("clear"); +#elif defined(__APPLE__) system("clear"); +#endif int ret = execv((GetBP() / GetEN()).c_str(), const_cast(options.argv)); if (ret < 0) { error(std::string("execv() failed with: ") + strerror(errno) + ". Failed to relaunch"); @@ -181,7 +185,7 @@ void URelaunch() { void CheckName() { #if defined(_WIN32) std::wstring DN = GetEN(), CDir = Utils::ToWString(options.executable_name), FN = CDir.substr(CDir.find_last_of('\\') + 1); -#elif defined(__linux__) +#elif defined(__linux__) || defined(__APPLE__) std::string DN = GetEN(), CDir = options.executable_name, FN = CDir.substr(CDir.find_last_of('/') + 1); #endif if (FN != DN) { @@ -280,7 +284,7 @@ void InitLauncher() { CheckLocalKey(); CheckForUpdates(std::string(GetVer()) + GetPatch()); } -#elif defined(__linux__) +#elif defined(__linux__) || defined(__APPLE__) void InitLauncher() { info("BeamMP Launcher v" + GetVer() + GetPatch()); @@ -367,8 +371,8 @@ void PreGame(const beammp_fs_string& GamePath) { } #if defined(_WIN32) std::wstring ZipPath(GetGamePath() / LR"(mods\multiplayer\BeamMP.zip)"); -#elif defined(__linux__) - // Linux version of the game cant handle mods with uppercase names +#elif defined(__linux__) || defined(__APPLE__) + // Linux and macOS versions of the game cant handle mods with uppercase names std::string ZipPath(GetGamePath() / R"(mods/multiplayer/beammp.zip)"); #endif From 6b7b5a1574992e6db38f50a060acd0fd90c31032 Mon Sep 17 00:00:00 2001 From: Fournet Enzo Date: Fri, 9 Jan 2026 19:25:32 +0100 Subject: [PATCH 05/10] Simplify system clear command in ReLaunch Removed platform-specific preprocessor checks for the 'clear' system command in ReLaunch(). Now, 'system("clear")' is called unconditionally for all platforms. --- src/Startup.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Startup.cpp b/src/Startup.cpp index f28e5bcf..449dc90b 100644 --- a/src/Startup.cpp +++ b/src/Startup.cpp @@ -158,11 +158,7 @@ void ReLaunch() { Arg += " "; } info("Relaunch!"); -#if defined(__linux__) - system("clear"); -#elif defined(__APPLE__) system("clear"); -#endif int ret = execv((GetBP() / GetEN()).c_str(), const_cast(options.argv)); if (ret < 0) { error(std::string("execv() failed with: ") + strerror(errno) + ". Failed to relaunch"); From 832b2ae28cfe1fbb868c9d5f706bcfa7878a04da Mon Sep 17 00:00:00 2001 From: Fournet Enzo Date: Fri, 9 Jan 2026 19:28:23 +0100 Subject: [PATCH 06/10] Set macOS SDK and deployment target in CI workflow Adds steps to configure the SDKROOT and MACOSX_DEPLOYMENT_TARGET environment variables in the cmake-macos GitHub Actions workflow to ensure consistent build environment targeting macOS 11.0. --- .github/workflows/cmake-macos.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/cmake-macos.yml b/.github/workflows/cmake-macos.yml index 6efce071..0fb1e595 100644 --- a/.github/workflows/cmake-macos.yml +++ b/.github/workflows/cmake-macos.yml @@ -14,6 +14,11 @@ jobs: with: submodules: 'true' + - name: Configure macOS SDK + run: | + echo "SDKROOT=$(xcrun --show-sdk-path)" >> $GITHUB_ENV + echo "MACOSX_DEPLOYMENT_TARGET=11.0" >> $GITHUB_ENV + - name: Restore artifacts, or run vcpkg, build and cache artifacts uses: lukka/run-vcpkg@v7 id: runvcpkg From 249f30a6b9a8d43a90269e736c1920905d17ebea Mon Sep 17 00:00:00 2001 From: Fournet Enzo Date: Sun, 11 Jan 2026 12:45:24 +0100 Subject: [PATCH 07/10] Fix macOS game launch handling in InitGame and main Refactors InitGame to exclude its definition on macOS and updates main.cpp to only call InitGame when not on macOS. This prevents attempts to launch the game on macOS, where it is not supported. --- src/GameStart.cpp | 8 +++----- src/main.cpp | 4 +++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/GameStart.cpp b/src/GameStart.cpp index 58d57170..0cb6e454 100644 --- a/src/GameStart.cpp +++ b/src/GameStart.cpp @@ -222,14 +222,12 @@ void StartGame(std::string Dir) { } #endif +#if !defined(__APPLE__) void InitGame(const beammp_fs_string& Dir) { -#if defined(__APPLE__) - // macOS does not support game launching - return; -#else + if (!options.no_launch) { std::thread Game(StartGame, Dir); Game.detach(); } -#endif } +#endif diff --git a/src/main.cpp b/src/main.cpp index b2f90991..0cc859e4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -60,7 +60,9 @@ int main(int argc, const char** argv) try { error(std::string("Failed to start HTTP proxy: Some in-game functions may not work. Error: ") + e.what()); } PreGame(GetGameDir()); - InitGame(GetGameDir()); + if (!defined(__APPLE__)) { + InitGame(GetGameDir()); + } CoreNetwork(); } catch (const std::exception& e) { error(std::string("Exception in main(): ") + e.what()); From c3972f493b23820a60b1a174da0fae597a1f02e9 Mon Sep 17 00:00:00 2001 From: Fournet Enzo Date: Sun, 11 Jan 2026 12:47:50 +0100 Subject: [PATCH 08/10] Remove unnecessary blank line in InitGame --- src/GameStart.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/GameStart.cpp b/src/GameStart.cpp index 0cb6e454..09802464 100644 --- a/src/GameStart.cpp +++ b/src/GameStart.cpp @@ -224,7 +224,6 @@ void StartGame(std::string Dir) { #if !defined(__APPLE__) void InitGame(const beammp_fs_string& Dir) { - if (!options.no_launch) { std::thread Game(StartGame, Dir); Game.detach(); From 22edfca1fd2b3770efa4659f0cb5498d5b911a40 Mon Sep 17 00:00:00 2001 From: Fournet Enzo Date: Sun, 11 Jan 2026 12:49:17 +0100 Subject: [PATCH 09/10] Remove unused StartGame implementation for macOS Deleted the macOS-specific StartGame function, as game launching is not supported and the function is never called on that platform. --- src/GameStart.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/GameStart.cpp b/src/GameStart.cpp index 09802464..153e9d06 100644 --- a/src/GameStart.cpp +++ b/src/GameStart.cpp @@ -214,12 +214,6 @@ void StartGame(std::string Dir) { std::this_thread::sleep_for(std::chrono::seconds(5)); exit(2); } -#elif defined(__APPLE__) -// macOS does not support game launching - StartGame is never called -void StartGame(std::string Dir) { - // This function should never be called on macOS - error("Game launching is not supported on macOS"); -} #endif #if !defined(__APPLE__) From 76da51872e2cc53912c3763890b2ea8585c51813 Mon Sep 17 00:00:00 2001 From: Fournet Enzo Date: Sun, 11 Jan 2026 12:54:15 +0100 Subject: [PATCH 10/10] Fix platform check for InitGame on non-Apple systems Replaces incorrect use of 'defined' with preprocessor directive '#if !defined(__APPLE__)' to ensure InitGame is only called on non-Apple platforms. --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 0cc859e4..18f8a9a6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -60,9 +60,9 @@ int main(int argc, const char** argv) try { error(std::string("Failed to start HTTP proxy: Some in-game functions may not work. Error: ") + e.what()); } PreGame(GetGameDir()); - if (!defined(__APPLE__)) { + #if !defined(__APPLE__) InitGame(GetGameDir()); - } + #endif CoreNetwork(); } catch (const std::exception& e) { error(std::string("Exception in main(): ") + e.what());