mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2026-07-13 10:13:42 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bd4ab2b10d | |||
| 1cdc8e8f48 | |||
| 1f72a45231 | |||
| 002223afda | |||
| ecc79b1918 |
@@ -48,7 +48,6 @@ set(PRJ_HEADERS
|
||||
include/TScopedTimer.h
|
||||
include/TServer.h
|
||||
include/VehicleData.h
|
||||
include/Update.h
|
||||
)
|
||||
# add all source files (.cpp) to this, except the one with main()
|
||||
set(PRJ_SOURCES
|
||||
@@ -71,7 +70,6 @@ set(PRJ_SOURCES
|
||||
src/TScopedTimer.cpp
|
||||
src/TServer.cpp
|
||||
src/VehicleData.cpp
|
||||
src/Update.cpp
|
||||
)
|
||||
|
||||
find_package(Lua REQUIRED)
|
||||
|
||||
@@ -52,20 +52,22 @@ You can find precompiled binaries under [Releases](https://github.com/BeamMP/Bea
|
||||
|
||||
On Linux, you need some dependencies to **build** the server (on Windows, you don't):
|
||||
|
||||
For Debian, Ubuntu and others you can find scripts that do this in `scripts/`.
|
||||
|
||||
```
|
||||
liblua5.3-dev curl zip unzip tar cmake make git g++
|
||||
```
|
||||
|
||||
The names of each package may change depending on your platform. See in `scripts/` or use a search engine to find out what they're called for you.
|
||||
You can install these with your distribution's package manager. You will need sudo or need root for ONLY this step.
|
||||
|
||||
The names of each package may change depending on your platform.
|
||||
|
||||
If you are building for ARM (like aarch64), you need to run `export VCPKG_FORCE_SYSTEM_BINARIES=1` before the following commands.
|
||||
|
||||
You can build on **Windows, Linux** or other platforms by following these steps:
|
||||
|
||||
1. Check out the repository with git: `git clone --recursive https://github.com/BeamMP/BeamMP-Server`.
|
||||
2. Go into the directory `cd BeamMP-Server`.
|
||||
3. Run CMake `cmake -S . -B bin -DCMAKE_BUILD_TYPE=Release` - this can take a few minutes and may take a lot of disk space and bandwidth.
|
||||
4. Build via `cmake --build bin --parallel -t BeamMP-Server`.
|
||||
4. Build via `cmake --build bin --parallel --config Release -t BeamMP-Server`.
|
||||
5. Your executable can be found in `bin/`.
|
||||
|
||||
When you make changes to the code, you only have to run step 4 again.
|
||||
|
||||
Vendored
+1
-1
Submodule deps/commandline updated: 0ff46d25b1...b2a29733f9
+1
-1
@@ -134,7 +134,7 @@ private:
|
||||
static inline std::mutex mShutdownHandlersMutex {};
|
||||
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
|
||||
|
||||
static inline Version mVersion { 3, 2, 0 };
|
||||
static inline Version mVersion { 3, 2, 1 };
|
||||
};
|
||||
|
||||
void SplitString(std::string const& str, const char delim, std::vector<std::string>& out);
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Update {
|
||||
|
||||
[[noreturn]] void PerformUpdate(const std::string& InvokedAs);
|
||||
|
||||
}
|
||||
@@ -80,6 +80,17 @@ TEST_CASE("GetPidVid") {
|
||||
TServer::TServer(const std::vector<std::string_view>& Arguments) {
|
||||
beammp_info("BeamMP Server v" + Application::ServerVersionString());
|
||||
Application::SetSubsystemStatus("Server", Application::Status::Starting);
|
||||
if (Arguments.size() > 1) {
|
||||
Application::Settings.CustomIP = Arguments[0];
|
||||
size_t n = std::count(Application::Settings.CustomIP.begin(), Application::Settings.CustomIP.end(), '.');
|
||||
auto p = Application::Settings.CustomIP.find_first_not_of(".0123456789");
|
||||
if (p != std::string::npos || n != 3 || Application::Settings.CustomIP.substr(0, 3) == "127") {
|
||||
Application::Settings.CustomIP.clear();
|
||||
beammp_warn("IP Specified is invalid! Ignoring");
|
||||
} else {
|
||||
beammp_info("server started with custom IP");
|
||||
}
|
||||
}
|
||||
Application::SetSubsystemStatus("Server", Application::Status::Good);
|
||||
}
|
||||
|
||||
|
||||
-249
@@ -1,249 +0,0 @@
|
||||
#include "Update.h"
|
||||
#include "Common.h"
|
||||
#include <algorithm>
|
||||
#include <boost/predef.h>
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <httplib.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#if defined(__linux)
|
||||
#include <unistd.h>
|
||||
#elif defined(WIN32)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
static bool ProgressReport(uint64_t current, uint64_t total) {
|
||||
if (current == total) {
|
||||
fmt::print("100% ({:.2f} / {:.2f} KiB)\n", float(current) / 1024.0f, float(total) / 1024.0f);
|
||||
}
|
||||
if (total != 0) {
|
||||
auto percent = (float(current) / float(total)) * 100.0f;
|
||||
fmt::print("{:3.0f}% ({:.2f} / {:.2f} KiB)\r", percent, float(current) / 1024.0f, float(total) / 1024.0f);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Update::PerformUpdate(const std::string& InvokedAs) {
|
||||
using json = nlohmann::json;
|
||||
namespace http = httplib;
|
||||
|
||||
constexpr auto GH = "api.github.com";
|
||||
std::string ReleasesAPI = "/repos/BeamMP/BeamMP-Server/releases";
|
||||
|
||||
http::Headers APIHeaders {};
|
||||
APIHeaders.emplace("X-GitHub-Api-Version", "2022-11-28");
|
||||
APIHeaders.emplace("Accept", "application/vnd.github+json");
|
||||
|
||||
http::SSLClient c(GH);
|
||||
c.set_read_timeout(std::chrono::seconds(30));
|
||||
|
||||
beammp_infof("Checking for latest release...");
|
||||
// check if there is a new release
|
||||
auto Res = c.Get(ReleasesAPI + "/latest", APIHeaders);
|
||||
if (!Res || Res->status < 200 || Res->status >= 300) {
|
||||
beammp_errorf("Failed to fetch latest release: {}", to_string(Res.error()));
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
Version NewVersion { 0, 0, 0 };
|
||||
|
||||
json ReleaseInfo;
|
||||
|
||||
try {
|
||||
ReleaseInfo = json::parse(Res->body);
|
||||
|
||||
std::string TagName = ReleaseInfo["tag_name"].get<std::string>();
|
||||
if (!TagName.starts_with("v") || std::count(TagName.begin(), TagName.end(), '.') != 2) {
|
||||
beammp_errorf("Invalid version provided by GitHub: '{}', exiting", TagName);
|
||||
std::exit(1);
|
||||
}
|
||||
TagName = TagName.substr(1);
|
||||
auto Tag = Application::VersionStrToInts(TagName);
|
||||
NewVersion = Version { Tag[0], Tag[1], Tag[2] };
|
||||
beammp_infof("Latest release is v{}", NewVersion.AsString());
|
||||
} catch (const std::exception& e) {
|
||||
beammp_errorf("Failed to fetch latest release info from GitHub");
|
||||
beammp_errorf("Error: {}", e.what());
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
// check if the new release is patch, minor or major
|
||||
auto Current = Application::ServerVersion();
|
||||
|
||||
bool MajorOutdated = NewVersion.major > Current.major;
|
||||
bool MinorOutdated = !MajorOutdated && Application::IsOutdated(Current, NewVersion);
|
||||
|
||||
if (!MajorOutdated && !MinorOutdated) {
|
||||
beammp_infof("BeamMP-Server is already the latest version (v{})!", Current.AsString());
|
||||
std::exit(0);
|
||||
} else {
|
||||
beammp_infof("New update available, updating from v{} to v{}", Current.AsString(), NewVersion.AsString());
|
||||
}
|
||||
|
||||
std::string Arch = "";
|
||||
|
||||
#if BOOST_ARCH_ARM && BOOST_ARCH_WORD_BITS == 64
|
||||
Arch = "arm64";
|
||||
#elif BOOST_ARCH_X86_64
|
||||
Arch = "x86_64";
|
||||
#else
|
||||
beammp_errorf("The current architecture is not supported, please update manually.");
|
||||
std::exit(1);
|
||||
#endif
|
||||
|
||||
std::string Postfix = {};
|
||||
// figure out current platform
|
||||
#if WIN32
|
||||
beammp_infof("Current platform is Windows");
|
||||
Postfix = ".exe";
|
||||
#elif __linux
|
||||
beammp_infof("Current platform is Linux, checking distribution");
|
||||
const std::string OsReleasePath = "/etc/os-release";
|
||||
|
||||
std::string DistroID = "";
|
||||
std::string DistroVersion = "";
|
||||
|
||||
if (fs::exists(OsReleasePath)) {
|
||||
std::ifstream OsRelease(OsReleasePath);
|
||||
std::string Line {};
|
||||
while (std::getline(OsRelease, Line)) {
|
||||
if (Line.starts_with("ID=")) {
|
||||
DistroID = Line.substr(3);
|
||||
} else if (Line.starts_with("VERSION_ID=\"")) {
|
||||
DistroVersion = Line.substr(strlen("VERSION_ID=\""));
|
||||
// skip closing quote
|
||||
DistroVersion = DistroVersion.substr(0, DistroVersion.size() - 1);
|
||||
} else if (Line.starts_with("VERSION_ID=")) {
|
||||
DistroVersion = Line.substr(strlen("VERSION_ID="));
|
||||
}
|
||||
}
|
||||
}
|
||||
beammp_infof("Distribution: {} {}", DistroID, DistroVersion);
|
||||
|
||||
std::string Distro = {};
|
||||
if (DistroVersion.empty()) {
|
||||
Distro = DistroID;
|
||||
} else {
|
||||
Distro = DistroID + "." + DistroVersion;
|
||||
}
|
||||
|
||||
Postfix = fmt::format(".{}.{}", Distro, Arch);
|
||||
#else
|
||||
beammp_infof("BeamMP doesn't provide binaries for this OS, please update manually");
|
||||
std::exit(1);
|
||||
#endif
|
||||
|
||||
beammp_infof("Looking for BeamMP-Server{}", Postfix);
|
||||
|
||||
// check if the release exists for that platform
|
||||
std::string DownloadURL = "";
|
||||
try {
|
||||
for (const auto& Asset : ReleaseInfo.at("assets")) {
|
||||
if (Asset.at("name").get<std::string>() == "BeamMP-Server" + Postfix) {
|
||||
DownloadURL = Asset.at("browser_download_url");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
beammp_errorf("Failed to parse GitHub API's release assets: {}", e.what());
|
||||
std::exit(1);
|
||||
}
|
||||
if (DownloadURL.empty()) {
|
||||
beammp_infof("BeamMP doesn't provide binaries for this platform or distribution (release 'BeamMP-Server{}' not found in the release assets), please update manually", Postfix);
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
// download urls exist, ask if the user wants to do a major update
|
||||
if (MajorOutdated) {
|
||||
beammp_warnf("The update from v{} to v{} is a major update, which is likely to *break* any Lua Plugins. Please make sure you have read the release notes at {} before proceeding!", Current.AsString(), NewVersion.AsString(), ReleaseInfo.at("html_url").get<std::string>());
|
||||
#if !defined(WIN32)
|
||||
if (!isatty(STDIN_FILENO)) {
|
||||
beammp_errorf("Refusing to do a major version update non-interactively. Run this again in a TTY or update manually");
|
||||
std::exit(1);
|
||||
}
|
||||
#endif
|
||||
fmt::print("\n");
|
||||
int ch;
|
||||
do {
|
||||
fmt::print("Do you wish to proceed with this update? [y/n] ");
|
||||
std::string Input;
|
||||
std::getline(std::cin, Input);
|
||||
if (Input.empty()) {
|
||||
continue;
|
||||
}
|
||||
ch = Input.at(0);
|
||||
} while (tolower(ch) != 'y' && tolower(ch) != 'n');
|
||||
if (tolower(ch) == 'n') {
|
||||
beammp_error("Cancelling update");
|
||||
std::exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
beammp_info("Downloading latest release from github.com...");
|
||||
|
||||
http::SSLClient DlClient("github.com");
|
||||
DlClient.set_follow_location(true);
|
||||
auto ReleaseRes = DlClient.Get(DownloadURL.substr(strlen("https://github.com")), ProgressReport);
|
||||
|
||||
if (!ReleaseRes || ReleaseRes->status < 200 || ReleaseRes->status >= 300) {
|
||||
beammp_errorf("Failed to fetch binary: {}. Please update manually", to_string(ReleaseRes.error()));
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
beammp_info("Download complete!");
|
||||
|
||||
auto Temp = InvokedAs + ".temp";
|
||||
beammp_infof("Creating '{}'", Temp);
|
||||
FILE* Out = std::fopen(Temp.c_str(), "w+");
|
||||
if (!Out) {
|
||||
beammp_errorf("Failed to update executable, because a temporary file couldn't be created: {}", std::strerror(errno));
|
||||
std::exit(1);
|
||||
}
|
||||
auto n = std::fwrite(ReleaseRes->body.data(), 1, ReleaseRes->body.size(), Out);
|
||||
if (n != ReleaseRes->body.size()) {
|
||||
beammp_errorf("Failed to update executable, because a temporary file couldn't be written to: {}", std::strerror(errno));
|
||||
std::fclose(Out);
|
||||
std::exit(1);
|
||||
}
|
||||
std::fclose(Out);
|
||||
|
||||
#if defined(__linux)
|
||||
beammp_infof("Removing '{}'", InvokedAs);
|
||||
struct stat st;
|
||||
if (stat(InvokedAs.c_str(), &st) != 0) {
|
||||
// shouldn't happen at this point
|
||||
beammp_errorf("Failed to stat original executable: {}", std::strerror(errno));
|
||||
std::exit(1);
|
||||
}
|
||||
auto Ret = unlink(InvokedAs.c_str());
|
||||
if (Ret != 0) {
|
||||
beammp_errorf("Failed to remove executable: {}", std::strerror(errno));
|
||||
std::exit(1);
|
||||
}
|
||||
beammp_infof("Replacing '{}' with '{}'", InvokedAs, Temp);
|
||||
fs::rename(Temp, InvokedAs);
|
||||
if (chmod(InvokedAs.c_str(), st.st_mode) != 0) {
|
||||
beammp_warnf("Failed to set file mode to 0{:o}: {}. File may not be executable.", st.st_mode, std::strerror(errno));
|
||||
}
|
||||
#elif defined(WIN32)
|
||||
auto DeleteMe = InvokedAs + ".delete_me";
|
||||
std::filesystem::rename(InvokedAs, DeleteMe);
|
||||
std::filesystem::rename(Temp, InvokedAs);
|
||||
int Attr = GetFileAttributesA(DeleteMe.c_str());
|
||||
if ((Attr & FILE_ATTRIBUTE_HIDDEN) == 0) {
|
||||
SetFileAttributesA(DeleteMe.c_str(), Attr | FILE_ATTRIBUTE_HIDDEN);
|
||||
}
|
||||
#else
|
||||
beammp_error("Not implemented");
|
||||
std::exit(4);
|
||||
#endif
|
||||
|
||||
// make sure the user knows that it was a success, on windows wait for return???
|
||||
|
||||
beammp_infof("SUCCESSFULLY UPDATED to v{}. Please launch the server manually to start the new version!", NewVersion.AsString());
|
||||
|
||||
std::exit(0);
|
||||
}
|
||||
@@ -11,9 +11,7 @@
|
||||
#include "TPluginMonitor.h"
|
||||
#include "TResourceManager.h"
|
||||
#include "TServer.h"
|
||||
#include "Update.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
@@ -35,9 +33,6 @@ ARGUMENTS:
|
||||
including the path given in --config.
|
||||
--version
|
||||
Prints version info and exits.
|
||||
--update
|
||||
Starts an interactive update to the newest
|
||||
version of BeamMP-Server.
|
||||
|
||||
EXAMPLES:
|
||||
BeamMP-Server --config=../MyWestCoastServerConfig.toml
|
||||
@@ -77,7 +72,6 @@ int BeamMPServerMain(MainArguments Arguments) {
|
||||
ArgsParser Parser;
|
||||
Parser.RegisterArgument({ "help" }, ArgsParser::NONE);
|
||||
Parser.RegisterArgument({ "version" }, ArgsParser::NONE);
|
||||
Parser.RegisterArgument({ "update" }, ArgsParser::NONE);
|
||||
Parser.RegisterArgument({ "config" }, ArgsParser::HAS_VALUE);
|
||||
Parser.RegisterArgument({ "working-directory" }, ArgsParser::HAS_VALUE);
|
||||
Parser.Parse(Arguments.List);
|
||||
@@ -92,15 +86,6 @@ int BeamMPServerMain(MainArguments Arguments) {
|
||||
Application::Console().WriteRaw("BeamMP-Server v" + Application::ServerVersionString());
|
||||
return 0;
|
||||
}
|
||||
if (Parser.FoundArgument({ "update" })) {
|
||||
Update::PerformUpdate(Arguments.InvokedAs);
|
||||
return 123;
|
||||
}
|
||||
// check that there is no old update file
|
||||
auto OldFile = Arguments.InvokedAs + ".delete_me";
|
||||
if (fs::exists(OldFile)) {
|
||||
fs::remove(OldFile);
|
||||
}
|
||||
|
||||
std::string ConfigPath = "ServerConfig.toml";
|
||||
if (Parser.FoundArgument({ "config" })) {
|
||||
|
||||
Reference in New Issue
Block a user