133 Commits

Author SHA1 Message Date
Lion
cc4e322065 Merge pull request #88 from saile515/fix-input-linux
Fix input not working on Linux
2024-05-29 11:13:05 +02:00
saile515
915c05c57c fix input not working on linux 2024-05-29 10:56:22 +02:00
gamingdoom
35ad09dd5f Remove extra newline 2023-10-17 21:01:38 -07:00
gamingdoom
8ed2921ec1 remove .vscode 2023-10-17 20:41:15 -07:00
gamingdoom
1bf7faa34d fix guest account issue and uppercase mods 2023-10-17 20:40:11 -07:00
gamingdoom
8ead91c527 Update release-build.yml 2023-10-17 19:31:40 -07:00
gamingdoom
5e602a651c Fix linux build artifact path 2023-10-17 19:18:48 -07:00
gamingdoom
0993b2a463 fix linux build name ... again 2023-10-17 19:16:17 -07:00
gamingdoom
73ada2b541 fix linux build name 2023-10-17 19:15:44 -07:00
gamingdoom
f9bd0967bc Add github actions Linux build 2023-10-17 19:14:54 -07:00
gamingdoom
16b13c074e remove debug symbols 2023-10-17 19:13:16 -07:00
gamingdoom
0a9a883ca4 fix windows build 2023-10-17 18:59:49 -07:00
gamingdoom
a91735531a linux build 2023-10-17 18:53:01 -07:00
gamingdoom
f98ff3f502 Update cmake-windows.yml 2023-10-17 18:38:59 -07:00
gamingdoom
5dfb5f3b88 works but linux build is broken and this is an old version of the source 2023-10-16 22:13:30 -07:00
gamingdoom
54e1beb548 rename network.h to network.hpp 2023-10-16 17:33:52 -07:00
Anonymous275
839bb48cd6 Merge pull request #58 from WhiteHusky/patch-1
Emit a useful message if cleaning the mods folder fails
2023-08-04 20:30:36 +01:00
Carlen White
c4ebdda1a4 Emit a useful message if cleaning the mods folder fails
The launcher complains if it can not delete a file when it's trying to clean the mods folder out. However the message it emits is unhelpful and does not offer a suggestion to what is going on.

This commit addresses this issue by telling the user what the launcher is trying to do and suggests checking if something is currently open in that directory. I figure not having to go into detail in *where* the folder is not necessary and (primarily) only happens if the user is inspecting the folder themselves and already knows where the folder is.
2023-07-02 23:02:14 -04:00
Anonymous275
d6b494c6c4 - release v2.0.82 2022-12-18 14:30:21 +00:00
Anonymous275
a80d4f5147 - quick fix for launcher crash 2022-12-18 14:29:38 +00:00
Anonymous275
811b04485c - try fix for github workflow 2022-12-18 13:20:20 +00:00
Simon Abed El Sater
a64fead653 update 2.0.81 2022-12-18 14:39:54 +02:00
Simon Abed El Sater
399461d1b1 remove version check 2022-12-18 14:39:28 +02:00
Anonymous275
ec5e8ed5b3 v2.0.80 2022-09-25 20:39:27 +03:00
Anonymous275
5655164e60 bump 2.0.80 2022-09-25 20:37:46 +03:00
Simon Abed El Sater
3d9b7c2d67 Merge pull request #49 from snepsnepsnep/master
Fix kick message for server 3.0+
2022-09-25 20:32:50 +03:00
snepsnepsnep
764e3ab5c1 Fix kick message for server 3.0+ 2022-09-25 19:26:46 +02:00
Simon Abed El Sater
3314362faf Merge pull request #48 from Mack29446/master
Decided by vote: Update BeamNG Version and remove Mod Delete warning & Delay
2022-09-25 01:18:58 +03:00
Mackenzie
e483f520db Bump launcher version
2.0.78 -> 2.0.79
2022-09-24 22:49:10 +01:00
Mackenzie
c92e32c0e1 Remove mod wipe warn and delay 2022-09-24 22:48:17 +01:00
Mackenzie
cb872f8a41 Update BeamNG Version 2022-09-24 22:46:38 +01:00
Anonymous275
0aae245054 release 2.0.78 2022-09-24 00:37:29 +03:00
Anonymous275
480a7d038f follow redirects if present 2022-09-24 00:37:04 +03:00
Anonymous275
f62f44d4c0 version 2.0.77 2022-09-20 22:27:26 +03:00
Anonymous275
e316b89fb1 bump 0.26 2022-09-20 22:26:51 +03:00
Anonymous275
3b2dbcac1b release 2.0.76 2022-09-05 03:23:15 +03:00
Anonymous275
d881c9faf6 release 2.0.75 2022-09-05 02:59:05 +03:00
Anonymous275
11d9375f36 fix invalid Key being used 2022-09-05 02:58:22 +03:00
Simon Abed El Sater
4207d7adcf Merge pull request #44 from snepsnepsnep/master
remove key if auth is unsuccessful
2022-09-05 02:40:50 +03:00
snepsnepsnep
832b1d66a0 remove key if auth is unsuccessful 2022-09-04 20:24:27 +02:00
Anonymous275
cd829f9f22 Update Startup.cpp
version 2.0.74
2022-08-12 12:14:31 +03:00
Anonymous275
f7c70eb6df Merge pull request #36 from Mack29446/master
Change serverlist request URL and Protocol
2022-08-12 12:11:22 +03:00
Mackenzie
01960f6470 Change serverlist request URL and Protocol 2022-08-11 20:27:26 +01:00
Anonymous275
f6065a1c00 Merge pull request #21 from Mack29446/master
Update version
2022-06-24 19:46:26 +03:00
Mackenzie
ba3b7f0ed0 Update version 2022-06-23 16:53:06 +01:00
Anonymous275
056eadbef2 Merge pull request #20 from Mack29446/master
Update version
2022-06-21 18:12:49 +03:00
Mackenzie
2bb2dc9040 Update version 2022-06-21 14:28:10 +01:00
Anonymous275
17553fd412 bump launcher version 2022-06-19 02:28:26 +03:00
Anonymous275
08c1c0f682 Merge pull request #19 from Mack29446/master
Update Version Number
2022-06-18 02:02:36 +03:00
Mackenzie
84959ae9c9 Update Version Number 2022-06-17 21:35:00 +01:00
Anonymous275
c90c102097 trying to fix github actions 2022-06-15 21:38:45 +03:00
Anonymous275
5b004426ce vcpkgGitCommitId update 2022-06-15 21:35:12 +03:00
Anonymous275
69c9060dd2 Update release-build.yml 2022-06-15 21:13:02 +03:00
Anonymous275
49870639ff Update cmake-windows.yml 2022-06-15 21:12:41 +03:00
Anonymous275
0843862af9 update for game version 0.25 2022-06-15 21:02:43 +03:00
Anonymous275
71a9a567bc 2.0.63 2021-12-03 18:45:36 +02:00
Anonymous275
a6e3d0fad9 Update cmake-windows.yml 2021-12-03 18:28:03 +02:00
Anonymous275
411d0786a5 0.24.0.1 game version bump 2021-12-03 18:18:57 +02:00
Anonymous-275
acb6b11e24 bump minor version to 62 2021-10-01 00:17:55 +03:00
Anonymous-275
1739393a73 launcher no longer extracts zip file 2021-10-01 00:11:46 +03:00
Anonymous-275
298ef33ab7 V2.0.61 2021-07-03 17:11:56 +03:00
Anonymous-275
d2433cceca V2.0.7 2021-07-03 17:10:55 +03:00
Anonymous-275
705e0ab9c4 Updated version to 0.23.1 2021-07-03 17:09:29 +03:00
Anonymous-275
53c40a2bc3 Removed debug prints 2021-07-01 18:54:36 +03:00
Anonymous-275
96c60ef05a 0.23.0.0 support 2021-07-01 18:54:06 +03:00
Anonymous-275
61759b8531 0.23.0.0 support 2021-07-01 18:53:50 +03:00
Anonymous-275
691be8cd08 Mod validity check 2021-06-02 00:02:50 +03:00
Anonymous-275
082445c295 updated gitignore 2021-05-23 20:04:34 +03:00
Anonymous-275
f4bda81be0 Added 'N' packet flag 2021-05-23 19:22:44 +03:00
Anonymous-275
1ad8e0b8e5 Minor edit 2021-05-11 22:32:46 +03:00
Anonymous-275
6c673e78e5 Fixed invalid string position, Bumped game version 2021-05-11 22:19:42 +03:00
Anonymous-275
09abe75fbb Fixed false detection of proton 2021-04-15 21:05:30 +03:00
Anonymous-275
b45e4b40f2 Auto proton patch, fix for 0.22.2.0 2021-04-15 18:22:54 +03:00
Anonymous-275
15d1539a92 v2.0.2 2021-04-04 23:42:29 +03:00
Anonymous-275
14a5f47549 Merge branch 'master' of ssh://github.com/BeamMP/BeamMP-Launcher 2021-04-04 23:41:23 +03:00
Anonymous-275
584998277d v2.0.2 2021-04-04 23:40:57 +03:00
Anonymous275
fb59b6997c Create release-build.yml 2021-04-04 23:37:32 +03:00
Anonymous275
a1f5ec9ba5 Update cmake-windows.yml 2021-04-04 23:23:11 +03:00
Anonymous-275
989ac981be Merge branch 'master' of ssh://github.com/BeamMP/BeamMP-Launcher 2021-04-04 23:22:02 +03:00
Anonymous-275
d425b8035a Update CMakeLists.txt 2021-04-04 23:22:00 +03:00
Anonymous275
f0540c2df5 Update cmake-windows.yml 2021-04-04 23:08:07 +03:00
Anonymous275
9df08025ad Create cmake-windows.yml 2021-04-04 23:02:40 +03:00
Anonymous-275
bb732f102f v2.0.2 2021-04-04 22:49:52 +03:00
Anonymous-275
ec3bcffe7c New header only http library 2021-04-04 19:20:25 +03:00
Anonymous-275
e70cf0f877 Fixed launcher crash in wine! 2021-04-02 03:52:31 +03:00
Anonymous-275
b40ab5e36a V2.0.1 2021-03-31 23:18:09 +03:00
Anonymous-275
54a27f5c4c Bump to 0.22.1.0 2021-03-31 22:57:05 +03:00
Anonymous-275
37cfec9fdc Added chat to reliable 2021-03-30 23:31:11 +03:00
Anonymous-275
16f0769f87 fixed new custom profile directory 2021-03-30 03:07:49 +03:00
Starystars67
9f1e58bb70 Launcher v2 updated strings 2021-03-29 22:15:50 +01:00
Anonymous-275
83b43d782d Small tweak 2021-03-30 00:03:43 +03:00
Anonymous-275
3a0b96da52 Client mod download system update 2021-03-30 00:01:20 +03:00
Anonymous-275
aa2b07b359 Updated Supported version 2021-03-29 23:34:49 +03:00
Anonymous-275
289402a9ba Switched to new default userdata 2021-03-29 23:29:03 +03:00
Anonymous-275
1afa81e420 updated submodule 2021-03-29 21:45:32 +03:00
Starystars67
ae6725ece3 Possible fix for new backend linix ~Anonymous275 2021-03-29 19:00:23 +01:00
Anonymous-275
fff747afca Replaced cURL with evpp 2021-03-29 16:43:17 +03:00
Anonymous-275
5c1b7b1dbf updated submodule 2021-03-29 16:13:00 +03:00
Anonymous-275
e290910312 updated submodule 2021-03-29 12:44:48 +03:00
Anonymous-275
fb7831d629 Added evpp with git link 2021-03-29 11:57:58 +03:00
Anonymous-275
5b4b930701 Removed broken module link 2021-03-29 11:52:03 +03:00
Anonymous-275
fe59083257 Added evpp 2021-03-29 11:47:49 +03:00
Anonymous-275
fbc5e28d25 Mingw Compatible 2021-03-29 11:43:24 +03:00
Anonymous275
d96f968dde Cleanup 2021-02-22 23:00:54 +02:00
Anonymous275
ba5ba4b8b4 Partial fix for directory startup location 2021-02-22 20:09:36 +02:00
Anonymous275
263b6c9c0d Potential auth fix 2021-01-07 15:28:51 +02:00
Anonymous275
78875c2c9c Fixed single player, fixed duplicated multiplayer mods 2021-01-02 20:58:23 +02:00
Anonymous275
8e55edaa29 1.80.92, fixed some startup crashes 2021-01-02 02:02:50 +02:00
Anonymous275
c77f8742b4 Update BeamNG.cpp 2021-01-01 16:40:37 +02:00
Anonymous275
0a58001b09 Update BeamNG.cpp 2021-01-01 16:39:31 +02:00
Anonymous275
075d4a8c4d 1.80.91 2021-01-01 16:36:44 +02:00
Anonymous275
00313e32f9 Merge pull request #3 from finicu212/patch-1
Close #2: Alert user of what malicious file has been found
2021-01-01 16:34:45 +02:00
Anonymous275
16ea84b4cf Merge branch 'master' into patch-1 2021-01-01 16:34:27 +02:00
Anonymous275
6b124e89a8 Update BeamNG.cpp 2020-12-28 18:07:04 +02:00
Anonymous275
543183b852 launcher is now more linux friendly 2020-12-28 18:05:32 +02:00
Anonymous275
fda7567044 fixed crash -report 2020-12-28 16:34:24 +02:00
Anonymous275
6b4211d9cf bump to 1.80.8 2020-12-27 23:34:19 +02:00
Anonymous275
3572a188d1 added try catch 2020-12-27 23:21:41 +02:00
Anonymous275
775e44a68f fixed crash when starting up 2020-12-27 23:19:24 +02:00
Anonymous275
eaa8796c02 final hotfix 2020-12-27 06:34:11 +02:00
Anonymous275
2a03448b3f Fix for custom document location 2020-12-27 06:31:03 +02:00
Anonymous275
9734a7f3d5 Hotfix 2020-12-27 05:50:51 +02:00
Anonymous275
e207f2febd Potential default dir fix 2020-12-27 05:14:02 +02:00
Anonymous275
9f821a01f0 Mod downloading hotfix 2020-12-27 00:23:58 +02:00
Anonymous275
9a04665c34 Unicode path support 2020-12-26 23:35:43 +02:00
Anonymous275
ab2a58bf42 switched to normal filesystem + crash fixes 2020-12-26 22:52:01 +02:00
finicu
32bcbac979 Alert user of what malicious file has been found 2020-12-26 13:34:09 +02:00
Anonymous275
757ac6303b Update README.md 2020-12-23 21:03:05 +02:00
Anonymous275
20ba2b9e0a Update README.md 2020-12-23 20:47:23 +02:00
Anonymous275
99006627a4 Update README.md 2020-12-23 20:46:17 +02:00
Anonymous275
7cd25fbb22 fixed mod downloading stuck at 50% 2020-12-22 19:38:01 +02:00
Anonymous275
76a1b05056 Update Login.cpp 2020-12-22 17:19:02 +02:00
Anonymous275
b7ffa153b1 Added more error returns on auth fail 2020-12-22 17:12:38 +02:00
76 changed files with 15606 additions and 859 deletions

34
.github/workflows/cmake-linux.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: CMake Linux Build
on: [push, pull_request, workflow_dispatch]
env:
BUILD_TYPE: Release
jobs:
linux-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: 'true'
- name: Create Build Environment
run: cmake -E make_directory ${{github.workspace}}/build-linux
- name: Configure CMake
shell: bash
working-directory: ${{github.workspace}}/build-linux
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE
- name: Build
working-directory: ${{github.workspace}}/build-linux
shell: bash
run: cmake --build . --config $BUILD_TYPE
- name: Archive artifacts
uses: actions/upload-artifact@v2
with:
name: BeamMP-Launcher
path: ${{github.workspace}}/build-linux/BeamMP-Launcher

43
.github/workflows/cmake-windows.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: CMake Windows Build
on: [push, pull_request, workflow_dispatch]
env:
BUILD_TYPE: Release
jobs:
windows-build:
runs-on: windows-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: 'discord-rpc zlib rapidjson openssl'
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
vcpkgGitCommitId: '06b5f4a769d848d1a20fa0acd556019728b56273'
vcpkgTriplet: 'x64-windows-static'
- name: Create Build Environment
run: cmake -E make_directory ${{github.workspace}}/build-windows
- name: Configure CMake
shell: bash
working-directory: ${{github.workspace}}/build-windows
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE='${{ runner.workspace }}/b/vcpkg/scripts/buildsystems/vcpkg.cmake' -DVCPKG_TARGET_TRIPLET=x64-windows-static
- name: Build
working-directory: ${{github.workspace}}/build-windows
shell: bash
run: cmake --build . --config $BUILD_TYPE
- name: Archive artifacts
uses: actions/upload-artifact@v2
with:
name: BeamMP-Launcher.exe
path: ${{github.workspace}}/build-windows/Release/BeamMP-Launcher.exe

104
.github/workflows/release-build.yml vendored Normal file
View File

@@ -0,0 +1,104 @@
name: Release Create & Build
on:
push:
# Sequence of patterns matched against refs/tags
tags:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
env:
BUILD_TYPE: Release
jobs:
create-release:
runs-on: ubuntu-latest
name: Create Release
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
steps:
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
body: |
Files included in this release:
- `BeamMP-Launcher.exe` windows build
- `BeamMP-Launcher` linux build
upload-release-files-windows:
name: Upload Windows Release Files
runs-on: windows-latest
needs: create-release
steps:
- uses: actions/checkout@v2
with:
submodules: 'true'
- name: Restore artifacts, or run vcpkg, build and cache artifacts
uses: lukka/run-vcpkg@main
id: runvcpkg
with:
vcpkgArguments: 'discord-rpc zlib rapidjson openssl'
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
vcpkgGitCommitId: '06b5f4a769d848d1a20fa0acd556019728b56273'
vcpkgTriplet: 'x64-windows-static'
- name: Create Build Environment
run: cmake -E make_directory ${{github.workspace}}/build-windows
- name: Configure CMake
shell: bash
working-directory: ${{github.workspace}}/build-windows
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE='${{ runner.workspace }}/b/vcpkg/scripts/buildsystems/vcpkg.cmake' -DVCPKG_TARGET_TRIPLET=x64-windows-static
- name: Build
working-directory: ${{github.workspace}}/build-windows
shell: bash
run: cmake --build . --config $BUILD_TYPE
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: ${{github.workspace}}/build-windows/Release/BeamMP-Launcher.exe
asset_name: BeamMP-Launcher.exe
asset_content_type: application/vnd.microsoft.portable-executable
upload-release-files-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: 'true'
- name: Create Build Environment
run: cmake -E make_directory ${{github.workspace}}/build-linux
- name: Configure CMake
shell: bash
working-directory: ${{github.workspace}}/build-linux
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE
- name: Build
working-directory: ${{github.workspace}}/build-linux
shell: bash
run: cmake --build . --config $BUILD_TYPE
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: ${{github.workspace}}/build-linux/BeamMP-Launcher
asset_name: BeamMP-Launcher
asset_content_type: application/octet-stream

16
.gitignore vendored
View File

@@ -1,13 +1,7 @@
cmake-build-debug/CMakeFiles/ cmake-build-debug
cmake-build-release/CMakeFiles/ cmake-build-release
cmake-build-debug/BeamNG/
cmake-build-release/BeamNG/
cmake-build-debug/Resources/
cmake-build-release/Resources/
cmake-build-debug/*.*
cmake-build-release/*.*
cmake-build-debug/Makefile
cmake-build-release/Makefile
/.idea/ /.idea/
*.log *.log
cmake-build-release/key /*.sh
/*.obj
/*.exe

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "evpp"]
path = evpp
url = https://github.com/BeamMP/evpp.git

View File

@@ -1,12 +1,38 @@
cmake_minimum_required(VERSION 3.10) cmake_minimum_required(VERSION 3.10)
project(Launcher) project(Launcher)
if (WIN32)
message(STATUS "MSVC -> forcing use of statically-linked runtime.")
STRING(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
STRING(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
endif(WIN32)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
file(GLOB source_files "src/*.cpp" "src/*/*.cpp" "src/*/*.hpp" "include/*.h" "include/*/*.h" "include/*/*/*.h") file(GLOB source_files "src/*.cpp" "src/*/*.cpp" "src/*/*.hpp" "include/*.h" "include/*/*.h" "include/*/*/*.h" "include/*.hpp" "include/*/*.hpp" "include/*/*/*.hpp")
add_executable(${PROJECT_NAME} ${source_files}) add_executable(${PROJECT_NAME} ${source_files})
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "BeamMP-Launcher") set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "BeamMP-Launcher")
find_package(ZLIB REQUIRED)
find_package(CURL CONFIG REQUIRED) if (WIN32)
target_link_libraries(${PROJECT_NAME} PRIVATE ws2_32 rstrtmgr discord-rpc CURL::libcurl ZLIB::ZLIB) find_package(ZLIB REQUIRED)
target_include_directories(${PROJECT_NAME} PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>) find_package(OpenSSL REQUIRED)
#-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static
set(VcpkgRoot ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET})
include_directories(${VcpkgRoot}/include)
link_directories(${VcpkgRoot}/lib)
target_link_libraries(${PROJECT_NAME} PRIVATE ${VcpkgRoot}/lib/discord-rpc.lib
ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto ws2_32)
elseif (LINUX)
find_package(ZLIB REQUIRED)
find_package(OpenSSL REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE
ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto)
else(WIN32) #MINGW
add_definitions("-D_WIN32_WINNT=0x0600")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os -s --static")
target_link_libraries(${PROJECT_NAME} discord-rpc ssl crypto ws2_32 ssp crypt32 z)
endif(WIN32)
target_include_directories(${PROJECT_NAME} PRIVATE "include")

11
README.md Normal file → Executable file
View File

@@ -1 +1,10 @@
# BeamMP-Launcher # BeamMP-Launcher
The launcher is the way we communitcate to outside the game, it does a few automated actions such as but not limited to: downloading the mod, launching the game, and create a connection to a server.
Copyright (c) 2019-present Anonymous275.
BeamMP Launcher code is not in the public domain and is not free software.
One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries,
the only permission that has been granted is to use the software in its compiled form as distributed from the BeamMP.com website.
Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.

1
evpp Submodule

Submodule evpp added at 44e10774e2

0
include/Discord/discord_info.h Normal file → Executable file
View File

View File

@@ -1,5 +1,5 @@
#pragma once #pragma once
#include <stdint.h> #include <cstdint>
// clang-format off // clang-format off

14
include/Curl/http.h → include/Http.h Normal file → Executable file
View File

@@ -7,6 +7,14 @@
/// ///
#pragma once #pragma once
#include <string> #include <string>
int Download(const std::string& URL,const std::string& Path,bool close); #include "Logger.h"
std::string PostHTTP(const std::string& IP, const std::string& Fields); class HTTP {
std::string HTTP_REQUEST(const std::string& IP,int port); public:
static bool Download(const std::string &IP, const std::string &Path);
static std::string Post(const std::string& IP, const std::string& Fields);
static std::string Get(const std::string &IP);
static bool ProgressBar(size_t c, size_t t);
public:
static bool isDownload;
static std::string Codes_[];
};

22
include/Json.h Normal file → Executable file
View File

@@ -1,12 +1,12 @@
// Copyright (c) 2019-present Anonymous275. // Copyright (c) 2019-present Anonymous275.
// BeamMP Launcher code is not in the public domain and is not free software. // BeamMP Launcher code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. // One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. // Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
/// ///
/// Created by Anonymous275 on 11/27/2020 /// Created by Anonymous275 on 11/27/2020
/// ///
#pragma once #pragma once
#include "rapidjson/stringbuffer.h" #include "rapidjson/stringbuffer.h"
#include "rapidjson/document.h" #include "rapidjson/document.h"
#include "rapidjson/writer.h" #include "rapidjson/writer.h"
namespace json = rapidjson; namespace json = rapidjson;

0
include/Logger.h Normal file → Executable file
View File

View File

@@ -5,12 +5,25 @@
/// ///
/// Created by Anonymous275 on 7/18/2020 /// Created by Anonymous275 on 7/18/2020
/// ///
#pragma once #pragma once
#include <string> #include <string>
#ifdef __linux__
#include <cstdint>
#include "linuxfixes.h"
#include <bits/types/siginfo_t.h>
#include <sys/ucontext.h>
#endif
void NetReset(); void NetReset();
extern bool Dev; extern bool Dev;
extern int ping; extern int ping;
void CoreNetwork();
[[noreturn]] void CoreNetwork();
extern int ClientID; extern int ClientID;
extern int LastPort; extern int LastPort;
extern bool ModLoaded; extern bool ModLoaded;
@@ -18,6 +31,7 @@ extern bool Terminate;
extern int DEFAULT_PORT; extern int DEFAULT_PORT;
extern uint64_t UDPSock; extern uint64_t UDPSock;
extern uint64_t TCPSock; extern uint64_t TCPSock;
extern std::string Branch;
extern bool TCPTerminate; extern bool TCPTerminate;
extern std::string LastIP; extern std::string LastIP;
extern std::string MStatus; extern std::string MStatus;

0
include/Security/Game.h Normal file → Executable file
View File

2
include/Security/Init.h Normal file → Executable file
View File

@@ -7,7 +7,7 @@
/// ///
#pragma once #pragma once
#include <string> #include <string>
void PreGame(int argc, char* argv[],const std::string& GamePath); void PreGame(const std::string& GamePath);
std::string CheckVer(const std::string &path); std::string CheckVer(const std::string &path);
void InitGame(const std::string& Dir); void InitGame(const std::string& Dir);
std::string GetGameDir(); std::string GetGameDir();

3
include/Startup.h Normal file → Executable file
View File

@@ -8,8 +8,9 @@
#pragma once #pragma once
#include <string> #include <string>
void InitLauncher(int argc, char* argv[]); void InitLauncher(int argc, char* argv[]);
void CheckDir(int argc,char* args[]); std::string GetEP(char*P = nullptr);
std::string GetGamePath(); std::string GetGamePath();
std::string GetVer(); std::string GetVer();
std::string GetEN(); std::string GetEN();
void ConfigInit();
extern bool Dev; extern bool Dev;

0
include/Zlib/Compressor.h Normal file → Executable file
View File

7578
include/httplib.h Normal file

File diff suppressed because it is too large Load Diff

19
include/linuxfixes.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef _LINUXFIXES_H
#define _LINUXFIXES_H
#include <stdint.h>
// Translate windows sockets stuff to linux sockets
#define SOCKET uint64_t
#define SOCKADDR sockaddr
#define SOCKADDR_IN sockaddr_in
#define WSAGetLastError() errno
#define closesocket close
#define SD_BOTH SHUT_RDWR
// We dont need wsacleanup
#define WSACleanup()
#define SOCKET_ERROR -1
#define ZeroMemory(mem, len) memset(mem, 0, len)
#endif

0
include/rapidjson/allocators.h Normal file → Executable file
View File

0
include/rapidjson/cursorstreamwrapper.h Normal file → Executable file
View File

0
include/rapidjson/document.h Normal file → Executable file
View File

0
include/rapidjson/encodedstream.h Normal file → Executable file
View File

0
include/rapidjson/encodings.h Normal file → Executable file
View File

0
include/rapidjson/error/en.h Normal file → Executable file
View File

0
include/rapidjson/error/error.h Normal file → Executable file
View File

0
include/rapidjson/filereadstream.h Normal file → Executable file
View File

0
include/rapidjson/filewritestream.h Normal file → Executable file
View File

0
include/rapidjson/fwd.h Normal file → Executable file
View File

0
include/rapidjson/internal/biginteger.h Normal file → Executable file
View File

0
include/rapidjson/internal/clzll.h Normal file → Executable file
View File

0
include/rapidjson/internal/diyfp.h Normal file → Executable file
View File

0
include/rapidjson/internal/dtoa.h Normal file → Executable file
View File

0
include/rapidjson/internal/ieee754.h Normal file → Executable file
View File

0
include/rapidjson/internal/itoa.h Normal file → Executable file
View File

0
include/rapidjson/internal/meta.h Normal file → Executable file
View File

0
include/rapidjson/internal/pow10.h Normal file → Executable file
View File

0
include/rapidjson/internal/regex.h Normal file → Executable file
View File

0
include/rapidjson/internal/stack.h Normal file → Executable file
View File

0
include/rapidjson/internal/strfunc.h Normal file → Executable file
View File

0
include/rapidjson/internal/strtod.h Normal file → Executable file
View File

0
include/rapidjson/internal/swap.h Normal file → Executable file
View File

0
include/rapidjson/istreamwrapper.h Normal file → Executable file
View File

0
include/rapidjson/memorybuffer.h Normal file → Executable file
View File

0
include/rapidjson/memorystream.h Normal file → Executable file
View File

0
include/rapidjson/msinttypes/inttypes.h Normal file → Executable file
View File

0
include/rapidjson/msinttypes/stdint.h Normal file → Executable file
View File

0
include/rapidjson/ostreamwrapper.h Normal file → Executable file
View File

0
include/rapidjson/pointer.h Normal file → Executable file
View File

0
include/rapidjson/prettywriter.h Normal file → Executable file
View File

0
include/rapidjson/rapidjson.h Normal file → Executable file
View File

0
include/rapidjson/reader.h Normal file → Executable file
View File

0
include/rapidjson/schema.h Normal file → Executable file
View File

0
include/rapidjson/stream.h Normal file → Executable file
View File

0
include/rapidjson/stringbuffer.h Normal file → Executable file
View File

0
include/rapidjson/writer.h Normal file → Executable file
View File

730
include/vdf_parser.hpp Normal file
View File

@@ -0,0 +1,730 @@
//MIT License
//
//Copyright(c) 2016 Matthias Moeller
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files(the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions :
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef __TYTI_STEAM_VDF_PARSER_H__
#define __TYTI_STEAM_VDF_PARSER_H__
#include <map>
#include <vector>
#include <unordered_map>
#include <utility>
#include <fstream>
#include <memory>
#include <unordered_set>
#include <algorithm>
#include <iterator>
#include <functional>
#include <system_error>
#include <exception>
//for wstring support
#include <locale>
#include <string>
// internal
#include <stack>
//VS < 2015 has only partial C++11 support
#if defined(_MSC_VER) && _MSC_VER < 1900
#ifndef CONSTEXPR
#define CONSTEXPR
#endif
#ifndef NOEXCEPT
#define NOEXCEPT
#endif
#else
#ifndef CONSTEXPR
#define CONSTEXPR constexpr
#define TYTI_UNDEF_CONSTEXPR
#endif
#ifndef NOEXCEPT
#define NOEXCEPT noexcept
#define TYTI_UNDEF_NOEXCEPT
#endif
#endif
namespace tyti
{
namespace vdf
{
namespace detail
{
///////////////////////////////////////////////////////////////////////////
// Helper functions selecting the right encoding (char/wchar_T)
///////////////////////////////////////////////////////////////////////////
template <typename T>
struct literal_macro_help
{
static CONSTEXPR const char* result(const char* c, const wchar_t*) NOEXCEPT
{
return c;
}
static CONSTEXPR const char result(const char c, const wchar_t) NOEXCEPT
{
return c;
}
};
template <>
struct literal_macro_help<wchar_t>
{
static CONSTEXPR const wchar_t* result(const char*, const wchar_t* wc) NOEXCEPT
{
return wc;
}
static CONSTEXPR const wchar_t result(const char, const wchar_t wc) NOEXCEPT
{
return wc;
}
};
#define TYTI_L(type, text) vdf::detail::literal_macro_help<type>::result(text, L##text)
inline std::string string_converter(const std::string& w) NOEXCEPT
{
return w;
}
// utility wrapper to adapt locale-bound facets for wstring/wbuffer convert
// from cppreference
template <class Facet>
struct deletable_facet : Facet
{
template <class... Args>
deletable_facet(Args &&... args) : Facet(std::forward<Args>(args)...) {}
~deletable_facet() {}
};
inline std::string string_converter(const std::wstring& w) //todo: use us-locale
{
std::wstring_convert<deletable_facet<std::codecvt<wchar_t, char, std::mbstate_t>>> conv1;
return conv1.to_bytes(w);
}
///////////////////////////////////////////////////////////////////////////
// Writer helper functions
///////////////////////////////////////////////////////////////////////////
template <typename charT>
class tabs
{
const size_t t;
public:
explicit CONSTEXPR tabs(size_t i) NOEXCEPT : t(i) {}
std::basic_string<charT> print() const { return std::basic_string<charT>(t, TYTI_L(charT, '\t')); }
inline CONSTEXPR tabs operator+(size_t i) const NOEXCEPT
{
return tabs(t + i);
}
};
template <typename oStreamT>
oStreamT& operator<<(oStreamT& s, const tabs<typename oStreamT::char_type> t)
{
s << t.print();
return s;
}
} // end namespace detail
///////////////////////////////////////////////////////////////////////////
// Interface
///////////////////////////////////////////////////////////////////////////
/// custom objects and their corresponding write functions
/// basic object node. Every object has a name and can contains attributes saved as key_value pairs or childrens
template <typename CharT>
struct basic_object
{
typedef CharT char_type;
std::basic_string<char_type> name;
std::unordered_map<std::basic_string<char_type>, std::basic_string<char_type>> attribs;
std::unordered_map<std::basic_string<char_type>, std::shared_ptr<basic_object<char_type>>> childs;
void add_attribute(std::basic_string<char_type> key, std::basic_string<char_type> value)
{
attribs.emplace(std::move(key), std::move(value));
}
void add_child(std::unique_ptr<basic_object<char_type>> child)
{
std::shared_ptr<basic_object<char_type>> obj{ child.release() };
childs.emplace(obj->name, obj);
}
void set_name(std::basic_string<char_type> n)
{
name = std::move(n);
}
};
template <typename CharT>
struct basic_multikey_object
{
typedef CharT char_type;
std::basic_string<char_type> name;
std::unordered_multimap<std::basic_string<char_type>, std::basic_string<char_type>> attribs;
std::unordered_multimap<std::basic_string<char_type>, std::shared_ptr<basic_multikey_object<char_type>>> childs;
void add_attribute(std::basic_string<char_type> key, std::basic_string<char_type> value)
{
attribs.emplace(std::move(key), std::move(value));
}
void add_child(std::unique_ptr<basic_multikey_object<char_type>> child)
{
std::shared_ptr<basic_multikey_object<char_type>> obj{ child.release() };
childs.emplace(obj->name, obj);
}
void set_name(std::basic_string<char_type> n)
{
name = std::move(n);
}
};
typedef basic_object<char> object;
typedef basic_object<wchar_t> wobject;
typedef basic_multikey_object<char> multikey_object;
typedef basic_multikey_object<wchar_t> wmultikey_object;
struct Options
{
bool strip_escape_symbols;
bool ignore_all_platform_conditionals;
bool ignore_includes;
Options() : strip_escape_symbols(true), ignore_all_platform_conditionals(false), ignore_includes(false) {}
};
//forward decls
//forward decl
template <typename OutputT, typename iStreamT>
OutputT read(iStreamT& inStream, const Options& opt = Options{});
/** \brief writes given object tree in vdf format to given stream.
Output is prettyfied, using tabs
*/
template <typename oStreamT, typename T>
void write(oStreamT& s, const T& r,
const detail::tabs<typename oStreamT::char_type> tab = detail::tabs<typename oStreamT::char_type>(0))
{
typedef typename oStreamT::char_type charT;
using namespace detail;
s << tab << TYTI_L(charT, '"') << r.name << TYTI_L(charT, "\"\n") << tab << TYTI_L(charT, "{\n");
for (const auto& i : r.attribs)
s << tab + 1 << TYTI_L(charT, '"') << i.first << TYTI_L(charT, "\"\t\t\"") << i.second << TYTI_L(charT, "\"\n");
for (const auto& i : r.childs)
if (i.second)
write(s, *i.second, tab + 1);
s << tab << TYTI_L(charT, "}\n");
}
namespace detail
{
template <typename iStreamT>
std::basic_string<typename iStreamT::char_type> read_file(iStreamT& inStream)
{
// cache the file
typedef typename iStreamT::char_type charT;
std::basic_string<charT> str;
inStream.seekg(0, std::ios::end);
str.resize(static_cast<size_t>(inStream.tellg()));
if (str.empty())
return str;
inStream.seekg(0, std::ios::beg);
inStream.read(&str[0], str.size());
return str;
}
/** \brief Read VDF formatted sequences defined by the range [first, last).
If the file is mailformatted, parser will try to read it until it can.
@param first begin iterator
@param end end iterator
@param exclude_files list of files which cant be included anymore.
prevents circular includes
can thow:
- "std::runtime_error" if a parsing error occured
- "std::bad_alloc" if not enough memory coup be allocated
*/
template <typename OutputT, typename IterT>
std::vector<std::unique_ptr<OutputT>> read_internal(IterT first, const IterT last,
std::unordered_set<std::basic_string<typename std::iterator_traits<IterT>::value_type>>& exclude_files,
const Options& opt)
{
static_assert(std::is_default_constructible<OutputT>::value,
"Output Type must be default constructible (provide constructor without arguments)");
static_assert(std::is_move_constructible<OutputT>::value,
"Output Type must be move constructible");
typedef typename std::iterator_traits<IterT>::value_type charT;
const std::basic_string<charT> comment_end_str = TYTI_L(charT, "*/");
const std::basic_string<charT> whitespaces = TYTI_L(charT, " \n\v\f\r\t");
#ifdef WIN32
std::function<bool(const std::basic_string<charT>&)> is_platform_str = [](const std::basic_string<charT>& in) {
return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$WINDOWS");
};
#elif __APPLE__
// WIN32 stands for pc in general
std::function<bool(const std::basic_string<charT>&)> is_platform_str = [](const std::basic_string<charT>& in) {
return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") || in == TYTI_L(charT, "$OSX");
};
#elif __linux__
// WIN32 stands for pc in general
std::function<bool(const std::basic_string<charT>&)> is_platform_str = [](const std::basic_string<charT>& in) {
return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") || in == TYTI_L(charT, "$LINUX");
};
#else
std::function<bool(const std::basic_string<charT>&)> is_platform_str = [](const std::basic_string<charT>& in) {
return false;
};
#endif
if (opt.ignore_all_platform_conditionals)
is_platform_str = [](const std::basic_string<charT>&) {
return false;
};
// function for skipping a comment block
// iter: iterator poition to the position after a '/'
auto skip_comments = [&comment_end_str](IterT iter, const IterT& last) -> IterT {
++iter;
if (iter != last)
{
if (*iter == TYTI_L(charT, '/'))
{
// line comment, skip whole line
iter = std::find(iter + 1, last, TYTI_L(charT, '\n'));
}
if (*iter == '*')
{
// block comment, skip until next occurance of "*\"
iter = std::search(iter + 1, last, std::begin(comment_end_str), std::end(comment_end_str));
iter += 2;
}
}
return iter;
};
auto end_quote = [](IterT iter, const IterT& last) -> IterT {
const auto begin = iter;
auto last_esc = iter;
do
{
++iter;
iter = std::find(iter, last, TYTI_L(charT, '\"'));
if (iter == last)
break;
last_esc = std::prev(iter);
while (last_esc != begin && *last_esc == '\\')
--last_esc;
} while (!(std::distance(last_esc, iter) % 2));
if (iter == last)
throw std::runtime_error{ "quote was opened but not closed." };
return iter;
};
auto end_word = [&whitespaces](IterT iter, const IterT& last) -> IterT {
const auto begin = iter;
auto last_esc = iter;
do
{
++iter;
iter = std::find_first_of(iter, last, std::begin(whitespaces), std::end(whitespaces));
if (iter == last)
break;
last_esc = std::prev(iter);
while (last_esc != begin && *last_esc == '\\')
--last_esc;
} while (!(std::distance(last_esc, iter) % 2));
//if (iter == last)
// throw std::runtime_error{ "word wasnt properly ended" };
return iter;
};
auto skip_whitespaces = [&whitespaces](IterT iter, const IterT& last) -> IterT {
iter = std::find_if_not(iter, last, [&whitespaces](charT c) {
// return true if whitespace
return std::any_of(std::begin(whitespaces), std::end(whitespaces), [c](charT pc) { return pc == c; });
});
return iter;
};
std::function<void(std::basic_string<charT>&)> strip_escape_symbols = [](std::basic_string<charT>& s) {
auto quote_searcher = [&s](size_t pos) { return s.find(TYTI_L(charT, "\\\""), pos); };
auto p = quote_searcher(0);
while (p != s.npos)
{
s.replace(p, 2, TYTI_L(charT, "\""));
p = quote_searcher(p);
}
auto searcher = [&s](size_t pos) { return s.find(TYTI_L(charT, "\\\\"), pos); };
p = searcher(0);
while (p != s.npos)
{
s.replace(p, 2, TYTI_L(charT, "\\"));
p = searcher(p);
}
};
if (!opt.strip_escape_symbols)
strip_escape_symbols = [](std::basic_string<charT>&) {};
auto conditional_fullfilled = [&skip_whitespaces, &is_platform_str](IterT& iter, const IterT& last) {
iter = skip_whitespaces(iter, last);
if (*iter == '[')
{
++iter;
const auto end = std::find(iter, last, ']');
const bool negate = *iter == '!';
if (negate)
++iter;
auto conditional = std::basic_string<charT>(iter, end);
const bool is_platform = is_platform_str(conditional);
iter = end + 1;
return static_cast<bool>(is_platform ^ negate);
}
return true;
};
//read header
// first, quoted name
std::unique_ptr<OutputT> curObj = nullptr;
std::vector<std::unique_ptr<OutputT>> roots;
std::stack<std::unique_ptr<OutputT>> lvls;
auto curIter = first;
while (curIter != last && *curIter != '\0')
{
//find first starting attrib/child, or ending
curIter = skip_whitespaces(curIter, last);
if (curIter == last || *curIter == '\0')
break;
if (*curIter == TYTI_L(charT, '/'))
{
curIter = skip_comments(curIter, last);
}
else if (*curIter != TYTI_L(charT, '}'))
{
// get key
const auto keyEnd = (*curIter == TYTI_L(charT, '\"')) ? end_quote(curIter, last) : end_word(curIter, last);
if (*curIter == TYTI_L(charT, '\"'))
++curIter;
std::basic_string<charT> key(curIter, keyEnd);
strip_escape_symbols(key);
curIter = keyEnd + ((*keyEnd == TYTI_L(charT, '\"')) ? 1 : 0);
curIter = skip_whitespaces(curIter, last);
auto conditional = conditional_fullfilled(curIter, last);
if (!conditional)
continue;
while (*curIter == TYTI_L(charT, '/'))
{
curIter = skip_comments(curIter, last);
if (curIter == last || *curIter == '}')
throw std::runtime_error{ "key declared, but no value" };
curIter = skip_whitespaces(curIter, last);
if (curIter == last || *curIter == '}')
throw std::runtime_error{ "key declared, but no value" };
}
// get value
if (*curIter != '{')
{
const auto valueEnd = (*curIter == TYTI_L(charT, '\"')) ? end_quote(curIter, last) : end_word(curIter, last);
if (*curIter == TYTI_L(charT, '\"'))
++curIter;
auto value = std::basic_string<charT>(curIter, valueEnd);
strip_escape_symbols(value);
curIter = valueEnd + ((*valueEnd == TYTI_L(charT, '\"')) ? 1 : 0);
auto conditional = conditional_fullfilled(curIter, last);
if (!conditional)
continue;
// process value
if (key != TYTI_L(charT, "#include") && key != TYTI_L(charT, "#base"))
{
if (curObj)
{
curObj->add_attribute(std::move(key), std::move(value));
}
else
{
throw std::runtime_error{ "unexpected key without object" };
}
}
else
{
if (!opt.ignore_includes && exclude_files.find(value) == exclude_files.end())
{
exclude_files.insert(value);
std::basic_ifstream<charT> i(detail::string_converter(value));
auto str = read_file(i);
auto file_objs = read_internal<OutputT>(str.begin(), str.end(), exclude_files, opt);
for (auto& n : file_objs)
{
if (curObj)
curObj->add_child(std::move(n));
else
roots.push_back(std::move(n));
}
exclude_files.erase(value);
}
}
}
else if (*curIter == '{')
{
if (curObj)
lvls.push(std::move(curObj));
curObj = std::make_unique<OutputT>();
curObj->set_name(std::move(key));
++curIter;
}
}
//end of new object
else if (curObj && *curIter == TYTI_L(charT, '}'))
{
if (!lvls.empty())
{
//get object before
std::unique_ptr<OutputT> prev{ std::move(lvls.top()) };
lvls.pop();
// add finished obj to obj before and release it from processing
prev->add_child(std::move(curObj));
curObj = std::move(prev);
}
else
{
roots.push_back(std::move(curObj));
curObj.reset();
}
++curIter;
}
else
{
throw std::runtime_error{ "unexpected '}'" };
}
}
if (curObj != nullptr || !lvls.empty())
{
throw std::runtime_error{ "object is not closed with '}'" };
}
return roots;
}
} // namespace detail
/** \brief Read VDF formatted sequences defined by the range [first, last).
If the file is mailformatted, parser will try to read it until it can.
@param first begin iterator
@param end end iterator
can thow:
- "std::runtime_error" if a parsing error occured
- "std::bad_alloc" if not enough memory coup be allocated
*/
template <typename OutputT, typename IterT>
OutputT read(IterT first, const IterT last, const Options& opt = Options{})
{
auto exclude_files = std::unordered_set<std::basic_string<typename std::iterator_traits<IterT>::value_type>>{};
auto roots = detail::read_internal<OutputT>(first, last, exclude_files, opt);
OutputT result;
if (roots.size() > 1)
{
for (auto& i : roots)
result.add_child(std::move(i));
}
else if (roots.size() == 1)
result = std::move(*roots[0]);
return result;
}
/** \brief Read VDF formatted sequences defined by the range [first, last).
If the file is mailformatted, parser will try to read it until it can.
@param first begin iterator
@param end end iterator
@param ec output bool. 0 if ok, otherwise, holds an system error code
Possible error codes:
std::errc::protocol_error: file is mailformatted
std::errc::not_enough_memory: not enough space
std::errc::invalid_argument: iterators throws e.g. out of range
*/
template <typename OutputT, typename IterT>
OutputT read(IterT first, IterT last, std::error_code& ec, const Options& opt = Options{}) NOEXCEPT
{
ec.clear();
OutputT r{};
try
{
r = read<OutputT>(first, last, opt);
}
catch (std::runtime_error&)
{
ec = std::make_error_code(std::errc::protocol_error);
}
catch (std::bad_alloc&)
{
ec = std::make_error_code(std::errc::not_enough_memory);
}
catch (...)
{
ec = std::make_error_code(std::errc::invalid_argument);
}
return r;
}
/** \brief Read VDF formatted sequences defined by the range [first, last).
If the file is mailformatted, parser will try to read it until it can.
@param first begin iterator
@param end end iterator
@param ok output bool. true, if parser successed, false, if parser failed
*/
template <typename OutputT, typename IterT>
OutputT read(IterT first, const IterT last, bool* ok, const Options& opt = Options{}) NOEXCEPT
{
std::error_code ec;
auto r = read<OutputT>(first, last, ec, opt);
if (ok)
*ok = !ec;
return r;
}
template <typename IterT>
inline auto read(IterT first, const IterT last, bool* ok, const Options& opt = Options{}) NOEXCEPT -> basic_object<typename std::iterator_traits<IterT>::value_type>
{
return read<basic_object<typename std::iterator_traits<IterT>::value_type>>(first, last, ok, opt);
}
template <typename IterT>
inline auto read(IterT first, IterT last, std::error_code& ec, const Options& opt = Options{}) NOEXCEPT
-> basic_object<typename std::iterator_traits<IterT>::value_type>
{
return read<basic_object<typename std::iterator_traits<IterT>::value_type>>(first, last, ec, opt);
}
template <typename IterT>
inline auto read(IterT first, const IterT last, const Options& opt = Options{})
-> basic_object<typename std::iterator_traits<IterT>::value_type>
{
return read<basic_object<typename std::iterator_traits<IterT>::value_type>>(first, last, opt);
}
/** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data.
throws "std::bad_alloc" if file buffer could not be allocated
*/
template <typename OutputT, typename iStreamT>
OutputT read(iStreamT& inStream, std::error_code& ec, const Options& opt = Options{})
{
// cache the file
typedef typename iStreamT::char_type charT;
std::basic_string<charT> str = detail::read_file(inStream);
// parse it
return read<OutputT>(str.begin(), str.end(), ec, opt);
}
template <typename iStreamT>
inline basic_object<typename iStreamT::char_type> read(iStreamT& inStream, std::error_code& ec, const Options& opt = Options{})
{
return read<basic_object<typename iStreamT::char_type>>(inStream, ec, opt);
}
/** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data.
throws "std::bad_alloc" if file buffer could not be allocated
ok == false, if a parsing error occured
*/
template <typename OutputT, typename iStreamT>
OutputT read(iStreamT& inStream, bool* ok, const Options& opt = Options{})
{
std::error_code ec;
const auto r = read<OutputT>(inStream, ec, opt);
if (ok)
*ok = !ec;
return r;
}
template <typename iStreamT>
inline basic_object<typename iStreamT::char_type> read(iStreamT& inStream, bool* ok, const Options& opt = Options{})
{
return read<basic_object<typename iStreamT::char_type>>(inStream, ok, opt);
}
/** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data.
throws "std::bad_alloc" if file buffer could not be allocated
throws "std::runtime_error" if a parsing error occured
*/
template <typename OutputT, typename iStreamT>
OutputT read(iStreamT& inStream, const Options& opt)
{
// cache the file
typedef typename iStreamT::char_type charT;
std::basic_string<charT> str = detail::read_file(inStream);
// parse it
return read<OutputT>(str.begin(), str.end(), opt);
}
template <typename iStreamT>
inline basic_object<typename iStreamT::char_type> read(iStreamT& inStream, const Options& opt = Options{})
{
return read<basic_object<typename iStreamT::char_type>>(inStream, opt);
}
} // namespace vdf
} // namespace tyti
#ifndef TYTI_NO_L_UNDEF
#undef TYTI_L
#endif
#ifdef TYTI_UNDEF_CONSTEXPR
#undef CONSTEXPR
#undef TYTI_NO_L_UNDEF
#endif
#ifdef TYTI_UNDEF_NOTHROW
#undef NOTHROW
#undef TYTI_UNDEF_NOTHROW
#endif
#endif //__TYTI_STEAM_VDF_PARSER_H__

24
include/winmain-inl.h Normal file
View File

@@ -0,0 +1,24 @@
#pragma once
namespace {
struct OnApp {
OnApp() {
#ifdef WIN32
// Initialize Winsock 2.2
WSADATA wsaData;
int err = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (err) {
std::cout << "WSAStartup() failed with error: %d" << err;
}
#endif
}
~OnApp() {
#ifdef WIN32
WSACleanup();
#endif
}
} __s_onexit_pause;
}

5710
include/zip_file.h Normal file

File diff suppressed because it is too large Load Diff

7
src/Compressor.cpp Normal file → Executable file
View File

@@ -8,6 +8,9 @@
#include <iostream> #include <iostream>
#include <zlib.h> #include <zlib.h>
#ifdef __linux__
#include <cstring>
#endif
#define Biggest 30000 #define Biggest 30000
std::string Comp(std::string Data){ std::string Comp(std::string Data){
@@ -27,7 +30,7 @@ std::string Comp(std::string Data){
deflateEnd(&defstream); deflateEnd(&defstream);
int TO = defstream.total_out; int TO = defstream.total_out;
std::string Ret(TO,0); std::string Ret(TO,0);
memcpy_s(&Ret[0],TO,C,TO); memcpy(&Ret[0],C,TO);
delete [] C; delete [] C;
return Ret; return Ret;
} }
@@ -48,7 +51,7 @@ std::string DeComp(std::string Compressed){
inflateEnd(&infstream); inflateEnd(&infstream);
int TO = infstream.total_out; int TO = infstream.total_out;
std::string Ret(TO,0); std::string Ret(TO,0);
memcpy_s(&Ret[0],TO,C,TO); memcpy(&Ret[0],C,TO);
delete [] C; delete [] C;
return Ret; return Ret;
} }

59
src/Config.cpp Executable file
View File

@@ -0,0 +1,59 @@
///
/// Created by Anonymous275 on 2/23/2021
///
#include "Network/network.hpp"
#include <filesystem>
#include "Logger.h"
#include <fstream>
#include "Json.h"
#include <cstdint>
namespace fs = std::filesystem;
std::string Branch;
void ParseConfig(const json::Document& d){
if(d["Port"].IsInt()){
DEFAULT_PORT = d["Port"].GetInt();
}
//Default -1
//Release 1
//EA 2
//Dev 3
//Custom 3
if(d["Build"].IsString()){
Branch = d["Build"].GetString();
for(char& c : Branch)c = char(tolower(c));
}
}
void ConfigInit(){
if(fs::exists("Launcher.cfg")){
std::ifstream cfg("Launcher.cfg");
if(cfg.is_open()){
auto Size = fs::file_size("Launcher.cfg");
std::string Buffer(Size, 0);
cfg.read(&Buffer[0], Size);
cfg.close();
json::Document d;
d.Parse(Buffer.c_str());
if(d.HasParseError()){
fatal("Config failed to parse make sure it's valid JSON! Code : " + std::to_string(d.GetParseError()));
}
ParseConfig(d);
}else fatal("Failed to open Launcher.cfg!");
}else{
std::ofstream cfg("Launcher.cfg");
if(cfg.is_open()){
cfg <<
R"({
"Port": 4444,
"Build": "Default"
})";
cfg.close();
}else{
fatal("Failed to write config on disk!");
}
}
}

11
src/Discord.cpp Normal file → Executable file
View File

@@ -5,9 +5,12 @@
/// ///
/// Created by Anonymous275 on 7/16/2020 /// Created by Anonymous275 on 7/16/2020
/// ///
#ifndef __linux__
#include "Discord/discord_rpc.h" #include "Discord/discord_rpc.h"
#include "Logger.h" #include "Logger.h"
#include <iostream> #include <cstring>
#include <thread> #include <thread>
#include <ctime> #include <ctime>
@@ -99,8 +102,8 @@ void ErrorAboard(){
exit(6); exit(6);
} }
void Discord_Main(){ void Discord_Main(){
std::thread t1(DMain); /*std::thread t1(DMain);
t1.detach(); t1.detach();*/
/*info("Connecting to discord client..."); /*info("Connecting to discord client...");
int C = 0; int C = 0;
while(DiscordInfo == nullptr && C < 80){ while(DiscordInfo == nullptr && C < 80){
@@ -109,3 +112,5 @@ void Discord_Main(){
} }
if(DiscordInfo == nullptr)ErrorAboard();*/ if(DiscordInfo == nullptr)ErrorAboard();*/
} }
#endif

72
src/GameStart.cpp Normal file → Executable file
View File

@@ -6,14 +6,26 @@
/// Created by Anonymous275 on 7/19/2020 /// Created by Anonymous275 on 7/19/2020
/// ///
#include <Windows.h>
#if defined(_WIN32)
#include <windows.h>
#elif defined(__linux__)
#include "vdf_parser.hpp"
#include <pwd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <spawn.h>
#endif
#include <Security/Init.h>
#include <filesystem>
#include "Startup.h" #include "Startup.h"
#include <ShlObj.h>
#include "Logger.h" #include "Logger.h"
#include <iostream>
#include <thread> #include <thread>
unsigned long GamePID = 0; unsigned long GamePID = 0;
#if defined(_WIN32)
std::string QueryKey(HKEY hKey,int ID); std::string QueryKey(HKEY hKey,int ID);
std::string GetGamePath(){ std::string GetGamePath(){
static std::string Path; static std::string Path;
@@ -23,24 +35,39 @@ std::string GetGamePath(){
LPCTSTR sk = "Software\\BeamNG\\BeamNG.drive"; LPCTSTR sk = "Software\\BeamNG\\BeamNG.drive";
LONG openRes = RegOpenKeyEx(HKEY_CURRENT_USER, sk, 0, KEY_ALL_ACCESS, &hKey); LONG openRes = RegOpenKeyEx(HKEY_CURRENT_USER, sk, 0, KEY_ALL_ACCESS, &hKey);
if (openRes != ERROR_SUCCESS){ if (openRes != ERROR_SUCCESS){
fatal("Please launch the game at least once"); fatal("Please launch the game at least once!");
} }
Path = QueryKey(hKey,4); Path = QueryKey(hKey,4);
if(Path.empty()){ if(Path.empty()){
CoInitialize(nullptr); sk = R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders)";
wchar_t * path = nullptr; openRes = RegOpenKeyEx(HKEY_CURRENT_USER, sk, 0, KEY_ALL_ACCESS, &hKey);
SHGetKnownFolderPath(FOLDERID_Documents, KF_FLAG_SIMPLE_IDLIST, nullptr, (PWSTR *)(&path)); if (openRes != ERROR_SUCCESS){
CoTaskMemFree(path); fatal("Cannot get Local Appdata directory!");
std::wstring ws(path); }
std::string s(ws.begin(), ws.end()); Path = QueryKey(hKey,5);
Path = s;
Path += "\\BeamNG.drive\\"; Path += "\\BeamNG.drive\\";
} }
std::string Ver = CheckVer(GetGameDir());
Ver = Ver.substr(0,Ver.find('.',Ver.find('.')+1));
Path += Ver + "\\";
return Path; return Path;
} }
#elif defined(__linux__)
std::string GetGamePath(){
// Right now only steam is supported
struct passwd *pw = getpwuid(getuid());
std::string homeDir = pw->pw_dir;
std::string Path = homeDir + "/.local/share/BeamNG.drive/";
std::string Ver = CheckVer(GetGameDir());
Ver = Ver.substr(0,Ver.find('.',Ver.find('.')+1));
Path += Ver + "/";
return Path;
}
#endif
#if defined(_WIN32)
void StartGame(std::string Dir){ void StartGame(std::string Dir){
BOOL bSuccess = FALSE; BOOL bSuccess = FALSE;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
@@ -61,6 +88,27 @@ void StartGame(std::string Dir){
std::this_thread::sleep_for(std::chrono::seconds(5)); std::this_thread::sleep_for(std::chrono::seconds(5));
exit(2); exit(2);
} }
#elif defined(__linux__)
void StartGame(std::string Dir) {
int status;
std::string filename = (Dir + "/BinLinux/BeamNG.drive.x64");
char *argv[] = {filename.data(), NULL};
pid_t pid;
int result = posix_spawn(&pid, filename.c_str(), NULL, NULL, argv, environ);
if (result != 0) {
error("Failed to Launch the game! launcher closing soon");
return;
} else {
waitpid(pid, &status, 0);
error("Game Closed! launcher closing soon");
}
std::this_thread::sleep_for(std::chrono::seconds(5));
exit(2);
}
#endif
void InitGame(const std::string& Dir){ void InitGame(const std::string& Dir){
if(!Dev){ if(!Dev){
std::thread Game(StartGame, Dir); std::thread Game(StartGame, Dir);

16
src/Logger.cpp Normal file → Executable file
View File

@@ -14,16 +14,8 @@
#include <thread> #include <thread>
std::string getDate() { std::string getDate() {
typedef std::chrono::duration<int, std::ratio_multiply<std::chrono::hours::period, std::ratio<24>>::type> days; time_t tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); tm local_tm = *localtime(&tt);
std::chrono::system_clock::duration tp = now.time_since_epoch();
days d = std::chrono::duration_cast<days>(tp);tp -= d;
auto h = std::chrono::duration_cast<std::chrono::hours>(tp);tp -= h;
auto m = std::chrono::duration_cast<std::chrono::minutes>(tp);tp -= m;
auto s = std::chrono::duration_cast<std::chrono::seconds>(tp);tp -= s;
time_t tt = std::chrono::system_clock::to_time_t(now);
tm local_tm{};
localtime_s(&local_tm,&tt);
std::stringstream date; std::stringstream date;
int S = local_tm.tm_sec; int S = local_tm.tm_sec;
int M = local_tm.tm_min; int M = local_tm.tm_min;
@@ -44,14 +36,14 @@ std::string getDate() {
} }
void InitLog(){ void InitLog(){
std::ofstream LFS; std::ofstream LFS;
LFS.open ("Launcher.log"); LFS.open(GetEP() + "Launcher.log");
if(!LFS.is_open()){ if(!LFS.is_open()){
error("logger file init failed!"); error("logger file init failed!");
}else LFS.close(); }else LFS.close();
} }
void addToLog(const std::string& Line){ void addToLog(const std::string& Line){
std::ofstream LFS; std::ofstream LFS;
LFS.open("Launcher.log", std::ios_base::app); LFS.open(GetEP() + "Launcher.log", std::ios_base::app);
LFS << Line.c_str(); LFS << Line.c_str();
LFS.close(); LFS.close();
} }

52
src/Network/Core.cpp Normal file → Executable file
View File

@@ -5,12 +5,22 @@
/// ///
/// Created by Anonymous275 on 7/20/2020 /// Created by Anonymous275 on 7/20/2020
/// ///
#include "Network/network.h" #include "Network/network.hpp"
#include "Security/Init.h" #include "Security/Init.h"
#include "Curl/http.h" #include "Http.h"
#include <WinSock2.h> #if defined(_WIN32)
#include <WS2tcpip.h> #include <winsock2.h>
#include <ws2tcpip.h>
#elif defined(__linux__)
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <cstring>
#include <errno.h>
#endif
#include "Startup.h" #include "Startup.h"
#include "Logger.h" #include "Logger.h"
#include <charconv> #include <charconv>
@@ -58,7 +68,7 @@ void Parse(std::string Data,SOCKET CSocket){
NetReset(); NetReset();
Terminate = true; Terminate = true;
TCPTerminate = true; TCPTerminate = true;
Data = Code + HTTP_REQUEST("https://beammp.com/servers-info",443); Data = Code + HTTP::Get("https://backend.beammp.com/servers-info");
break; break;
case 'C': case 'C':
ListOfMods.clear(); ListOfMods.clear();
@@ -177,13 +187,18 @@ void localRes(){
} }
void CoreMain() { void CoreMain() {
debug("Core Network on start!"); debug("Core Network on start!");
WSADATA wsaData;
SOCKET LSocket,CSocket; SOCKET LSocket,CSocket;
struct addrinfo *res = nullptr; struct addrinfo *res = nullptr;
struct addrinfo hints{}; struct addrinfo hints{};
int iRes = WSAStartup(514, &wsaData); //2.2 int iRes;
#ifdef _WIN32
WSADATA wsaData;
iRes = WSAStartup(514, &wsaData); //2.2
if (iRes)debug("WSAStartup failed with error: " + std::to_string(iRes)); if (iRes)debug("WSAStartup failed with error: " + std::to_string(iRes));
#endif
ZeroMemory(&hints, sizeof(hints)); ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP; hints.ai_protocol = IPPROTO_TCP;
@@ -231,6 +246,8 @@ void CoreMain() {
KillSocket(LSocket); KillSocket(LSocket);
WSACleanup(); WSACleanup();
} }
#if defined(_WIN32)
int Handle(EXCEPTION_POINTERS *ep){ int Handle(EXCEPTION_POINTERS *ep){
char* hex = new char[100]; char* hex = new char[100];
sprintf_s(hex,100, "%lX", ep->ExceptionRecord->ExceptionCode); sprintf_s(hex,100, "%lX", ep->ExceptionRecord->ExceptionCode);
@@ -238,13 +255,24 @@ int Handle(EXCEPTION_POINTERS *ep){
delete [] hex; delete [] hex;
return 1; return 1;
} }
#endif
[[noreturn]] void CoreNetwork(){
while(true) {
#if 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)));
}
#endif
void CoreNetwork(){
while(TraceBack >= 4){
__try{
CoreMain();
}__except(Handle(GetExceptionInformation())){}
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));
} }
} }

15
src/Network/DNS.cpp Normal file → Executable file
View File

@@ -7,18 +7,29 @@
/// ///
#include <string> #include <string>
#include <winsock.h>
#if defined(_WIN32)
#include <winsock2.h>
#elif defined(__linux__)
#include "linuxfixes.h"
#include <netdb.h>
#include <arpa/inet.h>
#endif
#include "Logger.h" #include "Logger.h"
std::string GetAddr(const std::string&IP){ std::string GetAddr(const std::string&IP){
if(IP.find_first_not_of("0123456789.") == -1)return IP; if(IP.find_first_not_of("0123456789.") == -1)return IP;
WSADATA wsaData;
hostent *host; hostent *host;
#ifdef _WIN32
WSADATA wsaData;
if(WSAStartup(514, &wsaData) != 0){ if(WSAStartup(514, &wsaData) != 0){
error("WSA Startup Failed!"); error("WSA Startup Failed!");
WSACleanup(); WSACleanup();
return ""; return "";
} }
#endif
host = gethostbyname(IP.c_str()); host = gethostbyname(IP.c_str());
if(!host){ if(!host){
error("DNS lookup failed! on " + IP); error("DNS lookup failed! on " + IP);

27
src/Network/GlobalHandler.cpp Normal file → Executable file
View File

@@ -5,18 +5,27 @@
/// ///
/// Created by Anonymous275 on 7/25/2020 /// Created by Anonymous275 on 7/25/2020
/// ///
#include "Network/network.h" #include "Network/network.hpp"
#include "Security/Init.h" #if defined(_WIN32)
#include <winsock2.h>
#include <ws2tcpip.h>
#elif defined(__linux__)
#include "linuxfixes.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <cstring>
#endif
#include <WinSock2.h>
#include <WS2tcpip.h>
#include "Logger.h" #include "Logger.h"
#include <charconv> #include <charconv>
#include <string> #include <string>
#include <thread> #include <thread>
#include <mutex> #include <mutex>
std::chrono::time_point<std::chrono::steady_clock> PingStart,PingEnd; std::chrono::time_point<std::chrono::high_resolution_clock> PingStart,PingEnd;
bool GConnected = false; bool GConnected = false;
bool CServer = true; bool CServer = true;
SOCKET CSocket = -1; SOCKET CSocket = -1;
@@ -77,7 +86,7 @@ void ServerSend(std::string Data, bool Rel){
int DLen = int(Data.length()); int DLen = int(Data.length());
if(DLen > 3)C = Data.at(0); if(DLen > 3)C = Data.at(0);
if (C == 'O' || C == 'T')Ack = true; if (C == 'O' || C == 'T')Ack = true;
if(C == 'W' || C == 'Y' || C == 'V' || C == 'E')Rel = true; if(C == 'N' || C == 'W' || C == 'Y' || C == 'V' || C == 'E' || C == 'C')Rel = true;
if(Ack || Rel){ if(Ack || Rel){
if(Ack || DLen > 1000)SendLarge(Data); if(Ack || DLen > 1000)SendLarge(Data);
else TCPSend(Data,TCPSock); else TCPSend(Data,TCPSock);
@@ -119,12 +128,16 @@ SOCKET SetupListener(){
if(GSocket != -1)return GSocket; if(GSocket != -1)return GSocket;
struct addrinfo *result = nullptr; struct addrinfo *result = nullptr;
struct addrinfo hints{}; struct addrinfo hints{};
int iRes;
#ifdef _WIN32
WSADATA wsaData; WSADATA wsaData;
int iRes = WSAStartup(514, &wsaData); //2.2 iRes = WSAStartup(514, &wsaData); //2.2
if (iRes != 0) { if (iRes != 0) {
error("(Proxy) WSAStartup failed with error: " + std::to_string(iRes)); error("(Proxy) WSAStartup failed with error: " + std::to_string(iRes));
return -1; return -1;
} }
#endif
ZeroMemory(&hints, sizeof(hints)); ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;

244
src/Network/Http.cpp Normal file → Executable file
View File

@@ -1,115 +1,129 @@
// Copyright (c) 2019-present Anonymous275. // Copyright (c) 2019-present Anonymous275.
// BeamMP Launcher code is not in the public domain and is not free software. // BeamMP Launcher code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. // One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. // Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
/// ///
/// Created by Anonymous275 on 7/18/2020 /// Created by Anonymous275 on 7/18/2020
/// ///
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "Security/Game.h"
#include <iostream>
#include <curl/curl.h> #include <Logger.h>
#include <iostream> #include <fstream>
#include <mutex> #include "Http.h"
#include <mutex>
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp){ #include <cmath>
((std::string*)userp)->append((char*)contents, size * nmemb); #include <httplib.h>
return size * nmemb;
} std::string HTTP::Codes_[] =
{
std::string HTTP_REQUEST(const std::string& IP,int port){ "Success","Unknown","Connection","BindIPAddress",
static std::mutex Lock; "Read","Write","ExceedRedirectCount","Canceled",
Lock.lock(); "SSLConnection","SSLLoadingCerts","SSLServerVerification",
CURL *curl; "UnsupportedMultipartBoundaryChars","Compression"
CURLcode res; };
std::string readBuffer; bool HTTP::isDownload = false;
curl = curl_easy_init(); std::string HTTP::Get(const std::string &IP) {
if(curl) { static std::mutex Lock;
curl_easy_setopt(curl, CURLOPT_URL, IP.c_str()); std::scoped_lock Guard(Lock);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); auto pos = IP.find('/',10);
curl_easy_setopt(curl, CURLOPT_PORT, port);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); httplib::Client cli(IP.substr(0, pos).c_str());
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); cli.set_connection_timeout(std::chrono::seconds(10));
res = curl_easy_perform(curl); cli.set_follow_location(true);
curl_easy_cleanup(curl); auto res = cli.Get(IP.substr(pos).c_str(), ProgressBar);
if(res != CURLE_OK)return "-1"; std::string Ret;
}
Lock.unlock(); if(res.error() == 0){
return readBuffer; if(res->status == 200){
} Ret = res->body;
}else error(res->reason);
int nb_bar;
double last_progress, progress_bar_adv; }else{
int progress_bar (void *bar, double t, double d){ if(isDownload) {
if(last_progress != round(d/t*100)){ std::cout << "\n";
nb_bar = 25; }
progress_bar_adv = round(d/t*nb_bar); error("HTTP Get failed on " + Codes_[res.error()]);
std::cout<<"\r"; }
std::cout<< "Progress : [ ";
if(t!=0)std::cout<<round(d/t*100);else std::cout<<0; return Ret;
std::cout << "% ] ["; }
int i;
for(i = 0; i <= progress_bar_adv; i++)std::cout<<"#"; std::string HTTP::Post(const std::string& IP, const std::string& Fields) {
for(i = 0; i < nb_bar - progress_bar_adv; i++)std::cout<<"."; static std::mutex Lock;
std::cout<<"]"; std::scoped_lock Guard(Lock);
last_progress = round(d/t*100);
} auto pos = IP.find('/',10);
return 0;
} httplib::Client cli(IP.substr(0, pos).c_str());
struct File { cli.set_connection_timeout(std::chrono::seconds(10));
const char *filename; std::string Ret;
FILE *stream;
}; if(!Fields.empty()) {
static size_t my_fwrite(void *buffer,size_t size,size_t nmemb,void *stream){ httplib::Result res = cli.Post(IP.substr(pos).c_str(), Fields, "application/json");
auto *out = (struct File*)stream;
if(!out->stream) { if(res.error() == 0) {
fopen_s(&out->stream,out->filename,"wb"); if (res->status != 200) {
if(!out->stream)return -1; error(res->reason);
} }
return fwrite(buffer, size, nmemb, out->stream); Ret = res->body;
} }else{
int Download(const std::string& URL,const std::string& Path,bool close){ error("HTTP Post failed on " + Codes_[res.error()]);
CURL *curl; }
CURLcode res; }else{
struct File file = {Path.c_str(),nullptr}; httplib::Result res = cli.Post(IP.substr(pos).c_str());
curl = curl_easy_init(); if(res.error() == 0) {
if(curl){ if (res->status != 200) {
curl_easy_setopt(curl, CURLOPT_URL,URL.c_str()); error(res->reason);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); }
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); Ret = res->body;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite); }else{
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &file); error("HTTP Post failed on " + Codes_[res.error()]);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, FALSE); }
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_bar); }
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
res = curl_easy_perform(curl); if(Ret.empty())return "-1";
curl_easy_cleanup(curl); else return Ret;
if(res != CURLE_OK)return res; }
}
if(file.stream)fclose(file.stream); bool HTTP::ProgressBar(size_t c, size_t t){
std::cout << std::endl; if(isDownload) {
return -1; static double last_progress, progress_bar_adv;
} progress_bar_adv = round(c / double(t) * 25);
std::string PostHTTP(const std::string& IP, const std::string& Fields) { std::cout << "\r";
static auto *header = new curl_slist{(char*)"Content-Type: application/json"}; std::cout << "Progress : [ ";
CURL* curl; std::cout << round(c / double(t) * 100);
CURLcode res; std::cout << "% ] [";
std::string readBuffer; int i;
curl = curl_easy_init(); for (i = 0; i <= progress_bar_adv; i++)std::cout << "#";
if (curl) { for (i = 0; i < 25 - progress_bar_adv; i++)std::cout << ".";
curl_easy_setopt(curl, CURLOPT_URL, IP.c_str()); std::cout << "]";
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header); last_progress = round(c / double(t) * 100);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, Fields.size()); }
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, Fields.c_str()); return true;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); }
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); bool HTTP::Download(const std::string &IP, const std::string &Path) {
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5); static std::mutex Lock;
res = curl_easy_perform(curl); std::scoped_lock Guard(Lock);
curl_easy_cleanup(curl);
if (res != CURLE_OK) isDownload = true;
return "-1"; std::string Ret = Get(IP);
} isDownload = false;
return readBuffer;
} if(Ret.empty())return false;
std::ofstream File(Path, std::ios::binary);
if(File.is_open()) {
File << Ret;
File.close();
std::cout << "\n";
info("Download Complete!");
}else{
error("Failed to open file directory: " + Path);
return false;
}
return true;
}

85
src/Network/Resources.cpp Normal file → Executable file
View File

@@ -6,9 +6,19 @@
/// Created by Anonymous275 on 4/11/2020 /// Created by Anonymous275 on 4/11/2020
/// ///
#include "Network/network.h" #include "Network/network.hpp"
#if defined(_WIN32)
#include <ws2tcpip.h>
#elif defined(__linux__)
#include <sys/socket.h>
#include <sys/types.h>
#include <cstring>
#include <errno.h>
#include <netdb.h>
#include <arpa/inet.h>
#endif
#include <WS2tcpip.h>
#include <filesystem> #include <filesystem>
#include "Startup.h" #include "Startup.h"
#include "Logger.h" #include "Logger.h"
@@ -20,8 +30,9 @@
#include <atomic> #include <atomic>
#include <vector> #include <vector>
#include <future> #include <future>
#include <cmath>
namespace fs = std::experimental::filesystem; namespace fs = std::filesystem;
std::string ListOfMods; std::string ListOfMods;
std::vector<std::string> Split(const std::string& String,const std::string& delimiter){ std::vector<std::string> Split(const std::string& String,const std::string& delimiter){
std::vector<std::string> Val; std::vector<std::string> Val;
@@ -37,9 +48,13 @@ std::vector<std::string> Split(const std::string& String,const std::string& deli
} }
void CheckForDir(){ void CheckForDir(){
struct stat info{}; if(!fs::exists("Resources")){
if(stat( "Resources", &info) != 0){ // Could we just use fs::create_directory instead?
#if defined(_WIN32)
_wmkdir(L"Resources"); _wmkdir(L"Resources");
#elif defined(__linux__)
fs::create_directory(L"Resources");
#endif
} }
} }
void WaitForConfirm(){ void WaitForConfirm(){
@@ -61,7 +76,7 @@ std::string Auth(SOCKET Sock){
auto Res = TCPRcv(Sock); auto Res = TCPRcv(Sock);
if(Res.empty() || Res[0] == 'E'){ if(Res.empty() || Res[0] == 'E' || Res[0] == 'K'){
Abord(); Abord();
return ""; return "";
} }
@@ -88,7 +103,7 @@ std::string Auth(SOCKET Sock){
Res = TCPRcv(Sock); Res = TCPRcv(Sock);
if(Res[0] == 'E'){ if(Res[0] == 'E' || Res[0] == 'K'){
Abord(); Abord();
return ""; return "";
} }
@@ -110,11 +125,10 @@ void UpdateUl(bool D,const std::string&msg){
void AsyncUpdate(uint64_t& Rcv,uint64_t Size,const std::string& Name){ void AsyncUpdate(uint64_t& Rcv,uint64_t Size,const std::string& Name){
do { do {
double pr = Rcv / double(Size) * 100; double pr = double(Rcv) / double(Size) * 100;
std::string Per = std::to_string(trunc(pr * 10) / 10); std::string Per = std::to_string(trunc(pr * 10) / 10);
UpdateUl(true, Name + " (" + Per.substr(0, Per.find('.') + 2) + "%)"); UpdateUl(true, Name + " (" + Per.substr(0, Per.find('.') + 2) + "%)");
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(100));
}while(!Terminate && Rcv < Size); }while(!Terminate && Rcv < Size);
} }
@@ -126,11 +140,10 @@ char* TCPRcvRaw(SOCKET Sock,uint64_t& GRcv, uint64_t Size){
} }
char* File = new char[Size]; char* File = new char[Size];
uint64_t Rcv = 0; uint64_t Rcv = 0;
int32_t Temp;
do{ do{
int Len = int(Size-Rcv); int Len = int(Size-Rcv);
if(Len > 1000000)Len = 1000000; if(Len > 1000000)Len = 1000000;
Temp = recv(Sock, &File[Rcv], Len, MSG_WAITALL); int32_t Temp = recv(Sock, &File[Rcv], Len, MSG_WAITALL);
if(Temp < 1){ if(Temp < 1){
info(std::to_string(Temp)); info(std::to_string(Temp));
UUl("Socket Closed Code 1"); UUl("Socket Closed Code 1");
@@ -153,6 +166,7 @@ SOCKET InitDSock(){
SOCKET DSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); SOCKET DSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
SOCKADDR_IN ServerAddr; SOCKADDR_IN ServerAddr;
if(DSock < 1){ if(DSock < 1){
KillSocket(DSock);
Terminate = true; Terminate = true;
return 0; return 0;
} }
@@ -177,8 +191,7 @@ std::string MultiDownload(SOCKET MSock,SOCKET DSock, uint64_t Size, const std::s
uint64_t GRcv = 0, MSize = Size/2, DSize = Size - MSize; uint64_t GRcv = 0, MSize = Size/2, DSize = Size - MSize;
std::thread Au(AsyncUpdate,std::ref(GRcv),Size,Name); std::thread Au(AsyncUpdate,std::ref(GRcv), Size, Name);
Au.detach();
std::packaged_task<char*()> task([&] { return TCPRcvRaw(MSock,GRcv,MSize); }); std::packaged_task<char*()> task([&] { return TCPRcvRaw(MSock,GRcv,MSize); });
std::future<char*> f1 = task.get_future(); std::future<char*> f1 = task.get_future();
@@ -199,20 +212,26 @@ std::string MultiDownload(SOCKET MSock,SOCKET DSock, uint64_t Size, const std::s
MultiKill(MSock,DSock); MultiKill(MSock,DSock);
return ""; return "";
} }
if(Au.joinable())Au.join(); if(Au.joinable())Au.join();
///omg yes very ugly my god but i was in a rush will revisit ///omg yes very ugly my god but i was in a rush will revisit
std::string Ret(Size,0); std::string Ret(Size,0);
memcpy_s(&Ret[0],MSize,MData,MSize); memcpy(&Ret[0],MData,MSize);
delete[]MData; delete[]MData;
memcpy_s(&Ret[MSize],DSize,DData,DSize); memcpy(&Ret[MSize],DData,DSize);
delete[]DData; delete[]DData;
return Ret; return Ret;
} }
void InvalidResource(const std::string& File){
UUl("Invalid mod \"" + File + "\"");
warn("The server tried to sync \"" + File + "\" that is not a .zip file!");
Terminate = true;
}
void SyncResources(SOCKET Sock){ void SyncResources(SOCKET Sock){
std::string Ret = Auth(Sock); std::string Ret = Auth(Sock);
@@ -239,10 +258,16 @@ void SyncResources(SOCKET Sock){
t.clear(); t.clear();
for(auto FN = FNames.begin(),FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN,++FS) { for(auto FN = FNames.begin(),FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN,++FS) {
auto pos = FN->find_last_of('/'); auto pos = FN->find_last_of('/');
auto ZIP = FN->find(".zip");
if (ZIP == std::string::npos || FN->length() - ZIP != 4) {
InvalidResource(*FN);
return;
}
if (pos == std::string::npos)continue; if (pos == std::string::npos)continue;
Amount++; Amount++;
} }
if(!FNames.empty())info("Syncing..."); if(!FNames.empty())info("Syncing...");
SOCKET DSock = InitDSock();
for(auto FN = FNames.begin(),FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN,++FS) { for(auto FN = FNames.begin(),FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN,++FS) {
auto pos = FN->find_last_of('/'); auto pos = FN->find_last_of('/');
if (pos != std::string::npos) { if (pos != std::string::npos) {
@@ -251,14 +276,23 @@ void SyncResources(SOCKET Sock){
Pos++; Pos++;
if (fs::exists(a)) { if (fs::exists(a)) {
if (FS->find_first_not_of("0123456789") != std::string::npos)continue; if (FS->find_first_not_of("0123456789") != std::string::npos)continue;
if (fs::file_size(a) == std::stoi(*FS)){ if (fs::file_size(a) == std::stoull(*FS)){
UpdateUl(false,std::to_string(Pos) + "/" + std::to_string(Amount) + ": " + a.substr(a.find_last_of('/'))); UpdateUl(false,std::to_string(Pos) + "/" + std::to_string(Amount) + ": " + a.substr(a.find_last_of('/')));
std::this_thread::sleep_for(std::chrono::milliseconds(50)); std::this_thread::sleep_for(std::chrono::milliseconds(50));
try { try {
if(!fs::exists(GetGamePath() + "mods/multiplayer")){ if(!fs::exists(GetGamePath() + "mods/multiplayer")){
fs::create_directory(GetGamePath() + "mods/multiplayer"); fs::create_directories(GetGamePath() + "mods/multiplayer");
} }
fs::copy_file(a, GetGamePath() + "mods/multiplayer" + a.substr(a.find_last_of('/')),
std::string FName = a.substr(a.find_last_of('/'));
// Linux version of the game doesnt support uppercase letters in mod names
#if defined(__linux__)
for(char &c : FName){
c = ::tolower(c);
}
#endif
fs::copy_file(a, GetGamePath() + "mods/multiplayer" + FName,
fs::copy_options::overwrite_existing); fs::copy_options::overwrite_existing);
} catch (std::exception& e) { } catch (std::exception& e) {
error("Failed copy to the mods folder! " + std::string(e.what())); error("Failed copy to the mods folder! " + std::string(e.what()));
@@ -271,7 +305,6 @@ void SyncResources(SOCKET Sock){
} }
CheckForDir(); CheckForDir();
std::string FName = a.substr(a.find_last_of('/')); std::string FName = a.substr(a.find_last_of('/'));
SOCKET DSock = InitDSock();
do { do {
TCPSend("f" + *FN,Sock); TCPSend("f" + *FN,Sock);
@@ -295,16 +328,24 @@ void SyncResources(SOCKET Sock){
LFS.close(); LFS.close();
} }
}while(fs::file_size(a) != std::stoi(*FS) && !Terminate); }while(fs::file_size(a) != std::stoull(*FS) && !Terminate);
KillSocket(DSock);
if(!Terminate){ if(!Terminate){
if(!fs::exists(GetGamePath() + "mods/multiplayer")){ if(!fs::exists(GetGamePath() + "mods/multiplayer")){
fs::create_directory(GetGamePath() + "mods/multiplayer"); fs::create_directories(GetGamePath() + "mods/multiplayer");
} }
// Linux version of the game doesnt support uppercase letters in mod names
#if defined(__linux__)
for(char &c : FName){
c = ::tolower(c);
}
#endif
fs::copy_file(a,GetGamePath() + "mods/multiplayer" + FName, fs::copy_options::overwrite_existing); fs::copy_file(a,GetGamePath() + "mods/multiplayer" + FName, fs::copy_options::overwrite_existing);
} }
WaitForConfirm(); WaitForConfirm();
} }
KillSocket(DSock);
if(!Terminate){ if(!Terminate){
TCPSend("Done",Sock); TCPSend("Done",Sock);
info("Done!"); info("Done!");

22
src/Network/VehicleData.cpp Normal file → Executable file
View File

@@ -6,9 +6,20 @@
/// Created by Anonymous275 on 5/8/2020 /// Created by Anonymous275 on 5/8/2020
/// ///
#include "Zlib/Compressor.h" #include "Zlib/Compressor.h"
#include "Network/network.h" #include "Network/network.hpp"
#if defined(_WIN32)
#include <ws2tcpip.h>
#elif defined(__linux__)
#include <sys/socket.h>
#include <sys/types.h>
#include <cstring>
#include <errno.h>
#include <netdb.h>
#include <arpa/inet.h>
#include "linuxfixes.h"
#endif
#include <WS2tcpip.h>
#include "Logger.h" #include "Logger.h"
#include <string> #include <string>
#include <set> #include <set>
@@ -44,7 +55,11 @@ void UDPParser(std::string Packet){
} }
void UDPRcv(){ void UDPRcv(){
sockaddr_in FromServer{}; sockaddr_in FromServer{};
#if defined(_WIN32)
int clientLength = sizeof(FromServer); int clientLength = sizeof(FromServer);
#elif defined(__linux__)
socklen_t clientLength = sizeof(FromServer);
#endif
ZeroMemory(&FromServer, clientLength); ZeroMemory(&FromServer, clientLength);
std::string Ret(10240,0); std::string Ret(10240,0);
if(UDPSock == -1)return; if(UDPSock == -1)return;
@@ -53,11 +68,14 @@ void UDPRcv(){
UDPParser(Ret.substr(0,Rcv)); UDPParser(Ret.substr(0,Rcv));
} }
void UDPClientMain(const std::string& IP,int Port){ void UDPClientMain(const std::string& IP,int Port){
#ifdef _WIN32
WSADATA data; WSADATA data;
if (WSAStartup(514, &data)){ if (WSAStartup(514, &data)){
error("Can't start Winsock!"); error("Can't start Winsock!");
return; return;
} }
#endif
delete ToServer; delete ToServer;
ToServer = new sockaddr_in; ToServer = new sockaddr_in;
ToServer->sin_family = AF_INET; ToServer->sin_family = AF_INET;

30
src/Network/VehicleEvent.cpp Normal file → Executable file
View File

@@ -10,11 +10,20 @@
#include <vector> #include <vector>
#include "Logger.h" #include "Logger.h"
#include <iostream> #include <iostream>
#include <WS2tcpip.h>
#include <Zlib/Compressor.h> #include <Zlib/Compressor.h>
#include "Network/network.h" #if defined(_WIN32)
#include <ws2tcpip.h>
#elif defined(__linux__)
#include <sys/socket.h>
#include <sys/types.h>
#include <cstring>
#include <errno.h>
#include <netdb.h>
#include <arpa/inet.h>
#endif
#include "Network/network.hpp"
int LastPort; int LastPort;
std::string LastIP; std::string LastIP;
@@ -109,17 +118,19 @@ std::string TCPRcv(SOCKET Sock){
#ifdef DEBUG #ifdef DEBUG
//debug("Parsing from server -> " + std::to_string(Ret.size())); //debug("Parsing from server -> " + std::to_string(Ret.size()));
#endif #endif
if(Ret[0] == 'E')UUl(Ret.substr(1)); if(Ret[0] == 'E' || Ret[0] == 'K')UUl(Ret.substr(1));
return Ret; return Ret;
} }
void TCPClientMain(const std::string& IP,int Port){ void TCPClientMain(const std::string& IP,int Port){
LastIP = IP; LastIP = IP;
LastPort = Port; LastPort = Port;
WSADATA wsaData;
SOCKADDR_IN ServerAddr; SOCKADDR_IN ServerAddr;
int RetCode; int RetCode;
#ifdef _WIN32
WSADATA wsaData;
WSAStartup(514, &wsaData); //2.2 WSAStartup(514, &wsaData); //2.2
#endif
TCPSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); TCPSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(TCPSock == -1){ if(TCPSock == -1){
@@ -127,21 +138,23 @@ void TCPClientMain(const std::string& IP,int Port){
WSACleanup(); WSACleanup();
return; return;
} }
ServerAddr.sin_family = AF_INET; ServerAddr.sin_family = AF_INET;
ServerAddr.sin_port = htons(Port); ServerAddr.sin_port = htons(Port);
inet_pton(AF_INET, IP.c_str(), &ServerAddr.sin_addr); inet_pton(AF_INET, IP.c_str(), &ServerAddr.sin_addr);
RetCode = connect(TCPSock, (SOCKADDR *) &ServerAddr, sizeof(ServerAddr)); RetCode = connect(TCPSock, (SOCKADDR *) &ServerAddr, sizeof(ServerAddr));
if(RetCode != 0){ if(RetCode != 0){
UlStatus = "UlConnection Failed!"; UlStatus = "UlConnection Failed!";
std::cout << "Client: connect failed! Error code: " << WSAGetLastError() << std::endl; error("Client: connect failed! Error code: " + std::to_string(WSAGetLastError()));
KillSocket(TCPSock); KillSocket(TCPSock);
WSACleanup(); WSACleanup();
Terminate = true; Terminate = true;
return; return;
} }
getsockname(TCPSock, (SOCKADDR *)&ServerAddr, (int *)sizeof(ServerAddr)); info("Connected!");
char Code = 'C'; char Code = 'C';
send(TCPSock,&Code,1,0); send(TCPSock, &Code, 1, 0);
SyncResources(TCPSock); SyncResources(TCPSock);
while(!Terminate){ while(!Terminate){
ServerParser(TCPRcv(TCPSock)); ServerParser(TCPRcv(TCPSock));
@@ -151,6 +164,9 @@ void TCPClientMain(const std::string& IP,int Port){
if(KillSocket(TCPSock) != 0) if(KillSocket(TCPSock) != 0)
debug("(TCP) Cannot close socket. Error code: " + std::to_string(WSAGetLastError())); debug("(TCP) Cannot close socket. Error code: " + std::to_string(WSAGetLastError()));
#ifdef _WIN32
if(WSACleanup() != 0) if(WSACleanup() != 0)
debug("(TCP) Client: WSACleanup() failed!..."); debug("(TCP) Client: WSACleanup() failed!...");
#endif
} }

643
src/Security/BeamNG.cpp Normal file → Executable file
View File

@@ -1,292 +1,351 @@
// Copyright (c) 2019-present Anonymous275. // Copyright (c) 2019-present Anonymous275.
// BeamMP Launcher code is not in the public domain and is not free software. // BeamMP Launcher code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. // One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. // Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
/// ///
/// Created by Anonymous275 on 7/18/2020 /// Created by Anonymous275 on 7/18/2020
/// ///
#include <filesystem> #include <filesystem>
#include <Windows.h> #if defined(_WIN32)
#include "Logger.h" #include <windows.h>
#include <fstream> #elif defined(__linux__)
#include <sstream> #include "vdf_parser.hpp"
#include <string> #include <vector>
#include <thread> #include <pwd.h>
#include <unistd.h>
#define MAX_KEY_LENGTH 255 #endif
#define MAX_VALUE_NAME 16383 #include "Logger.h"
#include <fstream>
int TraceBack = 0; #include <sstream>
std::string GameDir; #include <string>
#include <thread>
void lowExit(int code){
TraceBack = 0; #define MAX_KEY_LENGTH 255
std::string msg = #define MAX_VALUE_NAME 16383
"Failed to find the game please launch it. Report this if the issue persists code ";
error(msg+std::to_string(code)); int TraceBack = 0;
std::this_thread::sleep_for(std::chrono::seconds(10)); std::string GameDir;
exit(2);
} void lowExit(int code){
void Exit(int code){ TraceBack = 0;
TraceBack = 0; std::string msg =
std::string msg = "Failed to find the game please launch it. Report this if the issue persists code ";
"Sorry. We do not support cracked copies report this if you believe this is a mistake code "; error(msg+std::to_string(code));
error(msg+std::to_string(code)); std::this_thread::sleep_for(std::chrono::seconds(10));
std::this_thread::sleep_for(std::chrono::seconds(10)); exit(2);
exit(3); }
} /*void Exit(int code){
void SteamExit(int code){ TraceBack = 0;
TraceBack = 0; std::string msg =
std::string msg = "Sorry. We do not support cracked copies report this if you believe this is a mistake code ";
"Illegal steam modifications detected report this if you believe this is a mistake code "; error(msg+std::to_string(code));
error(msg+std::to_string(code)); std::this_thread::sleep_for(std::chrono::seconds(10));
std::this_thread::sleep_for(std::chrono::seconds(10)); exit(3);
exit(4); }
} void SteamExit(int code){
std::string GetGameDir(){ TraceBack = 0;
if(TraceBack != 4)Exit(0); std::string msg =
return GameDir.substr(0,GameDir.find_last_of('\\')); "Illegal steam modifications detected report this if you believe this is a mistake code ";
} error(msg+std::to_string(code));
LONG OpenKey(HKEY root,const char* path,PHKEY hKey){ std::this_thread::sleep_for(std::chrono::seconds(10));
return RegOpenKeyEx(root, reinterpret_cast<LPCSTR>(path), 0, KEY_READ, hKey); exit(4);
} }*/
std::string QueryKey(HKEY hKey,int ID){ std::string GetGameDir(){
TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name //if(TraceBack != 4)Exit(0);
DWORD cbName; // size of name string #if defined(_WIN32)
TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name return GameDir.substr(0,GameDir.find_last_of('\\'));
DWORD cchClassName = MAX_PATH; // size of class string #elif defined(__linux__)
DWORD cSubKeys=0; // number of subkeys return GameDir.substr(0,GameDir.find_last_of('/'));
DWORD cbMaxSubKey; // longest subkey size #endif
DWORD cchMaxClass; // longest class string }
DWORD cValues; // number of values for key #ifdef _WIN32
DWORD cchMaxValue; // longest value name LONG OpenKey(HKEY root,const char* path,PHKEY hKey){
DWORD cbMaxValueData; // longest value data return RegOpenKeyEx(root, reinterpret_cast<LPCSTR>(path), 0, KEY_READ, hKey);
DWORD cbSecurityDescriptor; // size of security descriptor }
FILETIME ftLastWriteTime; // last write time std::string QueryKey(HKEY hKey,int ID){
TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name
DWORD i, retCode; DWORD cbName; // size of name string
TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name
TCHAR achValue[MAX_VALUE_NAME]; DWORD cchClassName = MAX_PATH; // size of class string
DWORD cchValue = MAX_VALUE_NAME; DWORD cSubKeys=0; // number of subkeys
DWORD cbMaxSubKey; // longest subkey size
retCode = RegQueryInfoKey( DWORD cchMaxClass; // longest class string
hKey, // key handle DWORD cValues; // number of values for key
achClass, // buffer for class name DWORD cchMaxValue; // longest value name
&cchClassName, // size of class string DWORD cbMaxValueData; // longest value data
nullptr, // reserved DWORD cbSecurityDescriptor; // size of security descriptor
&cSubKeys, // number of subkeys FILETIME ftLastWriteTime; // last write time
&cbMaxSubKey, // longest subkey size
&cchMaxClass, // longest class string DWORD i, retCode;
&cValues, // number of values for this key
&cchMaxValue, // longest value name TCHAR achValue[MAX_VALUE_NAME];
&cbMaxValueData, // longest value data DWORD cchValue = MAX_VALUE_NAME;
&cbSecurityDescriptor, // security descriptor
&ftLastWriteTime); // last write time retCode = RegQueryInfoKey(
hKey, // key handle
BYTE* buffer = new BYTE[cbMaxValueData]; achClass, // buffer for class name
ZeroMemory(buffer, cbMaxValueData); &cchClassName, // size of class string
if (cSubKeys){ nullptr, // reserved
for (i=0; i<cSubKeys; i++){ &cSubKeys, // number of subkeys
cbName = MAX_KEY_LENGTH; &cbMaxSubKey, // longest subkey size
retCode = RegEnumKeyEx(hKey, i,achKey,&cbName,nullptr,nullptr,nullptr,&ftLastWriteTime); &cchMaxClass, // longest class string
if (retCode == ERROR_SUCCESS){ &cValues, // number of values for this key
if(strcmp(achKey,"Steam App 284160") == 0){ &cchMaxValue, // longest value name
return achKey; &cbMaxValueData, // longest value data
} &cbSecurityDescriptor, // security descriptor
} &ftLastWriteTime); // last write time
}
} BYTE* buffer = new BYTE[cbMaxValueData];
if (cValues){ ZeroMemory(buffer, cbMaxValueData);
for (i=0, retCode = ERROR_SUCCESS; i<cValues; i++){ if (cSubKeys){
cchValue = MAX_VALUE_NAME; for (i=0; i<cSubKeys; i++){
achValue[0] = '\0'; cbName = MAX_KEY_LENGTH;
retCode = RegEnumValue(hKey, i,achValue,&cchValue,nullptr,nullptr,nullptr,nullptr); retCode = RegEnumKeyEx(hKey, i,achKey,&cbName,nullptr,nullptr,nullptr,&ftLastWriteTime);
if (retCode == ERROR_SUCCESS ){ if (retCode == ERROR_SUCCESS){
DWORD lpData = cbMaxValueData; if(strcmp(achKey,"Steam App 284160") == 0){
buffer[0] = '\0'; return achKey;
LONG dwRes = RegQueryValueEx(hKey, achValue, nullptr, nullptr, buffer, &lpData); }
std::string data = reinterpret_cast<const char *const>(buffer); }
std::string key = achValue; }
switch (ID){ }
case 1: if(key == "SteamExe"){ if (cValues){
auto p = data.find_last_of('/'); for (i=0, retCode = ERROR_SUCCESS; i<cValues; i++){
if(p != std::string::npos)return data.substr(0,p); cchValue = MAX_VALUE_NAME;
}break; achValue[0] = '\0';
case 2: if(key == "Name" && data == "BeamNG.drive")return data;break; retCode = RegEnumValue(hKey, i,achValue,&cchValue,nullptr,nullptr,nullptr,nullptr);
case 3: if(key == "rootpath")return data;break; if (retCode == ERROR_SUCCESS ){
case 4: if(key == "userpath_override")return data; DWORD lpData = cbMaxValueData;
default: break; buffer[0] = '\0';
} LONG dwRes = RegQueryValueEx(hKey, achValue, nullptr, nullptr, buffer, &lpData);
} std::string data = (char *)(buffer);
} std::string key = achValue;
}
delete [] buffer; switch (ID){
return ""; case 1: if(key == "SteamExe"){
} auto p = data.find_last_of("/\\");
namespace fs = std::experimental::filesystem; if(p != std::string::npos){
void FileList(std::vector<std::string>&a,const std::string& Path){ return data.substr(0,p);
for (const auto &entry : fs::directory_iterator(Path)) { }
auto pos = entry.path().filename().string().find('.'); }
if (pos != std::string::npos) { break;
a.emplace_back(entry.path().string()); case 2: if(key == "Name" && data == "BeamNG.drive")return data;break;
}else FileList(a,entry.path().string()); case 3: if(key == "rootpath")return data;break;
} case 4: if(key == "userpath_override")return data;
} case 5: if(key == "Local AppData")return data;
bool Find(const std::string& FName,const std::string& Path){ default: break;
std::vector<std::string> FS; }
FileList(FS,Path+"/userdata"); }
for(std::string&a : FS){ }
if(a.find(FName) != std::string::npos){ }
FS.clear(); delete [] buffer;
return true; return "";
} }
} #endif
FS.clear();
return false; namespace fs = std::filesystem;
}
bool FindHack(const std::string& Path){ bool NameValid(const std::string& N){
bool s = true; if(N == "config" || N == "librarycache"){
for (const auto &entry : fs::directory_iterator(Path)) { return true;
std::string Name = entry.path().filename().string(); }
for(char&c : Name)c = char(tolower(c)); if(N.find_first_not_of("0123456789") == std::string::npos){
if(Name == "steam.exe")s = false; return true;
if(Name.find("greenluma") != -1)return true; }
Name.clear(); return false;
} }
return s; void FileList(std::vector<std::string>&a,const std::string& Path){
} for (const auto &entry : fs::directory_iterator(Path)) {
std::vector<std::string> GetID(const std::string& log){ const auto& DPath = entry.path();
std::string vec,t,r; if (!entry.is_directory()) {
std::vector<std::string> Ret; a.emplace_back(DPath.u8string());
std::ifstream f(log.c_str(), std::ios::binary); }else if(NameValid(DPath.filename().u8string())){
f.seekg(0, std::ios_base::end); FileList(a, DPath.u8string());
std::streampos fileSize = f.tellg(); }
vec.resize(size_t(fileSize) + 1); }
f.seekg(0, std::ios_base::beg); }
f.read(&vec[0], fileSize); bool Find(const std::string& FName,const std::string& Path){
f.close(); std::vector<std::string> FS;
std::stringstream ss(vec); FileList(FS,Path+"\\userdata");
bool S = false; for(std::string&a : FS){
while (std::getline(ss, t, '{')) { if(a.find(FName) != std::string::npos){
if(!S)S = true; FS.clear();
else{ return true;
for(char& c : t){ }
if(isdigit(c))r += c; }
} FS.clear();
break; return false;
} }
} bool FindHack(const std::string& Path){
Ret.emplace_back(r); bool s = true;
r.clear(); for (const auto &entry : fs::directory_iterator(Path)) {
S = false; std::string Name = entry.path().filename().u8string();
bool L = true; for(char&c : Name)c = char(tolower(c));
while (std::getline(ss, t, '}')) { if(Name == "steam.exe")s = false;
if(L){ if(Name.find("greenluma") != -1){
L = false; error("Found malicious file/folder \"" + Name+"\"");
continue; return true;
} }
for(char& c : t){ Name.clear();
if(c == '"'){ }
if(!S)S = true; return s;
else{ }
if(r.length() > 10) { std::vector<std::string> GetID(const std::string& log){
Ret.emplace_back(r); std::string vec,t,r;
} std::vector<std::string> Ret;
r.clear(); std::ifstream f(log.c_str(), std::ios::binary);
S = false; f.seekg(0, std::ios_base::end);
continue; std::streampos fileSize = f.tellg();
} vec.resize(size_t(fileSize) + 1);
} f.seekg(0, std::ios_base::beg);
if(isdigit(c))r += c; f.read(&vec[0], fileSize);
} f.close();
} std::stringstream ss(vec);
vec.clear(); bool S = false;
return Ret; while (std::getline(ss, t, '{')) {
} if(!S)S = true;
std::string GetManifest(const std::string& Man){ else{
std::string vec; for(char& c : t){
std::ifstream f(Man.c_str(), std::ios::binary); if(isdigit(c))r += c;
f.seekg(0, std::ios_base::end); }
std::streampos fileSize = f.tellg(); break;
vec.resize(size_t(fileSize) + 1); }
f.seekg(0, std::ios_base::beg); }
f.read(&vec[0], fileSize); Ret.emplace_back(r);
f.close(); r.clear();
std::string ToFind = "\"LastOwner\"\t\t\""; S = false;
int pos = int(vec.find(ToFind)); bool L = true;
if(pos != -1){ while (std::getline(ss, t, '}')) {
pos += int(ToFind.length()); if(L){
vec = vec.substr(pos); L = false;
return vec.substr(0,vec.find('\"')); continue;
}else return ""; }
} for(char& c : t){
bool IDCheck(std::string Man, std::string steam){ if(c == '"'){
bool a = false,b = true; if(!S)S = true;
int pos = int(Man.rfind("steamapps")); else{
if(pos == -1)Exit(5); if(r.length() > 10) {
Man = Man.substr(0,pos+9) + "/appmanifest_284160.acf"; Ret.emplace_back(r);
steam += "/config/loginusers.vdf"; }
if(fs::exists(Man) && fs::exists(steam)){ r.clear();
for(const std::string&ID : GetID(steam)){ S = false;
if(ID == GetManifest(Man))b = false; continue;
} }
if(b)Exit(6); }
}else a = true; if(isdigit(c))r += c;
return a; }
} }
void LegitimacyCheck(){ vec.clear();
std::string Result,T; return Ret;
std::string K1 = R"(Software\Valve\Steam)"; }
std::string K2 = R"(Software\Valve\Steam\Apps\284160)"; std::string GetManifest(const std::string& Man){
std::string K3 = R"(Software\BeamNG\BeamNG.drive)"; std::string vec;
HKEY hKey; std::ifstream f(Man.c_str(), std::ios::binary);
LONG dwRegOPenKey = OpenKey(HKEY_CURRENT_USER, K1.c_str(), &hKey); f.seekg(0, std::ios_base::end);
if(dwRegOPenKey == ERROR_SUCCESS) { std::streampos fileSize = f.tellg();
Result = QueryKey(hKey, 1); vec.resize(size_t(fileSize) + 1);
if(Result.empty())Exit(1); f.seekg(0, std::ios_base::beg);
if(fs::exists(Result)){ f.read(&vec[0], fileSize);
if(!Find("284160.json",Result))Exit(2); f.close();
if(FindHack(Result))SteamExit(1); std::string ToFind = "\"LastOwner\"\t\t\"";
}else Exit(3); int pos = int(vec.find(ToFind));
T = Result; if(pos != -1){
Result.clear(); pos += int(ToFind.length());
TraceBack++; vec = vec.substr(pos);
}else Exit(4); return vec.substr(0,vec.find('\"'));
K1.clear(); }else return "";
RegCloseKey(hKey); }
dwRegOPenKey = OpenKey(HKEY_CURRENT_USER, K2.c_str(), &hKey); bool IDCheck(std::string Man, std::string steam){
if(dwRegOPenKey == ERROR_SUCCESS) { bool a = false,b = true;
Result = QueryKey(hKey, 2); int pos = int(Man.rfind("steamapps"));
if(Result.empty())lowExit(1); // if(pos == -1)Exit(5);
TraceBack++; Man = Man.substr(0,pos+9) + "\\appmanifest_284160.acf";
}else lowExit(2); steam += "\\config\\loginusers.vdf";
K2.clear(); if(fs::exists(Man) && fs::exists(steam)){
RegCloseKey(hKey); for(const std::string&ID : GetID(steam)){
dwRegOPenKey = OpenKey(HKEY_CURRENT_USER, K3.c_str(), &hKey); if(ID == GetManifest(Man))b = false;
if(dwRegOPenKey == ERROR_SUCCESS) { }
Result = QueryKey(hKey, 3); //if(b)Exit(6);
if(Result.empty())lowExit(3); }else a = true;
if(IDCheck(Result,T))lowExit(5); return a;
GameDir = Result; }
TraceBack++; void LegitimacyCheck(){
}else lowExit(4);
K3.clear(); //std::string K1 = R"(Software\Valve\Steam)";
Result.clear(); //std::string K2 = R"(Software\Valve\Steam\Apps\284160)";
RegCloseKey(hKey);
if(TraceBack < 3)exit(-1); /*LONG dwRegOPenKey = OpenKey(HKEY_CURRENT_USER, K1.c_str(), &hKey);
}
std::string CheckVer(const std::string &dir){ if(dwRegOPenKey == ERROR_SUCCESS) {
std::string temp,Path = dir + "\\integrity.json"; Result = QueryKey(hKey, 1);
std::ifstream f(Path.c_str(), std::ios::binary); if(Result.empty())Exit(1);
int Size = int(std::filesystem::file_size(Path));
std::string vec(Size,0); if(fs::exists(Result)){
f.read(&vec[0], Size); if(!Find("284160.json",Result))Exit(2);
f.close(); if(FindHack(Result))SteamExit(1);
}else Exit(3);
vec = vec.substr(vec.find_last_of("version"),vec.find_last_of('"'));
for(const char &a : vec){ T = Result;
if(isdigit(a) || a == '.')temp+=a; Result.clear();
} TraceBack++;
return temp; }else Exit(4);
}
K1.clear();
RegCloseKey(hKey);
dwRegOPenKey = OpenKey(HKEY_CURRENT_USER, K2.c_str(), &hKey);
if(dwRegOPenKey == ERROR_SUCCESS) {
Result = QueryKey(hKey, 2);
if(Result.empty())lowExit(1);
TraceBack++;
}else lowExit(2);
K2.clear();
RegCloseKey(hKey);*/
#if defined(_WIN32)
std::string Result;
std::string K3 = R"(Software\BeamNG\BeamNG.drive)";
HKEY hKey;
LONG dwRegOPenKey = OpenKey(HKEY_CURRENT_USER, K3.c_str(), &hKey);
if(dwRegOPenKey == ERROR_SUCCESS) {
Result = QueryKey(hKey, 3);
if(Result.empty())lowExit(3);
//if(IDCheck(Result,T))lowExit(5);
GameDir = Result;
//TraceBack++;
}else lowExit(4);
K3.clear();
Result.clear();
RegCloseKey(hKey);
//if(TraceBack < 3)exit(-1);
#elif defined(__linux__)
struct passwd *pw = getpwuid(getuid());
std::string homeDir = pw->pw_dir;
// Right now only steam is supported
std::ifstream libraryFolders(homeDir + "/.steam/root/steamapps/libraryfolders.vdf");
auto root = tyti::vdf::read(libraryFolders);
for (auto folderInfo: root.childs){
if (std::filesystem::exists(folderInfo.second->attribs["path"] + "/steamapps/common/BeamNG.drive/")){
GameDir = folderInfo.second->attribs["path"] + "/steamapps/common/BeamNG.drive/";
break;
}
}
#endif
}
std::string CheckVer(const std::string &dir){
#if defined(_WIN32)
std::string temp,Path = dir + "\\integrity.json";
#elif defined(__linux__)
std::string temp,Path = dir + "/integrity.json";
#endif
std::ifstream f(Path.c_str(), std::ios::binary);
int Size = int(std::filesystem::file_size(Path));
std::string vec(Size,0);
f.read(&vec[0], Size);
f.close();
vec = vec.substr(vec.find_last_of("version"),vec.find_last_of('"'));
for(const char &a : vec){
if(isdigit(a) || a == '.')temp+=a;
}
return temp;
}

216
src/Security/Login.cpp Normal file → Executable file
View File

@@ -1,94 +1,122 @@
// Copyright (c) 2019-present Anonymous275. // Copyright (c) 2019-present Anonymous275.
// BeamMP Launcher code is not in the public domain and is not free software. // BeamMP Launcher code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. // One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. // Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
/// ///
/// Created by Anonymous275 on 11/26/2020 /// Created by Anonymous275 on 11/26/2020
/// ///
#include "Curl/http.h" #include "Http.h"
#include <filesystem> #include <filesystem>
#include "Logger.h" #include "Logger.h"
#include <fstream> #include <fstream>
#include "Json.h" #include "Json.h"
using namespace std::filesystem; namespace fs = std::filesystem;
std::string PublicKey; std::string PublicKey;
extern bool LoginAuth; extern bool LoginAuth;
std::string Role;
void UpdateKey(const char* newKey){
if(newKey){ void UpdateKey(const char* newKey){
std::ofstream Key("key"); if(newKey && std::isalnum(newKey[0])){
if(Key.is_open()){ std::ofstream Key("key");
Key << newKey; if(Key.is_open()){
Key.close(); Key << newKey;
}else fatal("Cannot write to disk!"); Key.close();
}else if(exists("key")){ }else fatal("Cannot write to disk!");
remove("key"); }else if(fs::exists("key")){
} remove("key");
} }
}
/// "username":"value","password":"value"
/// "Guest":"Name" /// "username":"value","password":"value"
/// "pk":"private_key" /// "Guest":"Name"
/// "pk":"private_key"
std::string Login(const std::string& fields){
info("Attempting to authenticate..."); std::string GetFail(const std::string& R){
std::string Buffer = PostHTTP("https://auth.beammp.com/userlogin", fields); std::string DRet = R"({"success":false,"message":)";
json::Document d; DRet += "\""+R+"\"}";
d.Parse(Buffer.c_str()); error(R);
if(Buffer == "-1"){ return DRet;
fatal("Failed to communicate with the auth system!"); }
}
if (Buffer.find('{') == -1 || d.HasParseError()) { std::string Login(const std::string& fields){
fatal("Invalid answer from authentication servers, please try again later!"); if(fields == "LO"){
} LoginAuth = false;
if(!d["success"].IsNull() && d["success"].GetBool()){ UpdateKey(nullptr);
LoginAuth = true; return "";
if(!d["private_key"].IsNull()){ }
UpdateKey(d["private_key"].GetString()); info("Attempting to authenticate...");
} std::string Buffer = HTTP::Post("https://auth.beammp.com/userlogin", fields);
if(!d["public_key"].IsNull()){ json::Document d;
PublicKey = d["public_key"].GetString(); d.Parse(Buffer.c_str());
} if(Buffer == "-1"){
info("Authentication successful!"); return GetFail("Failed to communicate with the auth system!");
}else info("Authentication failed!"); }
if(!d["message"].IsNull()){
d.RemoveMember("private_key"); if (Buffer.at(0) != '{' || d.HasParseError()) {
d.RemoveMember("public_key"); error(Buffer);
rapidjson::StringBuffer buffer; return GetFail("Invalid answer from authentication servers, please try again later!");
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); }
d.Accept(writer); if(d.HasMember("success") && !d["success"].IsNull() && d["success"].GetBool()){
return buffer.GetString(); LoginAuth = true;
} if(d.HasMember("private_key") && !d["private_key"].IsNull()){
return "{\"success\":false}"; UpdateKey(d["private_key"].GetString());
} }
if(d.HasMember("public_key") && !d["public_key"].IsNull()){
void CheckLocalKey(){ PublicKey = d["public_key"].GetString();
if(exists("key") && file_size("key") < 100){ }
std::ifstream Key("key"); info("Authentication successful!");
if(Key.is_open()) { }else info("Authentication failed!");
auto Size = file_size("key"); if(d.HasMember("message") && !d["message"].IsNull()){
std::string Buffer(Size, 0); d.RemoveMember("private_key");
Key.read(&Buffer[0], Size); d.RemoveMember("public_key");
Key.close(); rapidjson::StringBuffer buffer;
Buffer = PostHTTP("https://auth.beammp.com/userlogin", R"({"pk":")"+Buffer+"\"}"); rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
json::Document d; d.Accept(writer);
d.Parse(Buffer.c_str()); return buffer.GetString();
if (Buffer == "-1" || Buffer.find('{') == -1 || d.HasParseError()) { }
fatal("Invalid answer from authentication servers, please try again later!"); return GetFail("Invalid message parsing!");
} }
if(d["success"].GetBool()){
LoginAuth = true; void CheckLocalKey(){
UpdateKey(d["private_key"].GetString()); if(fs::exists("key") && fs::file_size("key") < 100){
PublicKey = d["public_key"].GetString(); std::ifstream Key("key");
}else{ if(Key.is_open()) {
info("Auto-Authentication unsuccessful please re-login!"); auto Size = fs::file_size("key");
UpdateKey(nullptr); std::string Buffer(Size, 0);
} Key.read(&Buffer[0], Size);
}else{ Key.close();
warn("Could not open saved key!");
UpdateKey(nullptr); for (char& c : Buffer) {
} if (!std::isalnum(c) && c != '-') {
}else UpdateKey(nullptr); UpdateKey(nullptr);
} return;
}
}
Buffer = HTTP::Post("https://auth.beammp.com/userlogin", R"({"pk":")" + Buffer + "\"}");
json::Document d;
d.Parse(Buffer.c_str());
if (Buffer == "-1" || Buffer.at(0) != '{' || d.HasParseError()) {
error(Buffer);
info("Invalid answer from authentication servers.");
UpdateKey(nullptr);
}
if(d.HasMember("success") && d["success"].GetBool()){
LoginAuth = true;
UpdateKey(d["private_key"].GetString());
PublicKey = d["public_key"].GetString();
Role = d["role"].GetString();
//info(Role);
}else{
info("Auto-Authentication unsuccessful please re-login!");
UpdateKey(nullptr);
}
}else{
warn("Could not open saved key!");
UpdateKey(nullptr);
}
}else UpdateKey(nullptr);
}

575
src/Startup.cpp Normal file → Executable file
View File

@@ -1,236 +1,339 @@
// Copyright (c) 2019-present Anonymous275. // Copyright (c) 2019-present Anonymous275.
// BeamMP Launcher code is not in the public domain and is not free software. // BeamMP Launcher code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. // One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. // Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
/// ///
/// Created by Anonymous275 on 7/16/2020 /// Created by Anonymous275 on 7/16/2020
/// ///
#include "Discord/discord_info.h" #include "zip_file.h"
#include "Network/network.h" #include <string>
#include "Security/Init.h" #if defined(_WIN32)
#include <windows.h>
#include "Curl/http.h" #elif defined(__linux__)
#include <curl/curl.h> #include <unistd.h>
#include <filesystem> #endif
#include "Startup.h" #include "Discord/discord_info.h"
#include <iostream> #include "Network/network.hpp"
#include "Logger.h" #include "Security/Init.h"
#include <thread> #include <filesystem>
#include "Startup.h"
extern int TraceBack; #include "Logger.h"
bool Dev = false; #include <fstream>
namespace fs = std::experimental::filesystem; #include <thread>
std::string GetEN(){ #include "Http.h"
return "BeamMP-Launcher.exe"; #include "Json.h"
}
std::string GetVer(){ extern int TraceBack;
return "1.80"; bool Dev = false;
} namespace fs = std::filesystem;
std::string GetPatch(){
return ""; #if defined(_WIN32)
} std::string GetEN(){
void ReLaunch(int argc,char*args[]){ return "BeamMP-Launcher.exe";
std::string Arg; }
for(int c = 2; c <= argc; c++){ #elif defined(__linux__)
Arg += " "; std::string GetEN(){
Arg += args[c-1]; return "BeamMP-Launcher";
} }
system("cls"); #endif
ShellExecute(nullptr,"runas",GetEN().c_str(),Arg.c_str(),nullptr,SW_SHOWNORMAL);
ShowWindow(GetConsoleWindow(),0); std::string GetVer(){
std::this_thread::sleep_for(std::chrono::seconds(1)); return "2.0";
exit(1); }
} std::string GetPatch(){
void URelaunch(int argc,char* args[]){ return ".82";
std::string Arg; }
for(int c = 2; c <= argc; c++){
Arg += " "; std::string GetEP(char*P){
Arg += args[c-1]; static std::string Ret = [&](){
} std::string path(P);
ShellExecute(nullptr,"open",GetEN().c_str(),Arg.c_str(),nullptr,SW_SHOWNORMAL); return path.substr(0, path.find_last_of("\\/") + 1);
ShowWindow(GetConsoleWindow(),0); } ();
std::this_thread::sleep_for(std::chrono::seconds(1)); return Ret;
exit(1); }
} #if defined(_WIN32)
void CheckName(int argc,char* args[]){ void ReLaunch(int argc,char*args[]){
struct stat info{}; std::string Arg;
std::string DN = GetEN(),CDir = args[0],FN = CDir.substr(CDir.find_last_of('\\')+1); for(int c = 2; c <= argc; c++){
if(FN != DN){ Arg += " ";
if(stat(DN.c_str(),&info)==0)remove(DN.c_str()); Arg += args[c-1];
if(stat(DN.c_str(),&info)==0)ReLaunch(argc,args); }
std::rename(FN.c_str(), DN.c_str()); system("cls");
URelaunch(argc,args); ShellExecute(nullptr,"runas",(GetEP() + GetEN()).c_str(),Arg.c_str(),nullptr,SW_SHOWNORMAL);
} ShowWindow(GetConsoleWindow(),0);
} std::this_thread::sleep_for(std::chrono::seconds(1));
exit(1);
/// Deprecated }
void RequestRole(){ void URelaunch(int argc,char* args[]){
auto NPos = std::string::npos; std::string Arg;
std::string HTTP_Result = HTTP_REQUEST("https://beammp.com/entitlement?did="+GetDID()+"&t=l",443); for(int c = 2; c <= argc; c++){
if(HTTP_Result == "-1"){ Arg += " ";
HTTP_Result = HTTP_REQUEST("https://backup1.beammp.com/entitlement?did="+GetDID()+"&t=l",443); Arg += args[c-1];
if(HTTP_Result == "-1") { }
fatal("Sorry Backend System Outage! Don't worry it will back on soon!"); ShellExecute(nullptr,"open",(GetEP() + GetEN()).c_str(),Arg.c_str(),nullptr,SW_SHOWNORMAL);
} ShowWindow(GetConsoleWindow(),0);
} std::this_thread::sleep_for(std::chrono::seconds(1));
if(HTTP_Result.find("\"MDEV\"") != NPos || HTTP_Result.find("\"CON\"") != NPos){ exit(1);
Dev = true; }
} #elif defined(__linux__)
if(HTTP_Result.find("Error") != NPos){ void ReLaunch(int argc,char*args[]){
fatal("Sorry You need to be in the official BeamMP Discord to proceed! https://discord.gg/beammp"); std::string Arg;
} for(int c = 2; c <= argc; c++){
info("Client Connected!"); Arg += " ";
} Arg += args[c-1];
}
void CheckForUpdates(int argc,char*args[],const std::string& CV){ system("clear");
std::string link = "https://beammp.com/builds/launcher?version=true"; execl((GetEP() + GetEN()).c_str(), Arg.c_str(), NULL);
std::string HTTP = HTTP_REQUEST(link,443); std::this_thread::sleep_for(std::chrono::seconds(1));
bool fallback = false; exit(1);
if(HTTP.find_first_of("0123456789") == std::string::npos){ }
link = "https://backup1.beammp.com/builds/launcher?version=true"; void URelaunch(int argc,char* args[]){
HTTP = HTTP_REQUEST(link,443); std::string Arg;
fallback = true; for(int c = 2; c <= argc; c++){
if(HTTP.find_first_of("0123456789") == std::string::npos) { Arg += " ";
fatal("Primary Servers Offline! sorry for the inconvenience!"); Arg += args[c-1];
} }
} execl((GetEP() + GetEN()).c_str(), Arg.c_str(), NULL);
if(fallback){ std::this_thread::sleep_for(std::chrono::seconds(1));
link = "https://backup1.beammp.com/builds/launcher?download=true"; exit(1);
}else link = "https://beammp.com/builds/launcher?download=true"; }
#endif
struct stat buffer{};
std::string Back = "BeamMP-Launcher.back"; void CheckName(int argc,char* args[]){
if(stat(Back.c_str(), &buffer) == 0)remove(Back.c_str()); #if defined(_WIN32)
if(HTTP > CV){ std::string DN = GetEN(),CDir = args[0],FN = CDir.substr(CDir.find_last_of('\\')+1);
system("cls"); #elif defined(__linux__)
info("Update found!"); std::string DN = GetEN(),CDir = args[0],FN = CDir.substr(CDir.find_last_of('/')+1);
info("Updating..."); #endif
if(std::rename(GetEN().c_str(), Back.c_str()))error("failed creating a backup!"); if(FN != DN){
int i = Download(link, GetEN(),true); if(fs::exists(DN))remove(DN.c_str());
if(i != -1){ if(fs::exists(DN))ReLaunch(argc,args);
error("Launcher Update failed! trying again... code : " + std::to_string(i)); std::rename(FN.c_str(), DN.c_str());
std::this_thread::sleep_for(std::chrono::seconds(2)); URelaunch(argc,args);
int i2 = Download(link, GetEN(),true); }
if(i2 != -1){ }
error("Launcher Update failed! code : " + std::to_string(i2));
std::this_thread::sleep_for(std::chrono::seconds(5)); void CheckForUpdates(int argc,char*args[],const std::string& CV){
ReLaunch(argc,args); std::string link;
} std::string HTTP = HTTP::Get("https://beammp.com/builds/launcher?version=true");
} bool fallback = false;
URelaunch(argc,args); if(HTTP.find_first_of("0123456789") == std::string::npos){
}else{ HTTP = HTTP::Get("https://backup1.beammp.com/builds/launcher?version=true");
info("Version is up to date"); fallback = true;
} if(HTTP.find_first_of("0123456789") == std::string::npos) {
TraceBack++; fatal("Primary Servers Offline! sorry for the inconvenience!");
} }
void CheckDir(int argc,char*args[]){ }
std::string CDir = args[0]; if(fallback){
std::string MDir = "BeamNG\\mods"; link = "https://backup1.beammp.com/builds/launcher?download=true";
if(!fs::is_directory("BeamNG")){ }else link = "https://beammp.com/builds/launcher?download=true";
if(!fs::create_directory("BeamNG")){
error("Cannot Create BeamNG Directory! Retrying..."); std::string EP(GetEP() + GetEN()), Back(GetEP() + "BeamMP-Launcher.back");
std::this_thread::sleep_for(std::chrono::seconds(3));
ReLaunch(argc,args); if(fs::exists(Back))remove(Back.c_str());
}
} if(HTTP > CV){
if(fs::is_directory(MDir) && !Dev){ #if defined(_WIN32)
int c = 0; system("cls");
for (auto& p : fs::directory_iterator(MDir))c++; #elif defined(__linux__)
if(c > 2) { system("clear");
warn(std::to_string(c-1) + " local launcher mods will be wiped! Close this window if you don't want that!"); #endif
std::this_thread::sleep_for(std::chrono::seconds(15)); info("Update found!");
} info("Updating...");
try{ if(std::rename(EP.c_str(), Back.c_str()))error("failed creating a backup!");
fs::remove_all(MDir);
} catch (...) { if(!HTTP::Download(link, EP)){
error("Please close the game and try again"); error("Launcher Update failed! trying again...");
std::this_thread::sleep_for(std::chrono::seconds(5)); std::this_thread::sleep_for(std::chrono::seconds(2));
exit(1);
} if(!HTTP::Download(link, EP)){
} error("Launcher Update failed!");
if(fs::is_directory(MDir) && !Dev)ReLaunch(argc,args); std::this_thread::sleep_for(std::chrono::seconds(5));
if(!fs::create_directory(MDir) && !Dev){ ReLaunch(argc,args);
error("Cannot Create Mods Directory! Retrying..."); }
std::this_thread::sleep_for(std::chrono::seconds(3)); }
ReLaunch(argc,args); URelaunch(argc,args);
} }else info("Launcher version is up to date");
if(!fs::is_directory("BeamNG\\settings")){ TraceBack++;
if(!fs::create_directory("BeamNG\\settings")){ }
error("Cannot Create Settings Directory! Retrying...");
std::this_thread::sleep_for(std::chrono::seconds(3)); void CustomPort(int argc, char* argv[]){
ReLaunch(argc,args); if(argc > 1){
} std::string Port = argv[1];
} if(Port.find_first_not_of("0123456789") == std::string::npos){
} if(std::stoi(Port) > 1000){
void CustomPort(int argc, char* argv[]){ DEFAULT_PORT = std::stoi(Port);
if(argc > 1){ warn("Running on custom port : " + std::to_string(DEFAULT_PORT));
std::string Port = argv[1]; }
if(Port.find_first_not_of("0123456789") == std::string::npos){ }
if(std::stoi(Port) > 1000){ if(argc > 2)Dev = true;
DEFAULT_PORT = std::stoi(Port); }
warn("Running on custom port : " + std::to_string(DEFAULT_PORT)); }
}
} #ifdef _WIN32
if(argc > 2)Dev = false; void LinuxPatch(){
} HKEY hKey = nullptr;
} LONG result = RegOpenKeyEx(HKEY_CURRENT_USER, R"(Software\Wine)", 0, KEY_READ, &hKey);
void InitLauncher(int argc, char* argv[]) { if (result != ERROR_SUCCESS || getenv("USER") == nullptr)return;
system("cls"); RegCloseKey(hKey);
curl_global_init(CURL_GLOBAL_DEFAULT); info("Wine/Proton Detected! If you are on windows delete HKEY_CURRENT_USER\\Software\\Wine in regedit");
SetConsoleTitleA(("BeamMP Launcher v" + std::string(GetVer()) + GetPatch()).c_str()); info("Applying patches...");
InitLog();
CheckName(argc, argv); result = RegCreateKey(HKEY_CURRENT_USER, R"(Software\Valve\Steam\Apps\284160)", &hKey);
CheckLocalKey(); //will replace RequestRole
Discord_Main(); if (result != ERROR_SUCCESS){
//RequestRole(); fatal(R"(failed to create HKEY_CURRENT_USER\Software\Valve\Steam\Apps\284160)");
CustomPort(argc, argv); return;
CheckForUpdates(argc, argv, std::string(GetVer()) + GetPatch()); }
}
result = RegSetValueEx(hKey, "Name", 0, REG_SZ, (BYTE*)"BeamNG.drive", 12);
void PreGame(int argc, char* argv[],const std::string& GamePath){
info("Game Version : " + CheckVer(GamePath)); if (result != ERROR_SUCCESS){
std::string DUI = R"(BeamNG\settings\uiapps-layouts.json)"; fatal(R"(failed to create the value "Name" under HKEY_CURRENT_USER\Software\Valve\Steam\Apps\284160)");
std::string GS = R"(BeamNG\settings\game-settings.ini)"; return;
std::string link = "https://beammp.com/client-ui-data"; }
bool fallback = false; RegCloseKey(hKey);
int i;
if(!fs::exists(DUI)){ std::string Path = R"(Z:\home\)" + std::string(getenv("USER")) + R"(\.steam\steam\Steam.exe)";
info("Downloading default ui data...");
i = Download(link,DUI,true); if(!fs::exists(Path)) {
if(i != -1){ std::ofstream ofs(Path);
fallback = true; if (!ofs.is_open()) {
remove(DUI.c_str()); fatal("Failed to create file \"" + Path + "\"");
link = "https://backup1.beammp.com/client-ui-data"; return;
i = Download(link,DUI,true); } else ofs.close();
if(i != -1) { }
error("Failed to download code : " + std::to_string(i));
std::this_thread::sleep_for(std::chrono::seconds(3)); result = RegOpenKeyEx(HKEY_CURRENT_USER, R"(Software\Valve\Steam)", 0, KEY_ALL_ACCESS, &hKey);
ReLaunch(argc, argv); if (result != ERROR_SUCCESS){
} fatal(R"(failed to open HKEY_CURRENT_USER\Software\Valve\Steam)");
} return;
info("Download Complete!"); }
}
if(!fs::exists(GS)) { result = RegSetValueEx(hKey, "SteamExe", 0, REG_SZ, (BYTE*)Path.c_str(), Path.size());
info("Downloading default game settings...");
if(fallback)link = "https://backup1.beammp.com/client-settings-data"; if (result != ERROR_SUCCESS){
else link = "https://beammp.com/client-settings-data"; fatal(R"(failed to create the value "Name" under HKEY_CURRENT_USER\Software\Valve\Steam\Apps\284160)");
Download(link, GS,true); return;
info("Download Complete!"); }
}
if(!Dev) { RegCloseKey(hKey);
info("Downloading mod...");
if(fallback)link = "https://backup1.beammp.com/builds/client"; info("Patched!");
else link ="https://beammp.com/builds/client"; }
if(!fs::exists(GetGamePath() + "mods")){ #endif
fs::create_directory(GetGamePath() + "mods");
} #if defined(_WIN32)
if(!fs::exists(GetGamePath() + "mods/multiplayer")){ void InitLauncher(int argc, char* argv[]) {
fs::create_directory(GetGamePath() + "mods/multiplayer"); system("cls");
} SetConsoleTitleA(("BeamMP Launcher v" + std::string(GetVer()) + GetPatch()).c_str());
Download(link, GetGamePath() + R"(mods\multiplayer\BeamMP.zip)", true); InitLog();
info("Download Complete!"); CheckName(argc, argv);
} LinuxPatch();
/*debug("Name : " + GetDName()); CheckLocalKey();
debug("Discriminator : " + GetDTag()); ConfigInit();
debug("Unique ID : " + GetDID());*/ CustomPort(argc, argv);
} Discord_Main();
CheckForUpdates(argc, argv, std::string(GetVer()) + GetPatch());
}
#elif defined(__linux__)
void InitLauncher(int argc, char* argv[]) {
system("clear");
InitLog();
CheckName(argc, argv);
CheckLocalKey();
ConfigInit();
CustomPort(argc, argv);
CheckForUpdates(argc, argv, std::string(GetVer()) + GetPatch());
}
#endif
size_t DirCount(const std::filesystem::path& path){
return (size_t)std::distance(std::filesystem::directory_iterator{path}, std::filesystem::directory_iterator{});
}
void CheckMP(const std::string& Path) {
if (!fs::exists(Path))return;
size_t c = DirCount(fs::path(Path));
try {
for (auto& p : fs::directory_iterator(Path)){
if(p.exists() && !p.is_directory()){
std::string Name = p.path().filename().u8string();
for(char&Ch : Name)Ch = char(tolower(Ch));
if(Name != "beammp.zip")fs::remove(p.path());
}
}
} catch (...) {
fatal("We were unable to clean the multiplayer mods folder! Is the game still running or do you have something open in that folder?");
}
}
void EnableMP(){
std::string File(GetGamePath() + "mods/db.json");
if(!fs::exists(File))return;
auto Size = fs::file_size(File);
if(Size < 2)return;
std::ifstream db(File);
if(db.is_open()) {
std::string Data(Size, 0);
db.read(&Data[0], Size);
db.close();
json::Document d;
d.Parse(Data.c_str());
if(Data.at(0) != '{' || d.HasParseError()){
//error("Failed to parse " + File); //TODO illegal formatting
return;
}
if(d.HasMember("mods") && !d["mods"].IsNull() && d["mods"].HasMember("multiplayerbeammp") && !d["mods"]["multiplayerbeammp"].IsNull()){
d["mods"]["multiplayerbeammp"]["active"] = true;
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
d.Accept(writer);
std::ofstream ofs(File);
if(ofs.is_open()){
ofs << buffer.GetString();
ofs.close();
}else{
error("Failed to write " + File);
}
}
}
}
void PreGame(const std::string& GamePath){
std::string GameVer = CheckVer(GamePath);
info("Game Version : " + GameVer);
CheckMP(GetGamePath() + "mods/multiplayer");
if(!Dev) {
info("Downloading mod please wait...");
try {
if (!fs::exists(GetGamePath() + "mods/multiplayer")) {
fs::create_directories(GetGamePath() + "mods/multiplayer");
}
EnableMP();
}catch(std::exception&e){
fatal(e.what());
}
#if defined(_WIN32)
std::string ZipPath(GetGamePath() + R"(mods\multiplayer\BeamMP.zip)");
#elif defined(__linux__)
// Linux version of the game cant handle mods with uppercase names
std::string ZipPath(GetGamePath() + R"(mods/multiplayer/beammp.zip)");
#endif
HTTP::Download("https://backend.beammp.com/builds/client?download=true"
"&pk=" + PublicKey + "&branch=" + Branch, ZipPath);
std::string Target(GetGamePath() + "mods/unpacked/beammp");
if(fs::is_directory(Target)) {
fs::remove_all(Target);
}
//HTTP::Download("beammp.com/builds/client", GetGamePath() + R"(mods\multiplayer\BeamMP.zip)");
}
}

23
src/main.cpp Normal file → Executable file
View File

@@ -5,16 +5,18 @@
/// ///
/// Created by Anonymous275 on 7/16/2020 /// Created by Anonymous275 on 7/16/2020
/// ///
#include "Network/network.h" #include "Network/network.hpp"
#include "Security/Init.h" #include "Security/Init.h"
#include "Startup.h" #include "Startup.h"
#include <thread>
#include <iostream> #include <iostream>
#include "Logger.h"
#include <thread>
#include "Http.h"
[[noreturn]] void flush(){ [[noreturn]] void flush(){
while(true){ while(true){
std::cout.flush(); std::cout.flush();
std::this_thread::sleep_for(std::chrono::milliseconds(500)); std::this_thread::sleep_for(std::chrono::milliseconds(100));
} }
} }
@@ -23,10 +25,19 @@ int main(int argc, char* argv[]) {
std::thread th(flush); std::thread th(flush);
th.detach(); th.detach();
#endif #endif
GetEP(argv[0]);
InitLauncher(argc,argv); InitLauncher(argc,argv);
CheckDir(argc,argv);
LegitimacyCheck(); try {
PreGame(argc,argv,GetGameDir()); LegitimacyCheck();
}catch (std::exception& e){
fatal("Main 1 : " + std::string(e.what()));
}
PreGame(GetGameDir());
InitGame(GetGameDir()); InitGame(GetGameDir());
CoreNetwork(); CoreNetwork();
///TODO: make sure to use argv[0] for everything that should be in the same dir (mod down ect...)
} }