Compare commits

..

1 Commits

41 changed files with 254 additions and 792 deletions
+13 -14
View File
@@ -3,11 +3,11 @@ name: Linux
on:
push:
branches:
- "develop"
- "minor"
- 'develop'
- 'minor'
pull_request:
env:
env:
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
VCPKG_FORCE_SYSTEM_BINARIES: 1
CMAKE_BUILD_TYPE: "Release"
@@ -20,9 +20,9 @@ jobs:
matrix:
include:
- distro: debian
version: 12
version: 11
- distro: debian
version: 13
version: 12
- distro: ubuntu
version: 22.04
- distro: ubuntu
@@ -32,7 +32,7 @@ jobs:
steps:
- name: get-cmake
uses: lukka/get-cmake@v3.28.1
- name: Export GitHub Actions cache environment variables
uses: actions/github-script@v6
with:
@@ -47,8 +47,7 @@ jobs:
- uses: actions/checkout@v2
with:
submodules: "recursive"
fetch-depth: 0
submodules: 'recursive'
- name: Git config safe directory
shell: bash
@@ -85,16 +84,16 @@ jobs:
run: ./bin/BeamMP-Server-tests
arm64-matrix:
runs-on: ubuntu-22.04-arm
runs-on: [Linux, ARM64]
env:
VCPKG_DEFAULT_TRIPLET: "arm64-linux"
strategy:
matrix:
include:
- distro: debian
version: 12
version: 11
- distro: debian
version: 13
version: 12
- distro: ubuntu
version: 22.04
- distro: ubuntu
@@ -104,7 +103,7 @@ jobs:
steps:
- name: get-cmake
uses: lukka/get-cmake@v3.28.1
- name: Export GitHub Actions cache environment variables
uses: actions/github-script@v6
with:
@@ -119,8 +118,7 @@ jobs:
- uses: actions/checkout@v2
with:
submodules: "recursive"
fetch-depth: 0
submodules: 'recursive'
- name: Git config safe directory
shell: bash
@@ -155,3 +153,4 @@ jobs:
- name: Test
run: ./bin/BeamMP-Server-tests
+16 -19
View File
@@ -3,7 +3,7 @@ on:
push:
# Sequence of patterns matched against refs/tags
tags:
- "v*" # Push events to matching v*, i.e. v1.0, v20.15.10
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
env:
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
@@ -14,8 +14,8 @@ jobs:
create-release:
runs-on: ubuntu-latest
name: Create Release
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
steps:
- name: Create Release
id: create_release
@@ -39,9 +39,9 @@ jobs:
matrix:
include:
- distro: debian
version: 12
version: 11
- distro: debian
version: 13
version: 12
- distro: ubuntu
version: 22.04
- distro: ubuntu
@@ -51,7 +51,7 @@ jobs:
steps:
- name: get-cmake
uses: lukka/get-cmake@v3.28.1
- name: Export GitHub Actions cache environment variables
uses: actions/github-script@v6
with:
@@ -66,8 +66,7 @@ jobs:
- uses: actions/checkout@v2
with:
submodules: "recursive"
fetch-depth: 0
submodules: 'recursive'
- name: Git config safe directory
shell: bash
@@ -83,7 +82,7 @@ jobs:
run: bash ./scripts/${{ matrix.distro }}-${{ matrix.version }}/3-build.sh
- name: Upload Release Asset
id: upload-release-asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -105,15 +104,15 @@ jobs:
asset_content_type: application/x-elf
arm64-matrix:
runs-on: ubuntu-22.04-arm
runs-on: [Linux, ARM64]
needs: create-release
strategy:
matrix:
include:
- distro: debian
version: 12
version: 11
- distro: debian
version: 13
version: 12
- distro: ubuntu
version: 22.04
- distro: ubuntu
@@ -126,7 +125,7 @@ jobs:
steps:
- name: get-cmake
uses: lukka/get-cmake@v3.28.1
- name: Export GitHub Actions cache environment variables
uses: actions/github-script@v6
with:
@@ -141,8 +140,7 @@ jobs:
- uses: actions/checkout@v2
with:
submodules: "recursive"
fetch-depth: 0
submodules: 'recursive'
- name: Git config safe directory
shell: bash
@@ -158,7 +156,7 @@ jobs:
run: bash ./scripts/${{ matrix.distro }}-${{ matrix.version }}/3-build.sh
- name: Upload Release Asset
id: upload-release-asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -195,8 +193,7 @@ jobs:
- uses: actions/checkout@v2
with:
submodules: "recursive"
fetch-depth: 0
submodules: 'recursive'
- name: Create Build Environment
shell: bash
@@ -207,7 +204,7 @@ jobs:
run: bash ./scripts/windows/2-build.sh
- name: Upload Release Asset
id: upload-release-asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-1
View File
@@ -26,7 +26,6 @@ jobs:
- uses: actions/checkout@v2
with:
submodules: 'recursive'
fetch-depth: 0
- name: Setup vcpkg
uses: lukka/run-vcpkg@v11
+1 -2
View File
@@ -148,8 +148,7 @@ if(UNIX)
endif(UNIX)
if (WIN32)
add_compile_definitions(_WIN32_WINNT=0x0A00)
add_compile_options("-D_WIN32_WINNT=0x0601")
add_compile_options("/bigobj")
endif(WIN32)
+3 -7
View File
@@ -24,7 +24,6 @@
#include <queue>
#include <string>
#include <unordered_set>
#include <utility>
#include "BoostAliases.h"
#include "Common.h"
@@ -57,14 +56,14 @@ public:
~TClient();
TClient& operator=(const TClient&) = delete;
void AddNewCar(int Ident, const nlohmann::json& Data);
void SetCarData(int Ident, const nlohmann::json& Data);
void AddNewCar(int Ident, const std::string& Data);
void SetCarData(int Ident, const std::string& Data);
void SetCarPosition(int Ident, const std::string& Data);
TVehicleDataLockPair GetAllCars();
void SetName(const std::string& Name) { mName = Name; }
void SetRoles(const std::string& Role) { mRole = Role; }
void SetIdentifier(const std::string& key, const std::string& value) { mIdentifiers[key] = value; }
nlohmann::json GetCarData(int Ident);
std::string GetCarData(int Ident);
std::string GetCarPositionRaw(int Ident);
void SetUDPAddr(const ip::udp::endpoint& Addr) { mUDPAddress = Addr; }
void SetTCPSock(ip::tcp::socket&& CSock) { mSocket = std::move(CSock); }
@@ -102,8 +101,6 @@ public:
[[nodiscard]] TServer& Server() const;
void UpdatePingTime();
int SecondsSinceLastPing();
void SetMagic(std::vector<uint8_t> magic) { mMagic = std::move(magic); }
[[nodiscard]] const std::vector<uint8_t>& GetMagic() const { return mMagic; }
private:
void InsertVehicle(int ID, const std::string& Data);
@@ -128,7 +125,6 @@ private:
std::string mDID;
int mID = -1;
std::chrono::time_point<std::chrono::high_resolution_clock> mLastPingTime = std::chrono::high_resolution_clock::now();
std::vector<uint8_t> mMagic;
};
std::optional<std::weak_ptr<TClient>> GetClient(class TServer& Server, int ID);
+2 -5
View File
@@ -74,7 +74,7 @@ public:
static TConsole& Console() { return mConsole; }
static std::string ServerVersionString();
static const Version& ServerVersion() { return mVersion; }
static Version ClientMinimumVersion() { return Version { 2, 7, 0 }; }
static Version ClientMinimumVersion() { return Version { 2, 2, 0 }; }
static std::string PPS() { return mPPS; }
static void SetPPS(const std::string& NewPPS) { mPPS = NewPPS; }
@@ -86,8 +86,6 @@ public:
};
}
static std::string GetServerCheckUrl() { return "https://check.beammp.com"; }
static std::string GetBackendUrlForAuth() { return "https://auth.beammp.com"; }
static std::string GetBackendUrlForSocketIO() { return "https://backend.beammp.com"; }
static void CheckForUpdates();
@@ -129,11 +127,10 @@ private:
static inline std::mutex mShutdownHandlersMutex {};
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
static inline Version mVersion { 3, 9, 2 };
static inline Version mVersion { 3, 8, 1 };
};
void SplitString(std::string const& str, const char delim, std::vector<std::string>& out);
std::string LowerString(std::string str);
std::string ThreadName(bool DebugModeOverride = false);
void RegisterThread(const std::string& str);
-2
View File
@@ -26,9 +26,7 @@ enum class Key {
// provider settings
PROVIDER_UPDATE_MESSAGE,
PROVIDER_DISABLE_CONFIG,
PROVIDER_DISABLE_MP_SET,
PROVIDER_PORT_ENV,
PROVIDER_IP_ENV
};
std::optional<std::string> Get(Key key);
+1 -2
View File
@@ -34,9 +34,8 @@ namespace MP {
std::pair<bool, std::string> TriggerClientEventJson(int PlayerID, const std::string& EventName, const sol::table& Data);
inline size_t GetPlayerCount() { return Engine->Server().ClientCount(); }
std::pair<bool, std::string> DropPlayer(int ID, std::optional<std::string> MaybeReason);
std::pair<bool, std::string> SendChatMessage(int ID, const std::string& Message, const bool& LogChat = true);
std::pair<bool, std::string> SendChatMessage(int ID, const std::string& Message);
std::pair<bool, std::string> SendNotification(int ID, const std::string& Message, const std::string& Icon, const std::string& Category);
std::pair<bool, std::string> ConfirmationDialog(int ID, const std::string& Title, const std::string& Body, const sol::table& buttons, const std::string& InteractionID, const bool& warning = false, const bool& reportToServer = true, const bool& reportToExtensions = true);
std::pair<bool, std::string> RemoveVehicle(int PlayerID, int VehicleID);
void Set(int ConfigID, sol::object NewValue);
TLuaValue Get(int ConfigID);
-1
View File
@@ -79,7 +79,6 @@ struct Settings {
General_Map,
General_AuthKey,
General_Private,
General_IP,
General_Port,
General_MaxCars,
General_LogChat,
-6
View File
@@ -59,9 +59,6 @@ private:
void Command_Settings(const std::string& cmd, const std::vector<std::string>& args);
void Command_Clear(const std::string&, const std::vector<std::string>& args);
void Command_Version(const std::string& cmd, const std::vector<std::string>& args);
void Command_ProtectMod(const std::string& cmd, const std::vector<std::string>& args);
void Command_ReloadMods(const std::string& cmd, const std::vector<std::string>& args);
void Command_NetTest(const std::string& cmd, const std::vector<std::string>& args);
void Command_Say(const std::string& FullCommand);
bool EnsureArgsCount(const std::vector<std::string>& args, size_t n);
@@ -80,9 +77,6 @@ private:
{ "clear", [this](const auto& a, const auto& b) { Command_Clear(a, b); } },
{ "say", [this](const auto&, const auto&) { Command_Say(""); } }, // shouldn't actually be called
{ "version", [this](const auto& a, const auto& b) { Command_Version(a, b); } },
{ "protectmod", [this](const auto& a, const auto& b) { Command_ProtectMod(a, b); } },
{ "reloadmods", [this](const auto& a, const auto& b) { Command_ReloadMods(a, b); } },
{ "nettest", [this](const auto& a, const auto& b) { Command_NetTest(a, b); } },
};
std::unique_ptr<Commandline> mCommandline { nullptr };
+3 -1
View File
@@ -192,7 +192,9 @@ public:
for (const auto& Event : mLuaEvents.at(EventName)) {
for (const auto& Function : Event.second) {
if (Event.first != IgnoreId) {
Results.push_back(EnqueueFunctionCall(Event.first, Function, Arguments, EventName));
auto Result = EnqueueFunctionCall(Event.first, Function, Arguments, EventName);
Results.push_back(Result);
AddResultToCheck(Result);
}
}
}
+6 -13
View File
@@ -34,11 +34,9 @@ public:
[[nodiscard]] bool TCPSend(TClient& c, const std::vector<uint8_t>& Data, bool IsSync = false);
[[nodiscard]] bool SendLarge(TClient& c, std::vector<uint8_t> Data, bool isSync = false);
[[nodiscard]] bool Respond(TClient& c, const std::vector<uint8_t>& MSG, bool Rel, bool isSync = false);
std::shared_ptr<TClient> CreateClient(boost::asio::ip::tcp::socket&& TCPSock);
std::shared_ptr<TClient> CreateClient(ip::tcp::socket&& TCPSock);
std::vector<uint8_t> TCPRcv(TClient& c);
void ClientKick(TClient& c, const std::string& R);
void DisconnectClient(const std::weak_ptr<TClient>& c, const std::string& R);
void DisconnectClient(TClient& c, const std::string& R);
[[nodiscard]] bool SyncClient(const std::weak_ptr<TClient>& c);
void Identify(TConnection&& client);
std::shared_ptr<TClient> Authentication(TConnection&& ClientConnection);
@@ -46,9 +44,6 @@ public:
[[nodiscard]] bool UDPSend(TClient& Client, std::vector<uint8_t> Data);
void SendToAll(TClient* c, const std::vector<uint8_t>& Data, bool Self, bool Rel);
void UpdatePlayer(TClient& Client);
boost::system::error_code ReadWithTimeout(TConnection& Connection, void* Buf, size_t Len, std::chrono::steady_clock::duration Timeout);
TResourceManager& ResourceManager() const { return mResourceManager; }
private:
void UDPServerMain();
@@ -56,15 +51,13 @@ private:
TServer& mServer;
TPPSMonitor& mPPSMonitor;
boost::asio::ip::udp::socket mUDPSock;
ip::udp::socket mUDPSock;
TResourceManager& mResourceManager;
std::thread mUDPThread;
std::thread mTCPThread;
std::mutex mOpenIDMutex;
std::map<std::string, uint16_t> mClientMap;
std::mutex mClientMapMutex;
std::vector<uint8_t> UDPRcvFromClient(boost::asio::ip::udp::endpoint& ClientEndpoint);
std::vector<uint8_t> UDPRcvFromClient(ip::udp::endpoint& ClientEndpoint);
void OnConnect(const std::weak_ptr<TClient>& c);
void TCPClient(const std::weak_ptr<TClient>& c);
void Looper(const std::weak_ptr<TClient>& c);
@@ -72,9 +65,9 @@ private:
void OnDisconnect(const std::weak_ptr<TClient>& ClientPtr);
void Parse(TClient& c, const std::vector<uint8_t>& Packet);
void SendFile(TClient& c, const std::string& Name);
static bool TCPSendRaw(TClient& C, boost::asio::ip::tcp::socket& socket, const uint8_t* Data, size_t Size);
void SendFileToClient(TClient& c, size_t Size, const std::string& Name);
static const uint8_t* SendSplit(TClient& c, boost::asio::ip::tcp::socket& Socket, const uint8_t* DataPtr, size_t Size);
static bool TCPSendRaw(TClient& C, ip::tcp::socket& socket, const uint8_t* Data, size_t Size);
static void SendFileToClient(TClient& c, size_t Size, const std::string& Name);
static const uint8_t* SendSplit(TClient& c, ip::tcp::socket& Socket, const uint8_t* DataPtr, size_t Size);
};
std::string HashPassword(const std::string& str);
+2 -2
View File
@@ -30,10 +30,10 @@ public:
[[nodiscard]] std::string TrimmedList() const { return mTrimmedList; }
[[nodiscard]] std::string FileSizes() const { return mFileSizes; }
[[nodiscard]] int ModsLoaded() const { return mModsLoaded; }
[[nodiscard]] nlohmann::json GetMods() const { return mMods; }
[[nodiscard]] std::string NewFileList() const;
void RefreshFiles();
void SetProtected(const std::string& ModName, bool Protected);
private:
size_t mMaxModSize = 0;
+1 -1
View File
@@ -44,7 +44,7 @@ public:
void ForEachClient(const std::function<bool(std::weak_ptr<TClient>)>& Fn);
size_t ClientCount() const;
void GlobalParser(const std::weak_ptr<TClient>& Client, std::vector<uint8_t>&& Packet, TPPSMonitor& PPSMonitor, TNetwork& Network, bool udp);
void GlobalParser(const std::weak_ptr<TClient>& Client, std::vector<uint8_t>&& Packet, TPPSMonitor& PPSMonitor, TNetwork& Network);
static void HandleEvent(TClient& c, const std::string& Data);
RWMutex& GetClientMutex() const { return mClientsMutex; }
+4 -7
View File
@@ -18,12 +18,11 @@
#pragma once
#include <nlohmann/json.hpp>
#include <string>
class TVehicleData final {
public:
TVehicleData(int ID, nlohmann::json Data);
TVehicleData(int ID, std::string Data);
~TVehicleData();
// We cannot delete this, since vector needs to be able to copy when it resizes.
// Deleting this causes some wacky template errors which are hard to decipher,
@@ -33,16 +32,14 @@ public:
[[nodiscard]] bool IsInvalid() const { return mID == -1; }
[[nodiscard]] int ID() const { return mID; }
[[nodiscard]] nlohmann::json Data() const { return mData; }
[[nodiscard]] std::string DataAsPacket(const std::string& Role, const std::string& Name, int ID) const;
void SetData(const nlohmann::json& Data) { mData = Data; }
[[nodiscard]] std::string Data() const { return mData; }
void SetData(const std::string& Data) { mData = Data; }
bool operator==(const TVehicleData& v) const { return mID == v.mID; }
private:
int mID { -1 };
nlohmann::json mData;
std::string mData;
};
// TODO: unused now, remove?
+1 -1
View File
@@ -4,4 +4,4 @@ set -ex
./vcpkg/bootstrap-vcpkg.sh
cmake . -B bin $1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O3 -g -Wl,-z,norelro -Wl,--hash-style=gnu -Wl,-z,noseparate-code -ffunction-sections -fdata-sections -Wl,--gc-sections -Wl,--export-dynamic" -DBeamMP-Server_ENABLE_LTO=ON
cmake . -B bin $1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O3 -g -Wl,-z,norelro -Wl,--hash-style=gnu -Wl,-z,noseparate-code -ffunction-sections -fdata-sections -Wl,--gc-sections" -DBeamMP-Server_ENABLE_LTO=ON
+1 -1
View File
@@ -4,4 +4,4 @@ set -ex
./vcpkg/bootstrap-vcpkg.sh
cmake . -B bin $1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O3 -g -Wl,-z,norelro -Wl,--hash-style=gnu -Wl,-z,noseparate-code -ffunction-sections -fdata-sections -Wl,--gc-sections -Wl,--export-dynamic" -DBeamMP-Server_ENABLE_LTO=ON
cmake . -B bin $1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O3 -g -Wl,-z,norelro -Wl,--hash-style=gnu -Wl,-z,noseparate-code -ffunction-sections -fdata-sections -Wl,--gc-sections" -DBeamMP-Server_ENABLE_LTO=ON
-7
View File
@@ -1,7 +0,0 @@
#!/bin/bash
set -ex
apt-get update -y
apt-get install -y liblua5.3-0 liblua5.3-dev curl zip unzip tar cmake make git g++ ninja-build
-7
View File
@@ -1,7 +0,0 @@
#!/bin/bash
set -ex
git config --global --add safe.directory $(pwd)
git config --global --add safe.directory $(pwd)/vcpkg
git config --global --add safe.directory $(pwd)/deps/commandline
-7
View File
@@ -1,7 +0,0 @@
#!/bin/bash
set -ex
./vcpkg/bootstrap-vcpkg.sh
cmake . -B bin $1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O3 -g -Wl,-z,norelro -Wl,--hash-style=gnu -Wl,-z,noseparate-code -ffunction-sections -fdata-sections -Wl,--gc-sections -Wl,--export-dynamic" -DBeamMP-Server_ENABLE_LTO=ON
-5
View File
@@ -1,5 +0,0 @@
#!/bin/bash
set -ex
cmake --build bin --parallel -t BeamMP-Server-tests
-10
View File
@@ -1,10 +0,0 @@
#!/bin/bash
set -ex
cmake --build bin --parallel -t BeamMP-Server
objcopy --only-keep-debug bin/BeamMP-Server bin/BeamMP-Server.debug
objcopy --add-gnu-debuglink bin/BeamMP-Server bin/BeamMP-Server.debug
strip -s bin/BeamMP-Server
@@ -1,8 +0,0 @@
#!/bin/bash
set -ex
apt-get update -y
apt-get install -y liblua5.3-0 curl
+1 -1
View File
@@ -4,4 +4,4 @@ set -ex
./vcpkg/bootstrap-vcpkg.sh
cmake . -B bin $1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O3 -g -Wl,-z,norelro -Wl,--hash-style=gnu -Wl,-z,noseparate-code -ffunction-sections -fdata-sections -Wl,--gc-sections -Wl,--export-dynamic" -DBeamMP-Server_ENABLE_LTO=ON
cmake . -B bin $1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O3 -g -Wl,-z,norelro -Wl,--hash-style=gnu -Wl,-z,noseparate-code -ffunction-sections -fdata-sections -Wl,--gc-sections" -DBeamMP-Server_ENABLE_LTO=ON
+1 -1
View File
@@ -4,4 +4,4 @@ set -ex
./vcpkg/bootstrap-vcpkg.sh
cmake . -B bin $1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O3 -g -Wl,-z,norelro -Wl,--hash-style=gnu -Wl,-z,noseparate-code -ffunction-sections -fdata-sections -Wl,--gc-sections -Wl,--export-dynamic" -DBeamMP-Server_ENABLE_LTO=ON
cmake . -B bin $1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O3 -g -Wl,-z,norelro -Wl,--hash-style=gnu -Wl,-z,noseparate-code -ffunction-sections -fdata-sections -Wl,--gc-sections" -DBeamMP-Server_ENABLE_LTO=ON
+5 -4
View File
@@ -57,7 +57,7 @@ int TClient::GetOpenCarID() const {
return OpenID;
}
void TClient::AddNewCar(int Ident, const nlohmann::json& Data) {
void TClient::AddNewCar(int Ident, const std::string& Data) {
std::unique_lock lock(mVehicleDataMutex);
mVehicleData.emplace_back(Ident, Data);
}
@@ -84,6 +84,7 @@ void TClient::Disconnect(std::string_view Reason) {
if (ec) {
beammp_debugf("Failed to shutdown client socket: {}", ec.message());
}
mSocket.cancel();
mSocket.close(ec);
if (ec) {
beammp_debugf("Failed to close client socket: {}", ec.message());
@@ -98,7 +99,7 @@ void TClient::SetCarPosition(int Ident, const std::string& Data) {
mVehiclePosition[size_t(Ident)] = Data;
}
nlohmann::json TClient::GetCarData(int Ident) {
std::string TClient::GetCarData(int Ident) {
{ // lock
std::unique_lock lock(mVehicleDataMutex);
for (auto& v : mVehicleData) {
@@ -108,10 +109,10 @@ nlohmann::json TClient::GetCarData(int Ident) {
}
} // unlock
DeleteCar(Ident);
return nlohmann::detail::value_t::null;
return "";
}
void TClient::SetCarData(int Ident, const nlohmann::json& Data) {
void TClient::SetCarData(int Ident, const std::string& Data) {
{ // lock
std::unique_lock lock(mVehicleDataMutex);
for (auto& v : mVehicleData) {
-7
View File
@@ -384,13 +384,6 @@ void SplitString(const std::string& str, const char delim, std::vector<std::stri
out.push_back(str.substr(start, end - start));
}
}
std::string LowerString(std::string str) {
std::ranges::transform(str, str.begin(), ::tolower);
return str;
}
static constexpr size_t STARTING_MAX_DECOMPRESSION_BUFFER_SIZE = 15 * 1024 * 1024;
static constexpr size_t MAX_DECOMPRESSION_BUFFER_SIZE = 30 * 1024 * 1024;
-6
View File
@@ -36,15 +36,9 @@ std::string_view Env::ToString(Env::Key key) {
case Key::PROVIDER_DISABLE_CONFIG:
return "BEAMMP_PROVIDER_DISABLE_CONFIG";
break;
case Key::PROVIDER_DISABLE_MP_SET:
return "BEAMMP_PROVIDER_DISABLE_MP_SET";
break;
case Key::PROVIDER_PORT_ENV:
return "BEAMMP_PROVIDER_PORT_ENV";
break;
case Key::PROVIDER_IP_ENV:
return "BEAMMP_PROVIDER_IP_ENV";
break;
}
return "";
}
+4 -55
View File
@@ -148,11 +148,6 @@ static inline std::pair<bool, std::string> InternalTriggerClientEvent(int Player
return { false, "Invalid Player ID" };
}
auto c = MaybeClient.value().lock();
if (!c->IsSyncing() && !c->IsSynced()) {
return { false, "Player hasn't joined yet" };
}
if (!LuaAPI::MP::Engine->Network().Respond(*c, StringToVector(Packet), true)) {
beammp_lua_errorf("Respond failed, dropping client {}", PlayerID);
LuaAPI::MP::Engine->Network().ClientKick(*c, "Disconnected after failing to receive packets");
@@ -178,13 +173,11 @@ std::pair<bool, std::string> LuaAPI::MP::DropPlayer(int ID, std::optional<std::s
return { true, "" };
}
std::pair<bool, std::string> LuaAPI::MP::SendChatMessage(int ID, const std::string& Message, const bool& LogChat) {
std::pair<bool, std::string> LuaAPI::MP::SendChatMessage(int ID, const std::string& Message) {
std::pair<bool, std::string> Result;
std::string Packet = "C:Server: " + Message;
if (ID == -1) {
if (LogChat) {
LogChatMessage("<Server> (to everyone) ", -1, Message);
}
LogChatMessage("<Server> (to everyone) ", -1, Message);
Engine->Network().SendToAll(nullptr, StringToVector(Packet), true, true);
Result.first = true;
} else {
@@ -196,9 +189,7 @@ std::pair<bool, std::string> LuaAPI::MP::SendChatMessage(int ID, const std::stri
Result.second = "Player still syncing data";
return Result;
}
if (LogChat) {
LogChatMessage("<Server> (to \"" + c->GetName() + "\")", -1, Message);
}
LogChatMessage("<Server> (to \"" + c->GetName() + "\")", -1, Message);
if (!Engine->Network().Respond(*c, StringToVector(Packet), true)) {
beammp_errorf("Failed to send chat message back to sender (id {}) - did the sender disconnect?", ID);
// TODO: should we return an error here?
@@ -245,48 +236,6 @@ std::pair<bool, std::string> LuaAPI::MP::SendNotification(int ID, const std::str
return Result;
}
std::pair<bool, std::string> LuaAPI::MP::ConfirmationDialog(int ID, const std::string& Title, const std::string& Body, const sol::table& buttons, const std::string& InteractionID, const bool& warning, const bool& reportToServer, const bool& reportToExtensions) {
std::pair<bool, std::string> Result;
const nlohmann::json PacketBody = {
{ "title", Title },
{ "body", Body },
{ "buttons", nlohmann::json::parse(JsonEncode(buttons), nullptr, false) },
{ "interactionID", InteractionID },
{ "class", warning ? "experimental" : "" },
{ "reportToServer", reportToServer },
{ "reportToExtensions", reportToExtensions }
};
std::string Packet = "D" + PacketBody.dump();
if (ID == -1) {
Engine->Network().SendToAll(nullptr, StringToVector(Packet), true, true);
Result.first = true;
} else {
auto MaybeClient = GetClient(Engine->Server(), ID);
if (MaybeClient) {
auto c = MaybeClient.value().lock();
if (!c->IsSynced()) {
Result.first = false;
Result.second = "Player is not synced yet";
return Result;
}
if (!Engine->Network().Respond(*c, StringToVector(Packet), true)) {
beammp_errorf("Failed to send confirmation dialog to player (id {}) - did the player disconnect?", ID);
Result.first = false;
Result.second = "Failed to send packet";
}
Result.first = true;
} else {
beammp_lua_error("ConfirmationDialog invalid argument [1] invalid ID");
Result.first = false;
Result.second = "Invalid Player ID";
}
return Result;
}
return Result;
}
std::pair<bool, std::string> LuaAPI::MP::RemoveVehicle(int PID, int VID) {
std::pair<bool, std::string> Result;
auto MaybeClient = GetClient(Engine->Server(), PID);
@@ -297,7 +246,7 @@ std::pair<bool, std::string> LuaAPI::MP::RemoveVehicle(int PID, int VID) {
return Result;
}
auto c = MaybeClient.value().lock();
if (c->GetCarData(VID) != nlohmann::detail::value_t::null) {
if (!c->GetCarData(VID).empty()) {
std::string Destroy = "Od:" + std::to_string(PID) + "-" + std::to_string(VID);
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onVehicleDeleted", "", PID, VID));
Engine->Network().SendToAll(nullptr, StringToVector(Destroy), true, true);
-2
View File
@@ -28,7 +28,6 @@ Settings::Settings() {
{ General_Map, std::string("/levels/gridmap_v2/info.json") },
{ General_AuthKey, std::string("") },
{ General_Private, true },
{ General_IP, "::"},
{ General_Port, 30814 },
{ General_MaxCars, 1 },
{ General_LogChat, true },
@@ -48,7 +47,6 @@ Settings::Settings() {
{ { "General", "Map" }, { General_Map, READ_WRITE } },
{ { "General", "AuthKey" }, { General_AuthKey, NO_ACCESS } },
{ { "General", "Private" }, { General_Private, READ_ONLY } },
{ { "General", "IP" }, { General_IP, READ_ONLY } },
{ { "General", "Port" }, { General_Port, READ_ONLY } },
{ { "General", "MaxCars" }, { General_MaxCars, READ_WRITE } },
{ { "General", "LogChat" }, { General_LogChat, READ_ONLY } },
-10
View File
@@ -34,8 +34,6 @@ static constexpr std::string_view StrDebug = "Debug";
static constexpr std::string_view EnvStrDebug = "BEAMMP_DEBUG";
static constexpr std::string_view StrPrivate = "Private";
static constexpr std::string_view EnvStrPrivate = "BEAMMP_PRIVATE";
static constexpr std::string_view StrIP = "IP";
static constexpr std::string_view EnvStrIP = "BEAMMP_IP";
static constexpr std::string_view StrPort = "Port";
static constexpr std::string_view EnvStrPort = "BEAMMP_PORT";
static constexpr std::string_view StrMaxCars = "MaxCars";
@@ -140,8 +138,6 @@ void TConfig::FlushToFile() {
data["General"][StrInformationPacket.data()] = Application::Settings.getAsBool(Settings::Key::General_InformationPacket);
data["General"][StrAllowGuests.data()] = Application::Settings.getAsBool(Settings::Key::General_AllowGuests);
SetComment(data["General"][StrAllowGuests.data()].comments(), " Whether to allow guests");
data["General"][StrIP.data()] = Application::Settings.getAsString(Settings::Key::General_IP);
SetComment(data["General"][StrIP.data()].comments(), " The IP address to bind the server to, this is NOT related to your public IP. Can be used if your machine has multiple network interfaces");
data["General"][StrPort.data()] = Application::Settings.getAsInt(Settings::Key::General_Port);
data["General"][StrName.data()] = Application::Settings.getAsString(Settings::Key::General_Name);
SetComment(data["General"][StrTags.data()].comments(), " Add custom identifying tags to your server to make it easier to find. Format should be TagA,TagB,TagC. Note the comma seperation.");
@@ -258,11 +254,6 @@ void TConfig::ParseFromFile(std::string_view name) {
} else {
TryReadValue(data, "General", StrPort, EnvStrPort, Settings::Key::General_Port);
}
if (Env::Get(Env::Key::PROVIDER_IP_ENV).has_value()) {
TryReadValue(data, "General", StrIP, Env::Get(Env::Key::PROVIDER_IP_ENV).value(), Settings::Key::General_IP);
} else {
TryReadValue(data, "General", StrIP, EnvStrIP, Settings::Key::General_IP);
}
TryReadValue(data, "General", StrMaxCars, EnvStrMaxCars, Settings::Key::General_MaxCars);
TryReadValue(data, "General", StrMaxPlayers, EnvStrMaxPlayers, Settings::Key::General_MaxPlayers);
TryReadValue(data, "General", StrMap, EnvStrMap, Settings::Key::General_Map);
@@ -313,7 +304,6 @@ void TConfig::PrintDebug() {
beammp_debug(std::string(StrPrivate) + ": " + std::string(Application::Settings.getAsBool(Settings::Key::General_Private) ? "true" : "false"));
beammp_debug(std::string(StrInformationPacket) + ": " + std::string(Application::Settings.getAsBool(Settings::Key::General_InformationPacket) ? "true" : "false"));
beammp_debug(std::string(StrPort) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_Port)));
beammp_debug(std::string(StrIP) + ": \"" + Application::Settings.getAsString(Settings::Key::General_IP) + "\"");
beammp_debug(std::string(StrMaxCars) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxCars)));
beammp_debug(std::string(StrMaxPlayers) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers)));
beammp_debug(std::string(StrMap) + ": \"" + Application::Settings.getAsString(Settings::Key::General_Map) + "\"");
+14 -67
View File
@@ -24,7 +24,6 @@
#include "CustomAssert.h"
#include "LuaAPI.h"
#include "TLuaEngine.h"
#include "Http.h"
#include <ctime>
#include <lua.hpp>
@@ -160,7 +159,7 @@ void TConsole::ChangeToRegularConsole() {
}
bool TConsole::EnsureArgsCount(const std::vector<std::string>& args, size_t n) {
if (n == 0 && !args.empty()) {
if (n == 0 && args.size() != 0) {
Application::Console().WriteRaw("This command expects no arguments.");
return false;
} else if (args.size() != n) {
@@ -198,7 +197,7 @@ void TConsole::Command_Lua(const std::string&, const std::vector<std::string>& a
} else {
Application::Console().WriteRaw("Lua state '" + NewStateId + "' is not a known state. Didn't switch to Lua.");
}
} else if (args.empty()) {
} else if (args.size() == 0) {
ChangeToLuaConsole(mDefaultStateId);
}
}
@@ -209,18 +208,16 @@ void TConsole::Command_Help(const std::string&, const std::vector<std::string>&
}
static constexpr const char* sHelpString = R"(
Commands:
help displays this help
exit shuts down the server
kick <name> [reason] kicks specified player with an optional reason
list lists all players and info about them
say <message> sends the message to all players in chat
lua [state id] switches to lua, optionally into a specific state id's lua
settings [command] sets or gets settings for the server, run `settings help` for more info
status how the server is doing and what it's up to
clear clears the console window
version displays the server version
protectmod <name> <value> sets whether a mod is protected, value can be true or false
reloadmods reloads all mods from the Resources Client folder)";
help displays this help
exit shuts down the server
kick <name> [reason] kicks specified player with an optional reason
list lists all players and info about them
say <message> sends the message to all players in chat
lua [state id] switches to lua, optionally into a specific state id's lua
settings [command] sets or gets settings for the server, run `settings help` for more info
status how the server is doing and what it's up to
clear clears the console window
version displays the server version)";
Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString));
}
@@ -265,56 +262,6 @@ void TConsole::Command_Version(const std::string& cmd, const std::vector<std::st
std::string openssl_version = fmt::format("OpenSSL: v{}.{}.{}", OPENSSL_VERSION_MAJOR, OPENSSL_VERSION_MINOR, OPENSSL_VERSION_PATCH);
Application::Console().WriteRaw(openssl_version);
}
void TConsole::Command_ProtectMod(const std::string& cmd, const std::vector<std::string>& args) {
if (!EnsureArgsCount(args, 2)) {
return;
}
const auto& ModName = args.at(0);
const auto& Protect = args.at(1);
for (auto mod : mLuaEngine->Network().ResourceManager().GetMods()) {
if (mod["file_name"].get<std::string>() == ModName) {
mLuaEngine->Network().ResourceManager().SetProtected(ModName, Protect == "true");
Application::Console().WriteRaw("Mod " + ModName + " is now " + (Protect == "true" ? "protected" : "unprotected"));
return;
}
}
Application::Console().WriteRaw("Mod " + ModName + " not found.");
}
void TConsole::Command_ReloadMods(const std::string& cmd, const std::vector<std::string>& args) {
if (!EnsureArgsCount(args, 0)) {
return;
}
mLuaEngine->Network().ResourceManager().RefreshFiles();
Application::Console().WriteRaw("Mods reloaded.");
}
void TConsole::Command_NetTest(const std::string& cmd, const std::vector<std::string>& args) {
unsigned int status = 0;
std::string T = Http::GET(
Application::GetServerCheckUrl() + "/api/v2/beammp/" + std::to_string(Application::Settings.getAsInt(Settings::Key::General_Port)), &status
);
beammp_debugf("Status and response from Server Check API: {0}, {1}", status, T);
auto Doc = nlohmann::json::parse(T, nullptr, false);
if (Doc.is_discarded() || !Doc.is_object()) {
beammp_warn("Failed to parse Server Check API response, however the server will most likely still work correctly.");
} else {
std::string status = Doc["status"];
std::string details = "Response from Server Check API: " + std::string(Doc["details"]);
if (status == "ok") {
beammp_info(details);
} else {
beammp_warn(details);
}
}
}
void TConsole::Command_Kick(const std::string&, const std::vector<std::string>& args) {
if (!EnsureArgsCount(args, 1, size_t(-1))) {
@@ -423,7 +370,7 @@ void TConsole::Command_Settings(const std::string&, const std::vector<std::strin
settings set <category> <setting> <value> sets specified setting to value
)";
if (args.empty()) {
if (args.size() == 0) {
beammp_errorf("No arguments specified for command 'settings'!");
Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString));
return;
@@ -705,7 +652,7 @@ void TConsole::RunAsCommand(const std::string& cmd, bool IgnoreNotACommand) {
}
}
}
if (NonNilFutures.empty()) {
if (NonNilFutures.size() == 0) {
if (!IgnoreNotACommand) {
Application::Console().WriteRaw("Error: Unknown command: '" + cmd + "'. Type 'help' to see a list of valid commands.");
}
+54 -53
View File
@@ -22,7 +22,6 @@
#include "CustomAssert.h"
#include "Http.h"
#include "LuaAPI.h"
#include "Env.h"
#include "Profiling.h"
#include "TLuaPlugin.h"
#include "sol/object.hpp"
@@ -31,12 +30,30 @@
#include <condition_variable>
#include <fmt/core.h>
#include <nlohmann/json.hpp>
#include <optional>
#include <random>
#include <sol/stack_core.hpp>
#include <thread>
#include <tuple>
TLuaEngine* LuaAPI::MP::Engine;
static sol::protected_function AddTraceback(sol::state_view StateView, sol::protected_function RawFn);
static std::optional<sol::function> GetLuaHandler(sol::state_view StateView, const std::string Handler, const std::string EventName);
static std::optional<sol::function> GetLuaHandler(sol::state_view StateView, const std::string Handler, const std::string EventName) {
if (!StateView)
return std::nullopt;
auto Res = StateView.safe_script("return " + Handler, sol::script_pass_on_error);
if (!Res.valid()) {
beammp_errorf("invalid handler for event \"{}\". handler: \"{}\"", EventName, Handler);
} else if (Res.get_type() == sol::type::function) {
return Res.get<sol::function>();
}
return std::nullopt;
}
TLuaEngine::TLuaEngine()
: mResourceServerPath(fs::path(Application::Settings.getAsString(Settings::Key::General_ResourceFolder)) / "Server") {
Application::SetSubsystemStatus("LuaEngine", Application::Status::Starting);
@@ -65,11 +82,10 @@ TEST_CASE("TLuaEngine ctor & dtor") {
void TLuaEngine::operator()() {
RegisterThread("LuaEngine");
Application::SetSubsystemStatus("LuaEngine", Application::Status::Good);
// lua engine main thread
beammp_infof("Lua v{}.{}.{}", LUA_VERSION_MAJOR, LUA_VERSION_MINOR, LUA_VERSION_RELEASE);
CollectAndInitPlugins();
Application::SetSubsystemStatus("LuaEngine", Application::Status::Good);
// now call all onInit's
auto Futures = TriggerEvent("onInit", "");
WaitForAll(Futures, std::chrono::seconds(5));
@@ -128,7 +144,7 @@ void TLuaEngine::operator()() {
}
}
}
if (mLuaStates.empty()) {
if (mLuaStates.size() == 0) {
beammp_trace("No Lua states, event loop running extremely sparsely");
Application::SleepSafeSeconds(10);
} else {
@@ -492,8 +508,11 @@ sol::table TLuaEngine::StateThreadData::Lua_TriggerGlobalEvent(const std::string
sol::variadic_results LocalArgs = JsonStringToArray(Str);
for (const auto& Handler : MyHandlers) {
auto Fn = mStateView[Handler];
if (Fn.valid()) {
auto Res = GetLuaHandler(mStateView, Handler, EventName);
if (Res.has_value()) {
sol::function Fn = Res.value();
Fn = AddTraceback(mStateView, Fn);
auto LuaResult = Fn(LocalArgs);
auto Result = std::make_shared<TLuaResult>();
if (LuaResult.valid()) {
@@ -501,7 +520,9 @@ sol::table TLuaEngine::StateThreadData::Lua_TriggerGlobalEvent(const std::string
Result->Result = LuaResult;
} else {
Result->Error = true;
Result->ErrorMessage = "Function result in TriggerGlobalEvent was invalid";
sol::error Err = LuaResult;
Result->ErrorMessage = Err.what();
beammp_errorf("An error occured while executing local event handler \"{}\" for event \"{}\": {}", Handler, EventName, Result->ErrorMessage);
}
Result->MarkAsReady();
Return.push_back(Result);
@@ -543,8 +564,9 @@ sol::table TLuaEngine::StateThreadData::Lua_TriggerLocalEvent(const std::string&
sol::table Result = mStateView.create_table();
int i = 1;
for (const auto& Handler : mEngine->GetEventHandlersForState(EventName, mStateId)) {
auto Fn = mStateView[Handler];
if (Fn.valid() && Fn.get_type() == sol::type::function) {
auto Res = GetLuaHandler(mStateView, Handler, EventName);
if (Res.has_value()) {
sol::function Fn = Res.value();
auto FnRet = Fn(EventArgs);
if (FnRet.valid()) {
Result.set(i, FnRet);
@@ -662,7 +684,7 @@ sol::table TLuaEngine::StateThreadData::Lua_GetPlayerVehicles(int ID) {
sol::state_view StateView(mState);
sol::table Result = StateView.create_table();
for (const auto& v : VehicleData) {
Result[v.ID()] = v.DataAsPacket(Client->GetRoles(), Client->GetName(), Client->GetID()).substr(3);
Result[v.ID()] = v.Data().substr(3);
}
return Result;
} else
@@ -727,25 +749,6 @@ static void AddToTable(sol::table& table, const std::string& left, const T& valu
}
}
static bool mDisableMPSet = [] {
auto DisableMPSet = Env::Get(Env::Key::PROVIDER_DISABLE_MP_SET).value_or("false");
return DisableMPSet == "true" || DisableMPSet == "1";
}();
static auto GetSettingName = [](int id) -> const char* {
switch (id) {
case 0: return "Debug";
case 1: return "Private";
case 2: return "MaxCars";
case 3: return "MaxPlayers";
case 4: return "Map";
case 5: return "Name";
case 6: return "Description";
case 7: return "InformationPacket";
default: return "Unknown";
}
};
static void JsonDecodeRecursive(sol::state_view& StateView, sol::table& table, const std::string& left, const nlohmann::json& right) {
switch (right.type()) {
case nlohmann::detail::value_t::null:
@@ -877,15 +880,7 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateI
MPTable.set_function("GetPositionRaw", [&](int PID, int VID) -> std::pair<sol::table, std::string> {
return Lua_GetPositionRaw(PID, VID);
});
MPTable.set_function("SendChatMessage", [&](sol::variadic_args Args) {
if (Args.size() == 2) {
LuaAPI::MP::SendChatMessage(Args.get<int>(0), Args.get<std::string>(1));
} else if (Args.size() == 3) {
LuaAPI::MP::SendChatMessage(Args.get<int>(0), Args.get<std::string>(1), Args.get<bool>(2));
} else {
beammp_lua_error("SendChatMessage expects 2 or 3 arguments.");
}
});
MPTable.set_function("SendChatMessage", &LuaAPI::MP::SendChatMessage);
MPTable.set_function("SendNotification", [&](sol::variadic_args Args) {
if (Args.size() == 2) {
LuaAPI::MP::SendNotification(Args.get<int>(0), Args.get<std::string>(1), "", Args.get<std::string>(1));
@@ -897,12 +892,6 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateI
beammp_lua_error("SendNotification expects 2, 3 or 4 arguments.");
}
});
MPTable.set_function("ConfirmationDialog", sol::overload(
&LuaAPI::MP::ConfirmationDialog,
[&](const int& ID, const std::string& Title, const std::string& Body, const sol::table& Buttons, const std::string& InteractionID) {
LuaAPI::MP::ConfirmationDialog(ID, Title, Body, Buttons, InteractionID);
}
));
MPTable.set_function("GetPlayers", [&]() -> sol::table {
return Lua_GetPlayers();
});
@@ -946,13 +935,7 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateI
MPTable.set_function("CancelEventTimer", [&](const std::string& EventName) {
mEngine->CancelEventTimers(EventName, mStateId);
});
if (mDisableMPSet) {
MPTable.set_function("Set", [this](int ConfigID, sol::object NewValue) {
beammp_lua_errorf("A script ({}) tried to call MP.Set to change setting '{}' but this was blocked by your server provider.", mStateId, GetSettingName(ConfigID));
});
} else {
MPTable.set_function("Set", &LuaAPI::MP::Set);
}
MPTable.set_function("Set", &LuaAPI::MP::Set);
MPTable.set_function("Get", &LuaAPI::MP::Get);
auto UtilTable = StateView.create_named_table("Util");
@@ -1116,6 +1099,21 @@ void TLuaEngine::StateThreadData::RegisterEvent(const std::string& EventName, co
mEngine->RegisterEvent(EventName, mStateId, FunctionName);
}
static sol::protected_function AddTraceback(sol::state_view StateView, sol::protected_function RawFn) {
StateView["INTERNAL_ERROR_HANDLER"] = [](lua_State *L) {
auto Error = sol::stack::get<std::optional<std::string>>(L);
std::string ErrorString = "<Unknown error>";
if (Error.has_value()) {
ErrorString = Error.value();
}
auto DebugTracebackFn = sol::state_view(L).globals().get<sol::table>("debug").get<sol::protected_function>("traceback");
// 2 = start collecting the trace one above the current function (1=current function)
std::string Traceback = DebugTracebackFn(ErrorString, 2);
return sol::stack::push(L, Traceback);
};
return sol::protected_function(RawFn, StateView["INTERNAL_ERROR_HANDLER"]);
}
void TLuaEngine::StateThreadData::operator()() {
RegisterThread("Lua:" + mStateId);
while (!Application::IsShuttingDown()) {
@@ -1180,8 +1178,10 @@ void TLuaEngine::StateThreadData::operator()() {
// TODO: Use TheQueuedFunction.EventName for errors, warnings, etc
Result->StateId = mStateId;
sol::state_view StateView(mState);
auto Fn = StateView[FnName];
if (Fn.valid() && Fn.get_type() == sol::type::function) {
auto Res = GetLuaHandler(StateView, FnName, TheQueuedFunction.EventName);
if (Res.has_value()) {
sol::function Fn = Res.value();
std::vector<sol::object> LuaArgs;
for (const auto& Arg : Args) {
if (Arg.valueless_by_exception()) {
@@ -1216,6 +1216,7 @@ void TLuaEngine::StateThreadData::operator()() {
break;
}
}
Fn = AddTraceback(StateView, Fn);
auto Res = Fn(sol::as_args(LuaArgs));
if (Res.valid()) {
Result->Error = false;
+59 -208
View File
@@ -32,16 +32,10 @@
#include <boost/asio/ip/address_v6.hpp>
#include <boost/asio/ip/v6_only.hpp>
#include <cstring>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <zlib.h>
typedef boost::asio::detail::socket_option::integer<SOL_SOCKET, SO_RCVTIMEO> rcv_timeout_option;
static constexpr uint8_t MAX_CONCURRENT_CONNECTIONS = 10;
static constexpr uint8_t MAX_GLOBAL_CONNECTIONS = 128;
static constexpr uint8_t READ_TIMEOUT_S = 10; //seconds
std::vector<uint8_t> StringToVector(const std::string& Str) {
return std::vector<uint8_t>(Str.data(), Str.data() + Str.size());
}
@@ -91,17 +85,9 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R
void TNetwork::UDPServerMain() {
RegisterThread("UDPServer");
// listen on all ipv6 addresses
ip::udp::endpoint UdpListenEndpoint(ip::make_address("::"), Application::Settings.getAsInt(Settings::Key::General_Port));
boost::system::error_code ec;
auto address = boost::asio::ip::make_address(Application::Settings.getAsString(Settings::Key::General_IP), ec);
if (ec) {
beammp_errorf("Failed to parse IP: {}", ec.message());
Application::GracefullyShutdown();
}
boost::asio::ip::udp::endpoint UdpListenEndpoint(address, Application::Settings.getAsInt(Settings::Key::General_Port));
mUDPSock.open(UdpListenEndpoint.protocol(), ec);
if (ec) {
beammp_error("open() failed: " + ec.message());
@@ -121,17 +107,17 @@ void TNetwork::UDPServerMain() {
Application::GracefullyShutdown();
}
Application::SetSubsystemStatus("UDPNetwork", Application::Status::Good);
beammp_info(("Vehicle data network online on port ") + std::to_string(UdpListenEndpoint.port()) + (" with a Max of ")
beammp_info(("Vehicle data network online on port ") + std::to_string(Application::Settings.getAsInt(Settings::Key::General_Port)) + (" with a Max of ")
+ std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers)) + (" Clients"));
while (!Application::IsShuttingDown()) {
try {
boost::asio::ip::udp::endpoint remote_client_ep {};
ip::udp::endpoint remote_client_ep {};
std::vector<uint8_t> Data = UDPRcvFromClient(remote_client_ep);
if (Data.empty()) {
continue;
}
if (Data.size() == 1 && Data.at(0) == 'P') {
mUDPSock.send_to(boost::asio::const_buffer("P", 1), remote_client_ep, {}, ec);
mUDPSock.send_to(const_buffer("P", 1), remote_client_ep, {}, ec);
// ignore errors
(void)ec;
continue;
@@ -152,30 +138,23 @@ void TNetwork::UDPServerMain() {
}
if (Client->GetID() == ID) {
if (Client->GetUDPAddr() == boost::asio::ip::udp::endpoint {} && !Client->IsUDPConnected() && !Client->GetMagic().empty()) {
if (Data.size() != 66) {
beammp_debugf("Invalid size for UDP value. IP: {} ID: {}", remote_client_ep.address().to_string(), ID);
// not initialized yet
if (Client->GetUDPAddr() == ip::udp::endpoint {} || !Client->IsUDPConnected()) {
// same IP (just a sanity check)
if (remote_client_ep.address() == Client->GetTCPSock().remote_endpoint().address()) {
Client->SetUDPAddr(remote_client_ep);
Client->SetIsUDPConnected(true);
beammp_debugf("UDP connected for client {}", ID);
} else {
beammp_debugf("Denied initial UDP packet due to IP mismatch");
return false;
}
const std::vector Magic(Data.begin() + 2, Data.end());
if (Magic != Client->GetMagic()) {
beammp_debugf("Invalid value for UDP IP: {} ID: {}", remote_client_ep.address().to_string(), ID);
return false;
}
Client->SetMagic({});
Client->SetUDPAddr(remote_client_ep);
Client->SetIsUDPConnected(true);
return false;
}
if (Client->GetUDPAddr() == remote_client_ep) {
Data.erase(Data.begin(), Data.begin() + 2);
mServer.GlobalParser(ClientPtr, std::move(Data), mPPSMonitor, *this, true);
mServer.GlobalParser(ClientPtr, std::move(Data), mPPSMonitor, *this);
} else {
beammp_debugf("Ignored UDP packet for Client {} due to remote address mismatch. Source: {}, Client: {}", ID, remote_client_ep.address().to_string(), Client->GetUDPAddr().address().to_string());
beammp_debugf("Ignored UDP packet due to remote address mismatch");
return false;
}
}
@@ -191,17 +170,12 @@ void TNetwork::UDPServerMain() {
void TNetwork::TCPServerMain() {
RegisterThread("TCPServer");
// listen on all ipv6 addresses
auto port = uint16_t(Application::Settings.getAsInt(Settings::Key::General_Port));
ip::tcp::endpoint ListenEp(ip::make_address("::"), port);
beammp_infof("Listening on 0.0.0.0:{0} and [::]:{0}", port);
ip::tcp::socket Listener(mServer.IoCtx());
boost::system::error_code ec;
auto address = boost::asio::ip::make_address(Application::Settings.getAsString(Settings::Key::General_IP), ec);
if (ec) {
beammp_errorf("Failed to parse IP: {}", ec.message());
return;
}
boost::asio::ip::tcp::endpoint ListenEp(address,
uint16_t(Application::Settings.getAsInt(Settings::Key::General_Port)));
boost::asio::ip::tcp::socket Listener(mServer.IoCtx());
Listener.open(ListenEp.protocol(), ec);
if (ec) {
beammp_errorf("Failed to open socket: {}", ec.message());
@@ -226,7 +200,7 @@ void TNetwork::TCPServerMain() {
ec.message());
}
boost::asio::ip::tcp::acceptor Acceptor(mServer.IoCtx(), ListenEp);
ip::tcp::acceptor Acceptor(mServer.IoCtx(), ListenEp);
Acceptor.listen(socket_base::max_listen_connections, ec);
if (ec) {
beammp_errorf("listen() failed, which is needed for the server to operate. "
@@ -235,7 +209,6 @@ void TNetwork::TCPServerMain() {
Application::GracefullyShutdown();
}
Application::SetSubsystemStatus("TCPNetwork", Application::Status::Good);
beammp_infof("Listening on {0} port {1}", ListenEp.address().to_string(), static_cast<uint16_t>(ListenEp.port()));
beammp_info("Vehicle event network online");
do {
try {
@@ -243,28 +216,14 @@ void TNetwork::TCPServerMain() {
beammp_debug("shutdown during TCP wait for accept loop");
break;
}
boost::asio::ip::tcp::endpoint ClientEp;
boost::asio::ip::tcp::socket ClientSocket = Acceptor.accept(ClientEp, ec);
std::string ClientIP = ClientEp.address().to_string();
if (!ec) {
mClientMapMutex.lock();
if (mClientMap[ClientIP] >= MAX_CONCURRENT_CONNECTIONS) {
beammp_debugf("The connection was rejected for {}, as it had {} concurrent connections.", ClientIP, mClientMap[ClientIP]);
}
else if (mClientMap.size() >= MAX_GLOBAL_CONNECTIONS) {
beammp_debugf("The connection was rejected for {}, as there are {} global connections.", ClientIP, mClientMap.size());
}
else {
TConnection Conn { std::move(ClientSocket), ClientEp };
std::thread ID(&TNetwork::Identify, this, std::move(Conn));
ID.detach(); // TODO: Add to a queue and attempt to join periodically
mClientMap[ClientIP]++;
}
mClientMapMutex.unlock();
}
else {
ip::tcp::endpoint ClientEp;
ip::tcp::socket ClientSocket = Acceptor.accept(ClientEp, ec);
if (ec) {
beammp_errorf("Failed to accept() new client: {}", ec.message());
}
TConnection Conn { std::move(ClientSocket), ClientEp };
std::thread ID(&TNetwork::Identify, this, std::move(Conn));
ID.detach(); // TODO: Add to a queue and attempt to join periodically
} catch (const std::exception& e) {
beammp_errorf("Exception in accept routine: {}", e.what());
}
@@ -280,22 +239,11 @@ void TNetwork::Identify(TConnection&& RawConnection) {
RegisterThreadAuto();
char Code;
boost::system::error_code ec = ReadWithTimeout(RawConnection, &Code, 1, std::chrono::seconds(READ_TIMEOUT_S));
boost::system::error_code ec;
read(RawConnection.Socket, buffer(&Code, 1), ec);
if (ec) {
// TODO: is this right?!
beammp_debug("Error occured reading code");
RawConnection.Socket.shutdown(socket_base::shutdown_both, ec);
mClientMapMutex.lock();
{
std::string ClientIP = RawConnection.SockAddr.address().to_string();
if (mClientMap[ClientIP] > 0) {
mClientMap[ClientIP]--;
}
if (mClientMap[ClientIP] == 0) {
mClientMap.erase(ClientIP);
}
}
mClientMapMutex.unlock();
return;
}
std::shared_ptr<TClient> Client { nullptr };
@@ -306,7 +254,8 @@ void TNetwork::Identify(TConnection&& RawConnection) {
beammp_errorf("Old download packet detected - the client is wildly out of date, this will be ignored");
return;
} else if (Code == 'P') {
boost::asio::write(RawConnection.Socket, boost::asio::buffer("P"), ec);
boost::system::error_code ec;
write(RawConnection.Socket, buffer("P"), ec);
return;
} else if (Code == 'I') {
const std::string Data = Application::Settings.getAsBool(Settings::Key::General_InformationPacket) ? THeartbeatThread::lastCall : "";
@@ -318,25 +267,14 @@ void TNetwork::Identify(TConnection&& RawConnection) {
std::memcpy(ToSend.data() + sizeof(Size), Data.data(), Data.size());
boost::system::error_code ec;
boost::asio::write(RawConnection.Socket, boost::asio::buffer(ToSend), ec);
write(RawConnection.Socket, buffer(ToSend), ec);
} else {
beammp_errorf("Invalid code got in Identify: '{}'", Code);
}
} catch (const std::exception& e) {
beammp_errorf("Error during handling of code {} - client left in invalid state, closing socket: {}", Code, e.what());
boost::system::error_code ec;
RawConnection.Socket.shutdown(boost::asio::socket_base::shutdown_both, ec);
mClientMapMutex.lock();
{
std::string ClientIP = RawConnection.SockAddr.address().to_string();
if (mClientMap[ClientIP] > 0) {
mClientMap[ClientIP]--;
}
if (mClientMap[ClientIP] == 0) {
mClientMap.erase(ClientIP);
}
}
mClientMapMutex.unlock();
RawConnection.Socket.shutdown(socket_base::shutdown_both, ec);
if (ec) {
beammp_debugf("Failed to shutdown client socket: {}", ec.message());
}
@@ -369,11 +307,6 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
Client->SetIdentifier("ip", ip);
beammp_tracef("This thread is ip {} ({})", ip, RawConnection.SockAddr.address().to_v6().is_v4_mapped() ? "IPv4 mapped IPv6" : "IPv6");
if (Application::GetSubsystemStatuses().at("Main") == Application::Status::Starting) {
ClientKick(*Client, "The server is still starting, please try joining again later.");
return nullptr;
}
beammp_info("Identifying new ClientConnection...");
auto Data = TCPRcv(*Client);
@@ -434,7 +367,7 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
try {
nlohmann::json AuthRes = nlohmann::json::parse(AuthResStr);
if (AuthRes["username"].is_string() && AuthRes["username"].size() > 0 && AuthRes["roles"].is_string()
if (AuthRes["username"].is_string() && AuthRes["roles"].is_string()
&& AuthRes["guest"].is_boolean() && AuthRes["identifiers"].is_array()) {
Client->SetName(AuthRes["username"]);
@@ -468,7 +401,7 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
return true;
}
if (Cl->GetName() == Client->GetName() && Cl->IsGuest() == Client->IsGuest()) {
DisconnectClient(Cl, "Stale Client (not a real player)");
Cl->Disconnect("Stale Client (not a real player)");
return false;
}
@@ -531,7 +464,7 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
return Client;
}
std::shared_ptr<TClient> TNetwork::CreateClient(boost::asio::ip::tcp::socket&& TCPSock) {
std::shared_ptr<TClient> TNetwork::CreateClient(ip::tcp::socket&& TCPSock) {
auto c = std::make_shared<TClient>(mServer, std::move(TCPSock));
return c;
}
@@ -564,10 +497,10 @@ bool TNetwork::TCPSend(TClient& c, const std::vector<uint8_t>& Data, bool IsSync
std::memcpy(ToSend.data(), &Size, sizeof(Size));
std::memcpy(ToSend.data() + sizeof(Size), Data.data(), Data.size());
boost::system::error_code ec;
boost::asio::write(Sock, boost::asio::buffer(ToSend), ec);
write(Sock, buffer(ToSend), ec);
if (ec) {
beammp_debugf("write(): {}", ec.message());
DisconnectClient(c, "write() failed");
c.Disconnect("write() failed");
return false;
}
c.UpdatePingTime();
@@ -585,7 +518,7 @@ std::vector<uint8_t> TNetwork::TCPRcv(TClient& c) {
boost::system::error_code ec;
std::array<uint8_t, sizeof(Header)> HeaderData;
boost::asio::read(Sock, boost::asio::buffer(HeaderData), ec);
read(Sock, buffer(HeaderData), ec);
if (ec) {
// TODO: handle this case (read failed)
beammp_debugf("TCPRcv: Reading header failed: {}", ec.message());
@@ -601,16 +534,14 @@ std::vector<uint8_t> TNetwork::TCPRcv(TClient& c) {
std::vector<uint8_t> Data;
// TODO: This is arbitrary, this needs to be handled another way
bool isUnauthenticated = c.GetName().empty();
int32_t maxHeaderSize = isUnauthenticated ? 4096 : int32_t(100 * MB);
if (Header < maxHeaderSize) {
if (Header < int32_t(100 * MB)) {
Data.resize(Header);
} else {
ClientKick(c, "Header size limit exceeded");
beammp_warn("Client " + c.GetName() + " (" + std::to_string(c.GetID()) + ") sent header larger than expected - assuming malicious intent and disconnecting the client.");
beammp_warn("Client " + c.GetName() + " (" + std::to_string(c.GetID()) + ") sent header of >100MB - assuming malicious intent and disconnecting the client.");
return {};
}
auto N = boost::asio::read(Sock, boost::asio::buffer(Data), ec);
auto N = read(Sock, buffer(Data), ec);
if (ec) {
// TODO: handle this case properly
beammp_debugf("TCPRcv: Reading data failed: {}", ec.message());
@@ -645,32 +576,7 @@ void TNetwork::ClientKick(TClient& c, const std::string& R) {
if (!TCPSend(c, StringToVector("K" + R))) {
beammp_debugf("tried to kick player '{}' (id {}), but was already disconnected", c.GetName(), c.GetID());
}
DisconnectClient(c, "Kicked");
}
void TNetwork::DisconnectClient(const std::weak_ptr<TClient> &c, const std::string &R)
{
if (auto locked = c.lock()) {
DisconnectClient(*locked, R);
}
else {
beammp_debugf("Tried to disconnect a non existant client with reason: {}", R);
}
}
void TNetwork::DisconnectClient(TClient &c, const std::string &R)
{
if (c.IsDisconnected()) return;
std::string ClientIP = c.GetIdentifiers().at("ip");
mClientMapMutex.lock();
if (mClientMap[ClientIP] > 0) {
mClientMap[ClientIP]--;
}
if (mClientMap[ClientIP] == 0) {
mClientMap.erase(ClientIP);
}
mClientMapMutex.unlock();
c.Disconnect(R);
c.Disconnect("Kicked");
}
void TNetwork::Looper(const std::weak_ptr<TClient>& c) {
@@ -695,7 +601,7 @@ void TNetwork::Looper(const std::weak_ptr<TClient>& c) {
} // end locked context
// beammp_debug("sending a missed packet: " + QData);
if (!TCPSend(*Client, QData, true)) {
DisconnectClient(Client, "Failed to TCPSend while clearing the missed packet queue");
Client->Disconnect("Failed to TCPSend while clearing the missed packet queue");
std::unique_lock lock(Client->MissedPacketQueueMutex());
while (!Client->MissedPacketQueue().empty()) {
Client->MissedPacketQueue().pop();
@@ -732,16 +638,10 @@ void TNetwork::TCPClient(const std::weak_ptr<TClient>& c) {
auto res = TCPRcv(*Client);
if (res.empty()) {
beammp_debug("TCPRcv empty");
DisconnectClient(Client, "TCPRcv failed");
break;
}
try {
mServer.GlobalParser(c, std::move(res), mPPSMonitor, *this, false);
} catch (const std::exception& e) {
beammp_warnf("Failed to receive/parse packet via TCP from client {}: {}", Client->GetID(), e.what());
DisconnectClient(Client, "Failed to parse packet");
Client->Disconnect("TCPRcv failed");
break;
}
mServer.GlobalParser(c, std::move(res), mPPSMonitor, *this);
}
if (QueueSync.joinable())
@@ -770,34 +670,6 @@ void TNetwork::UpdatePlayer(TClient& Client) {
//(void)Respond(Client, Packet, true);
}
boost::system::error_code TNetwork::ReadWithTimeout(TConnection& Connection, void *Buf, size_t Len, std::chrono::steady_clock::duration Timeout)
{
io_context TimerIO;
steady_timer Timer(TimerIO);
Timer.expires_after(Timeout);
std::atomic<bool> TimedOut = false;
Timer.async_wait([&](const boost::system::error_code& ec) {
if (!ec) {
TimedOut = true;
Connection.Socket.cancel();
}
});
std::thread TimerThread([&]() { TimerIO.run(); });
boost::system::error_code ReadEc;
boost::asio::read(Connection.Socket, boost::asio::buffer(Buf, Len), ReadEc);
TimerIO.stop();
TimerThread.join();
if (TimedOut.load()) {
return error::timed_out; // synthesize a clean timeout error
}
return ReadEc; //Succes!
}
void TNetwork::OnDisconnect(const std::weak_ptr<TClient>& ClientPtr) {
std::shared_ptr<TClient> LockedClientPtr { nullptr };
try {
@@ -825,7 +697,7 @@ void TNetwork::OnDisconnect(const std::weak_ptr<TClient>& ClientPtr) {
Packet.clear();
auto Futures = LuaAPI::MP::Engine->TriggerEvent("onPlayerDisconnect", "", c.GetID());
LuaAPI::MP::Engine->WaitForAll(Futures);
DisconnectClient(c, "Already Disconnected (OnDisconnect)");
c.Disconnect("Already Disconnected (OnDisconnect)");
mServer.RemoveClient(ClientPtr);
}
@@ -860,18 +732,6 @@ void TNetwork::OnConnect(const std::weak_ptr<TClient>& c) {
SyncResources(*LockedClient);
if (LockedClient->IsDisconnected())
return;
std::vector<unsigned char> buf(64);
int ret = RAND_bytes(buf.data(), buf.size());
if (ret != 1) {
unsigned long error = ERR_get_error();
beammp_errorf("RAND_bytes failed with error code {}", error);
beammp_assert(ret != 1);
return;
}
LockedClient->SetMagic(buf);
buf.insert(buf.begin(), 'U');
(void)Respond(*LockedClient, buf, true);
(void)Respond(*LockedClient, StringToVector("M" + Application::Settings.getAsString(Settings::Key::General_Map)), true); // Send the Map on connect
beammp_info(LockedClient->GetName() + " : Connected");
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onPlayerJoining", "", LockedClient->GetID()));
@@ -907,7 +767,7 @@ void TNetwork::Parse(TClient& c, const std::vector<uint8_t>& Packet) {
case 'S':
if (SubCode == 'R') {
beammp_debug("Sending Mod Info");
std::string ToSend = mResourceManager.GetMods().dump();
std::string ToSend = mResourceManager.NewFileList();
beammp_debugf("Mod Info: {}", ToSend);
if (!TCPSend(c, StringToVector(ToSend))) {
ClientKick(c, "TCP Send 'SY' failed");
@@ -929,15 +789,6 @@ void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) {
return;
}
auto FileName = fs::path(UnsafeName).filename().string();
for (auto mod : mResourceManager.GetMods()) {
if (mod["file_name"].get<std::string>() == FileName && mod["protected"] == true) {
beammp_warn("Client tried to access protected file " + UnsafeName);
DisconnectClient(c, "Mod is protected thus cannot be downloaded");
return;
}
}
FileName = Application::Settings.getAsString(Settings::Key::General_ResourceFolder) + "/Client/" + FileName;
if (!std::filesystem::exists(FileName)) {
@@ -997,7 +848,7 @@ void TNetwork::SendFileToClient(TClient& c, size_t Size, const std::string& Name
Data.resize(Split);
else
Data.resize(Size);
boost::asio::ip::tcp::socket* TCPSock = &c.GetTCPSock();
ip::tcp::socket* TCPSock = &c.GetTCPSock();
std::streamsize Sent = 0;
while (!c.IsDisconnected() && Sent < Size) {
size_t Diff = Size - Sent;
@@ -1006,7 +857,7 @@ void TNetwork::SendFileToClient(TClient& c, size_t Size, const std::string& Name
f.read(reinterpret_cast<char*>(Data.data()), Split);
if (!TCPSendRaw(c, *TCPSock, Data.data(), Split)) {
if (!c.IsDisconnected())
DisconnectClient(c, "TCPSendRaw failed in mod download (1)");
c.Disconnect("TCPSendRaw failed in mod download (1)");
break;
}
Sent += Split;
@@ -1015,7 +866,7 @@ void TNetwork::SendFileToClient(TClient& c, size_t Size, const std::string& Name
f.read(reinterpret_cast<char*>(Data.data()), Diff);
if (!TCPSendRaw(c, *TCPSock, Data.data(), int32_t(Diff))) {
if (!c.IsDisconnected())
DisconnectClient(c, "TCPSendRaw failed in mod download (2)");
c.Disconnect("TCPSendRaw failed in mod download (2)");
break;
}
Sent += Diff;
@@ -1024,9 +875,9 @@ void TNetwork::SendFileToClient(TClient& c, size_t Size, const std::string& Name
#endif
}
bool TNetwork::TCPSendRaw(TClient& C, boost::asio::ip::tcp::socket& socket, const uint8_t* Data, size_t Size) {
bool TNetwork::TCPSendRaw(TClient& C, ip::tcp::socket& socket, const uint8_t* Data, size_t Size) {
boost::system::error_code ec;
boost::asio::write(socket, boost::asio::buffer(Data, Size), ec);
write(socket, buffer(Data, Size), ec);
if (ec) {
beammp_errorf("Failed to send raw data to client: {}", ec.message());
return false;
@@ -1095,7 +946,7 @@ bool TNetwork::SyncClient(const std::weak_ptr<TClient>& c) {
res = false;
return false;
}
res = Respond(*LockedClient, StringToVector(v.DataAsPacket(client->GetRoles(), client->GetName(), client->GetID())), true, true);
res = Respond(*LockedClient, StringToVector(v.Data()), true, true);
}
}
@@ -1167,20 +1018,20 @@ bool TNetwork::UDPSend(TClient& Client, std::vector<uint8_t> Data) {
CompressProperly(Data);
}
boost::system::error_code ec;
mUDPSock.send_to(boost::asio::buffer(Data), Addr, 0, ec);
mUDPSock.send_to(buffer(Data), Addr, 0, ec);
if (ec) {
beammp_debugf("UDP sendto() failed: {}", ec.message());
if (!Client.IsDisconnected())
DisconnectClient(Client, "UDP send failed");
Client.Disconnect("UDP send failed");
return false;
}
return true;
}
std::vector<uint8_t> TNetwork::UDPRcvFromClient(boost::asio::ip::udp::endpoint& ClientEndpoint) {
std::vector<uint8_t> TNetwork::UDPRcvFromClient(ip::udp::endpoint& ClientEndpoint) {
std::array<char, 1024> Ret {};
boost::system::error_code ec;
const auto Rcv = mUDPSock.receive_from(boost::asio::mutable_buffer(Ret.data(), Ret.size()), ClientEndpoint, 0, ec);
const auto Rcv = mUDPSock.receive_from(mutable_buffer(Ret.data(), Ret.size()), ClientEndpoint, 0, ec);
if (ec) {
beammp_errorf("UDP recvfrom() failed: {}", ec.message());
return {};
+14 -19
View File
@@ -57,26 +57,21 @@ void TPluginMonitor::operator()() {
mFileTimes[Pair.first] = CurrentTime;
// grandparent of the path should be Resources/Server
if (fs::equivalent(fs::path(Pair.first).parent_path().parent_path(), mPath)) {
if (LowerString(fs::path(Pair.first).extension().string()) == ".lua") {
beammp_infof("File \"{}\" changed, reloading", Pair.first);
// is in root folder, so reload
std::ifstream FileStream(Pair.first, std::ios::in | std::ios::binary);
auto Size = std::filesystem::file_size(Pair.first);
auto Contents = std::make_shared<std::string>();
Contents->resize(Size);
FileStream.read(Contents->data(), Contents->size());
TLuaChunk Chunk(Contents, Pair.first, fs::path(Pair.first).parent_path().string());
auto StateID = mEngine->GetStateIDForPlugin(fs::path(Pair.first).parent_path());
auto Res = mEngine->EnqueueScript(StateID, Chunk);
Res->WaitUntilReady();
if (Res->Error) {
beammp_lua_errorf("Error while hot-reloading \"{}\": {}", Pair.first, Res->ErrorMessage);
} else {
mEngine->ReportErrors(mEngine->TriggerLocalEvent(StateID, "onInit"));
mEngine->ReportErrors(mEngine->TriggerEvent("onFileChanged", "", Pair.first));
}
beammp_infof("File \"{}\" changed, reloading", Pair.first);
// is in root folder, so reload
std::ifstream FileStream(Pair.first, std::ios::in | std::ios::binary);
auto Size = std::filesystem::file_size(Pair.first);
auto Contents = std::make_shared<std::string>();
Contents->resize(Size);
FileStream.read(Contents->data(), Contents->size());
TLuaChunk Chunk(Contents, Pair.first, fs::path(Pair.first).parent_path().string());
auto StateID = mEngine->GetStateIDForPlugin(fs::path(Pair.first).parent_path());
auto Res = mEngine->EnqueueScript(StateID, Chunk);
Res->WaitUntilReady();
if (Res->Error) {
beammp_lua_errorf("Error while hot-reloading \"{}\": {}", Pair.first, Res->ErrorMessage);
} else {
beammp_debugf("File \"{}\" changed, not reloading because it's not a lua file. Triggering 'onFileChanged' event instead", Pair.first);
mEngine->ReportErrors(mEngine->TriggerLocalEvent(StateID, "onInit"));
mEngine->ReportErrors(mEngine->TriggerEvent("onFileChanged", "", Pair.first));
}
} else {
+4 -104
View File
@@ -58,58 +58,22 @@ TResourceManager::TResourceManager() {
Application::SetSubsystemStatus("ResourceManager", Application::Status::Good);
}
std::string TResourceManager::NewFileList() const {
return mMods.dump();
}
void TResourceManager::RefreshFiles() {
mMods.clear();
std::unique_lock Lock(mModsMutex);
std::string Path = Application::Settings.getAsString(Settings::Key::General_ResourceFolder) + "/Client";
nlohmann::json modsDB;
if (std::filesystem::exists(Path + "/mods.json")) {
try {
std::ifstream stream(Path + "/mods.json");
stream >> modsDB;
stream.close();
} catch (const std::exception& e) {
beammp_errorf("Failed to load mods.json: {}", e.what());
}
}
for (const auto& entry : fs::directory_iterator(Path)) {
std::string File(entry.path().string());
if (entry.path().filename().string() == "mods.json") {
continue;
}
if (entry.path().extension() != ".zip" || std::filesystem::is_directory(entry.path())) {
beammp_warnf("'{}' is not a ZIP file and will be ignored", File);
continue;
}
if (modsDB.contains(entry.path().filename().string())) {
auto& dbEntry = modsDB[entry.path().filename().string()];
if (entry.last_write_time().time_since_epoch().count() > dbEntry["lastwrite"] || std::filesystem::file_size(File) != dbEntry["filesize"].get<size_t>()) {
beammp_infof("File '{}' has been modified, rehashing", File);
} else {
dbEntry["exists"] = true;
mMods.push_back(nlohmann::json {
{ "file_name", std::filesystem::path(File).filename() },
{ "file_size", std::filesystem::file_size(File) },
{ "hash_algorithm", "sha256" },
{ "hash", dbEntry["hash"] },
{ "protected", dbEntry["protected"] } });
beammp_debugf("Mod '{}' loaded from cache", File);
continue;
}
}
try {
EVP_MD_CTX* mdctx;
const EVP_MD* md;
@@ -169,73 +133,9 @@ void TResourceManager::RefreshFiles() {
{ "file_size", std::filesystem::file_size(File) },
{ "hash_algorithm", "sha256" },
{ "hash", result },
{ "protected", false } });
modsDB[std::filesystem::path(File).filename().string()] = {
{ "lastwrite", entry.last_write_time().time_since_epoch().count() },
{ "hash", result },
{ "filesize", std::filesystem::file_size(File) },
{ "protected", false },
{ "exists", true }
};
});
} catch (const std::exception& e) {
beammp_errorf("Sha256 hashing of '{}' failed: {}", File, e.what());
}
}
for (auto it = modsDB.begin(); it != modsDB.end();) {
if (!it.value().contains("exists")) {
it = modsDB.erase(it);
} else {
it.value().erase("exists");
++it;
}
}
try {
std::ofstream stream(Path + "/mods.json");
stream << modsDB.dump(4);
stream.close();
} catch (std::exception& e) {
beammp_error("Failed to update mod DB: " + std::string(e.what()));
}
}
void TResourceManager::SetProtected(const std::string& ModName, bool Protected) {
std::unique_lock Lock(mModsMutex);
for (auto& mod : mMods) {
if (mod["file_name"].get<std::string>() == ModName) {
mod["protected"] = Protected;
break;
}
}
auto modsDBPath = Application::Settings.getAsString(Settings::Key::General_ResourceFolder) + "/Client/mods.json";
if (std::filesystem::exists(modsDBPath)) {
try {
nlohmann::json modsDB;
std::fstream stream(modsDBPath);
stream >> modsDB;
if (modsDB.contains(ModName)) {
modsDB[ModName]["protected"] = Protected;
}
stream.clear();
stream.seekp(0, std::ios::beg);
stream << modsDB.dump(4);
stream.close();
} catch (const std::exception& e) {
beammp_errorf("Failed to update mods.json: {}", e.what());
}
}
}
+35 -96
View File
@@ -28,7 +28,6 @@
#include <any>
#include <optional>
#include <sstream>
#include <utility>
#include <nlohmann/json.hpp>
@@ -54,6 +53,7 @@ static std::optional<std::pair<int, int>> GetPidVid(const std::string& str) {
}
return std::nullopt;
}
TEST_CASE("GetPidVid") {
SUBCASE("Valid singledigit") {
const auto MaybePidVid = GetPidVid("0-1");
@@ -120,6 +120,7 @@ TEST_CASE("GetPidVid") {
CHECK(!MaybePidVid);
}
}
TServer::TServer(const std::vector<std::string_view>& Arguments) {
beammp_info("BeamMP Server v" + Application::ServerVersionString());
Application::SetSubsystemStatus("Server", Application::Status::Starting);
@@ -160,7 +161,7 @@ size_t TServer::ClientCount() const {
return mClients.size();
}
void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::vector<uint8_t>&& Packet, TPPSMonitor& PPSMonitor, TNetwork& Network, bool udp) {
void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::vector<uint8_t>&& Packet, TPPSMonitor& PPSMonitor, TNetwork& Network) {
constexpr std::string_view ABG = "ABG:";
if (Packet.size() >= ABG.size() && std::equal(Packet.begin(), Packet.begin() + ABG.size(), ABG.begin(), ABG.end())) {
Packet.erase(Packet.begin(), Packet.begin() + ABG.size());
@@ -194,29 +195,12 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::vector<uin
// V to Y
if (Code <= 89 && Code >= 86) {
int PID = -1;
int VID = -1;
auto pidVidPart = StringPacket.substr(3);
auto MaybePidVid = GetPidVid(pidVidPart.substr(0, pidVidPart.find(':')));
if (MaybePidVid) {
std::tie(PID, VID) = MaybePidVid.value();
}
if (PID == -1 || VID == -1 || PID != LockedClient->GetID()) {
return;
}
PPSMonitor.IncrementInternalPPS();
Network.SendToAll(LockedClient.get(), Packet, false, false);
return;
}
switch (Code) {
case 'H': // initial connection
if (udp) {
beammp_debugf("Received 'H' packet over UDP from client '{}' ({}), ignoring it", LockedClient->GetName(), LockedClient->GetID());
return;
}
if (!Network.SyncClient(Client)) {
// TODO handle
}
@@ -230,20 +214,12 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::vector<uin
}
return;
case 'O':
if (udp) {
beammp_debugf("Received 'O' packet over UDP from client '{}' ({}), ignoring it", LockedClient->GetName(), LockedClient->GetID());
return;
}
if (Packet.size() > 1000) {
beammp_debug(("Received data from: ") + LockedClient->GetName() + (" Size: ") + std::to_string(Packet.size()));
}
ParseVehicle(*LockedClient, StringPacket, Network);
return;
case 'C': {
if (udp) {
beammp_debugf("Received 'C' packet over UDP from client '{}' ({}), ignoring it", LockedClient->GetName(), LockedClient->GetID());
return;
}
if (Packet.size() < 4 || std::find(Packet.begin() + 3, Packet.end(), ':') == Packet.end())
break;
const auto PacketAsString = std::string(reinterpret_cast<const char*>(Packet.data()), Packet.size());
@@ -256,10 +232,6 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::vector<uin
beammp_debugf("Empty chat message received from '{}' ({}), ignoring it", LockedClient->GetName(), LockedClient->GetID());
return;
}
if (Message.size() > 500) {
beammp_debugf("Chat message too long from '{}' ({}), ignoring it", LockedClient->GetName(), LockedClient->GetID());
return;
}
auto Futures = LuaAPI::MP::Engine->TriggerEvent("onChatMessage", "", LockedClient->GetID(), LockedClient->GetName(), Message);
TLuaEngine::WaitForAll(Futures);
LogChatMessage(LockedClient->GetName(), LockedClient->GetID(), PacketAsString.substr(PacketAsString.find(':', 3) + 1));
@@ -278,35 +250,16 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::vector<uin
return;
}
case 'E':
if (udp) {
beammp_debugf("Received 'E' packet over UDP from client '{}' ({}), ignoring it", LockedClient->GetName(), LockedClient->GetID());
return;
}
HandleEvent(*LockedClient, StringPacket);
return;
case 'N':
Network.SendToAll(LockedClient.get(), Packet, false, true);
return;
case 'Z': { // position packet
case 'Z': // position packet
PPSMonitor.IncrementInternalPPS();
int PID = -1;
int VID = -1;
auto pidVidPart = StringPacket.substr(3);
auto MaybePidVid = GetPidVid(pidVidPart.substr(0, pidVidPart.find(':')));
if (MaybePidVid) {
std::tie(PID, VID) = MaybePidVid.value();
}
if (PID == -1 || VID == -1 || PID != LockedClient->GetID()) {
return;
}
Network.SendToAll(LockedClient.get(), Packet, false, false);
HandlePosition(*LockedClient, StringPacket);
return;
}
default:
return;
}
@@ -325,15 +278,6 @@ void TServer::HandleEvent(TClient& c, const std::string& RawData) {
}
std::string Name = RawData.substr(2, NameDataSep - 2);
std::string Data = RawData.substr(NameDataSep + 1);
std::vector<std::string> exclude = {"onInit", "onFileChanged","onVehicleDeleted","onConsoleInput","onPlayerAuth","postPlayerAuth", "onPlayerDisconnect",
"onPlayerConnecting","onPlayerJoining","onPlayerJoin","onChatMessage","postChatMessage","onVehicleSpawn","postVehicleSpawn","onVehicleEdited", "postVehicleEdited",
"onVehicleReset","onVehiclePaintChanged","onShutdown"};
if (std::ranges::find(exclude, Name) != exclude.end()) {
beammp_debugf("Excluded event triggered by client '{}' ({}): '{}', ignoring.", c.GetName(), c.GetID(), Name);
return;
}
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent(Name, "", c.GetID(), Data));
}
@@ -384,9 +328,8 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
});
bool SpawnConfirmed = false;
auto CarJsonDoc = nlohmann::json::parse(CarJson, nullptr, false);
if (ShouldSpawn(c, CarJson, CarID) && !ShouldntSpawn && !CarJsonDoc.is_discarded()) {
c.AddNewCar(CarID, CarJsonDoc);
if (ShouldSpawn(c, CarJson, CarID) && !ShouldntSpawn) {
c.AddNewCar(CarID, Packet);
Network.SendToAll(nullptr, StringToVector(Packet), true, true);
SpawnConfirmed = true;
} else {
@@ -471,13 +414,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
}
if (PID != -1 && VID != -1 && PID == c.GetID()) {
auto BracketPos = Data.find('{');
if (BracketPos == std::string::npos) {
beammp_debugf("Invalid 'Or' packet body from client {}", c.GetID());
return;
}
Data = Data.substr(BracketPos);
Data = Data.substr(Data.find('{'));
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onVehicleReset", "", c.GetID(), VID, Data));
Network.SendToAll(&c, StringToVector(Packet), false, true);
}
@@ -506,27 +443,9 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
}
if (PID != -1 && VID != -1 && PID == c.GetID()) {
auto BracketPos = Data.find('[');
if (BracketPos == std::string::npos) {
beammp_debugf("Invalid 'Op' packet body from client {}", c.GetID());
return;
}
Data = Data.substr(BracketPos);
Data = Data.substr(Data.find('['));
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onVehiclePaintChanged", "", c.GetID(), VID, Data));
Network.SendToAll(&c, StringToVector(Packet), false, true);
auto CarData = c.GetCarData(VID);
if (CarData == nlohmann::detail::value_t::null)
return;
if (CarData.contains("vcf") && CarData.at("vcf").is_object())
if (CarData.at("vcf").contains("paints") && CarData.at("vcf").at("paints").is_array()) {
CarData.at("vcf")["paints"] = nlohmann::json::parse(Data);
c.SetCarData(VID, CarData);
}
}
return;
}
@@ -542,22 +461,42 @@ void TServer::Apply(TClient& c, int VID, const std::string& pckt) {
beammp_error("Malformed packet received, no '{' found");
return;
}
std::string Packet = pckt.substr(FoundPos);
nlohmann::json VD = c.GetCarData(VID);
if (VD == nlohmann::detail::value_t::null) {
std::string VD = c.GetCarData(VID);
if (VD.empty()) {
beammp_error("Tried to apply change to vehicle that does not exist");
return;
}
std::string Header = VD.substr(0, VD.find('{'));
nlohmann::json Pack = nlohmann::json::parse(Packet, nullptr, false);
if (Pack.is_discarded()) {
FoundPos = VD.find('{');
if (FoundPos == std::string::npos) {
return;
}
VD = VD.substr(FoundPos);
rapidjson::Document Veh, Pack;
Veh.Parse(VD.c_str());
if (Veh.HasParseError()) {
beammp_error("Could not get vehicle config!");
return;
}
Pack.Parse(Packet.c_str());
if (Pack.HasParseError() || Pack.IsNull()) {
beammp_error("Could not get active vehicle config!");
return;
}
c.SetCarData(VID, Pack);
for (auto& M : Pack.GetObject()) {
if (Veh[M.name].IsNull()) {
Veh.AddMember(M.name, M.value, Veh.GetAllocator());
} else {
Veh[M.name] = Pack[M.name];
}
}
rapidjson::StringBuffer Buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(Buffer);
Veh.Accept(writer);
c.SetCarData(VID, Header + Buffer.GetString());
}
void TServer::InsertClient(const std::shared_ptr<TClient>& NewClient) {
+1 -5
View File
@@ -21,7 +21,7 @@
#include "Common.h"
#include <utility>
TVehicleData::TVehicleData(int ID, nlohmann::json Data)
TVehicleData::TVehicleData(int ID, std::string Data)
: mID(ID)
, mData(std::move(Data)) {
beammp_trace("vehicle " + std::to_string(mID) + " constructed");
@@ -30,7 +30,3 @@ TVehicleData::TVehicleData(int ID, nlohmann::json Data)
TVehicleData::~TVehicleData() {
beammp_trace("vehicle " + std::to_string(mID) + " destroyed");
}
std::string TVehicleData::DataAsPacket(const std::string& Role, const std::string& Name, const int ID) const {
return "Os:" + Role + ":" + Name + ":" + std::to_string(ID) + "-" + std::to_string(this->mID) + ":" + this->mData.dump();
}
+5 -10
View File
@@ -182,6 +182,10 @@ int BeamMPServerMain(MainArguments Arguments) {
TServer Server(Arguments.List);
auto LuaEngine = std::make_shared<TLuaEngine>();
LuaEngine->SetServer(&Server);
Application::Console().InitializeLuaConsole(*LuaEngine);
RegisterThread("Main");
beammp_trace("Running in debug mode on a debug build");
@@ -190,16 +194,13 @@ int BeamMPServerMain(MainArguments Arguments) {
TPPSMonitor PPSMonitor(Server);
THeartbeatThread Heartbeat(ResourceManager, Server);
TNetwork Network(Server, PPSMonitor, ResourceManager);
auto LuaEngine = std::make_shared<TLuaEngine>();
LuaEngine->SetServer(&Server);
Application::Console().InitializeLuaConsole(*LuaEngine);
LuaEngine->SetNetwork(&Network);
PPSMonitor.SetNetwork(Network);
Application::CheckForUpdates();
TPluginMonitor PluginMonitor(fs::path(Application::Settings.getAsString(Settings::Key::General_ResourceFolder)) / "Server", LuaEngine);
Application::SetSubsystemStatus("Main", Application::Status::Good);
RegisterThread("Main(Waiting)");
std::set<std::string> IgnoreSubsystems {
@@ -214,10 +215,6 @@ int BeamMPServerMain(MainArguments Arguments) {
std::string SystemsBadList {};
auto Statuses = Application::GetSubsystemStatuses();
for (const auto& NameStatusPair : Statuses) {
if (NameStatusPair.first == "Main") {
continue;
}
if (IgnoreSubsystems.count(NameStatusPair.first) > 0) {
continue; // ignore
}
@@ -231,8 +228,6 @@ int BeamMPServerMain(MainArguments Arguments) {
// remove ", "
SystemsBadList = SystemsBadList.substr(0, SystemsBadList.size() - 2);
if (FullyStarted) {
Application::SetSubsystemStatus("Main", Application::Status::Good);
if (!WithErrors) {
beammp_info("ALL SYSTEMS STARTED SUCCESSFULLY, EVERYTHING IS OKAY");
} else {
+1 -1
Submodule vcpkg updated: 5bf0c55239...6978381401
+2 -14
View File
@@ -14,18 +14,6 @@
"openssl",
"rapidjson",
"sol2",
"curl",
"lua"
],
"overrides": [
{
"name": "sol2",
"version": "3.3.1"
},
{
"name": "lua",
"version": "5.3.5#6"
}
],
"builtin-baseline": "5bf0c55239da398b8c6f450818c9e28d36bf9966"
"curl"
]
}