diff --git a/.github/workflows/cmake-macos.yml b/.github/workflows/cmake-macos.yml new file mode 100644 index 00000000..0fb1e595 --- /dev/null +++ b/.github/workflows/cmake-macos.yml @@ -0,0 +1,51 @@ +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: 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 + 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 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/ + 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 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..153e9d06 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) @@ -204,9 +216,11 @@ void StartGame(std::string Dir) { } #endif +#if !defined(__APPLE__) void InitGame(const beammp_fs_string& Dir) { 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..449dc90b 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++) { @@ -181,7 +181,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 +280,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 +367,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 diff --git a/src/main.cpp b/src/main.cpp index b2f90991..18f8a9a6 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()); + #endif CoreNetwork(); } catch (const std::exception& e) { error(std::string("Exception in main(): ") + e.what());