mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2026-07-17 04:03:48 +00:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d0d5e9e4c | |||
| a1ca8e0576 | |||
| b22f21a6b2 | |||
| 531a901431 | |||
| 46f778bd01 | |||
| f3b6eea418 | |||
| bceb3aefe4 | |||
| 6c72432992 | |||
| 17d3f303ca | |||
| 2cbcf96282 | |||
| 7d4fd44dbf | |||
| 71c2af1224 | |||
| 2e112fc5f1 | |||
| 96c93a6aa6 | |||
| 9dbb91fd84 | |||
| 26c33ae2fb | |||
| 3eb943309e | |||
| 3c8e8399cb | |||
| 5b500a3da5 | |||
| ade19123b7 | |||
| 5ee18e0576 | |||
| 77d23caa63 | |||
| 79856cde8e |
@@ -20,7 +20,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp'
|
vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp'
|
||||||
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
|
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
|
||||||
vcpkgGitCommitId: '75522bb1f2e7d863078bcd06322348f053a9e33f'
|
vcpkgGitCommitId: '8dddc6c899ce6fdbeab38b525a31e7f23cb2d5bb'
|
||||||
vcpkgTriplet: 'x64-windows-static'
|
vcpkgTriplet: 'x64-windows-static'
|
||||||
|
|
||||||
- name: Create Build Environment
|
- name: Create Build Environment
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp'
|
vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp'
|
||||||
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
|
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
|
||||||
vcpkgGitCommitId: '75522bb1f2e7d863078bcd06322348f053a9e33f'
|
vcpkgGitCommitId: '8dddc6c899ce6fdbeab38b525a31e7f23cb2d5bb'
|
||||||
vcpkgTriplet: 'x64-windows-static'
|
vcpkgTriplet: 'x64-windows-static'
|
||||||
|
|
||||||
- name: Create Build Environment
|
- name: Create Build Environment
|
||||||
|
|||||||
@@ -63,27 +63,26 @@ These package names are in the debian / ubuntu style. Feel free to PR your own g
|
|||||||
- `g++`
|
- `g++`
|
||||||
|
|
||||||
Must support ISO C++17. If your distro's `g++` doesn't support C++17, chances are that it has a `g++-8` or `g++-10` package that does. If this is the case. you just need to run CMake with `-DCMAKE_CXX_COMPILER=g++-10` (replace `g++-10` with your compiler's name).
|
Must support ISO C++17. If your distro's `g++` doesn't support C++17, chances are that it has a `g++-8` or `g++-10` package that does. If this is the case. you just need to run CMake with `-DCMAKE_CXX_COMPILER=g++-10` (replace `g++-10` with your compiler's name).
|
||||||
- `liblua5.3`
|
- `liblua5.3-dev`
|
||||||
|
|
||||||
Any 5.x version should work, but 5.3 is what we officially use. Any other version might break in the future.
|
Any 5.x version should work, but 5.3 is what we officially use. Any other version might break in the future.
|
||||||
You can also use any version of `libluajit`, but the same applies regarding the version.
|
You can also use any version of `libluajit`, but the same applies regarding the version.
|
||||||
- `libz-dev`
|
- `libz-dev`
|
||||||
- `rapidjson-dev`
|
- `rapidjson-dev`
|
||||||
- `libopenssl-dev`
|
- `libopenssl-dev` or `libssl-dev`
|
||||||
|
|
||||||
**If** you're building it from source, you'll need `libboost1.70-dev` as well.
|
**If** you're building it from source, you'll need `libboost1.70-all-dev` or `libboost1.71-all-dev` or higher as well.
|
||||||
|
|
||||||
### How to build
|
### How to build
|
||||||
|
|
||||||
On windows. use git-bash for these commands.
|
On windows. use git-bash for these commands. On linux, these should work in your shell.
|
||||||
|
|
||||||
1. Make sure you have all [prerequisites](#prerequisites) installed
|
1. Make sure you have all [prerequisites](#prerequisites) installed
|
||||||
2. Clone the repository in a location of your choice with `git clone --recursive https://github.com/BeamMP/BeamMP-Server`
|
2. Clone the repository in a location of your choice with `git clone --recursive https://github.com/BeamMP/BeamMP-Server`. Now change into the cloned directory by running `cd BeamMP-Server`.
|
||||||
3. Checkout the branch of the release you want to compile (`master` is often unstable), for example `git checkout tags/v1.20` for version 1.20.
|
3. Checkout the branch of the release you want to compile (`master` is often unstable), for example `git checkout tags/v1.20` for version 1.20. You can find the latest version [here](https://github.com/BeamMP/BeamMP-Server/tags).
|
||||||
4. `cd` into it with `cd BeamMP-Server`
|
4. Run `cmake .` (with `.`)
|
||||||
5. Run `cmake .` (with `.`)
|
5. Run `make`
|
||||||
6. Run `make`
|
6. You will now have a `BeamMP-Server` file in your directory, which is executable with `./BeamMP-Server` (`.\BeamMP-Server.exe` for windows). Follow the (windows or linux, doesnt matter) instructions on the [wiki](https://wiki.beammp.com/en/home/Server_Mod) for further setup after installation (which we just did), such as port-forwarding and getting a key to actually run the server.
|
||||||
7. You will now have a `BeamMP-Server` file in your directory, which is executable with `./BeamMP-Server` (`.\BeamMP-Server.exe` for windows). Follow the (windows or linux, doesnt matter) instructions on the [wiki](https://wiki.beammp.com/en/home/Server_Mod) for further setup after installation (which we just did), such as port-forwarding and getting a key to actually run the server.
|
|
||||||
|
|
||||||
*tip: to run the server in the background, simply (in bash, zsh, etc) run:* `nohup ./BeamMP-Server &`*.*
|
*tip: to run the server in the background, simply (in bash, zsh, etc) run:* `nohup ./BeamMP-Server &`*.*
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -21,7 +21,7 @@ public:
|
|||||||
: ServerName("BeamMP Server")
|
: ServerName("BeamMP Server")
|
||||||
, ServerDesc("BeamMP Default Description")
|
, ServerDesc("BeamMP Default Description")
|
||||||
, Resource("Resources")
|
, Resource("Resources")
|
||||||
, MapName("/levels/gridmap/info.json")
|
, MapName("/levels/gridmap_v2/info.json")
|
||||||
, MaxPlayers(10)
|
, MaxPlayers(10)
|
||||||
, Private(false)
|
, Private(false)
|
||||||
, MaxCars(1)
|
, MaxCars(1)
|
||||||
@@ -50,7 +50,7 @@ public:
|
|||||||
// Causes all threads to finish up and exit gracefull gracefully
|
// Causes all threads to finish up and exit gracefull gracefully
|
||||||
static void GracefullyShutdown();
|
static void GracefullyShutdown();
|
||||||
static TConsole& Console() { return *mConsole; }
|
static TConsole& Console() { return *mConsole; }
|
||||||
static std::string ServerVersion() { return "2.1.1"; }
|
static std::string ServerVersion() { return "2.2.0"; }
|
||||||
static std::string ClientVersion() { return "2.0"; }
|
static std::string ClientVersion() { return "2.0"; }
|
||||||
static std::string PPS() { return mPPS; }
|
static std::string PPS() { return mPPS; }
|
||||||
static void SetPPS(std::string NewPPS) { mPPS = NewPPS; }
|
static void SetPPS(std::string NewPPS) { mPPS = NewPPS; }
|
||||||
|
|||||||
+2
-2
@@ -8,13 +8,13 @@ class TConfig {
|
|||||||
public:
|
public:
|
||||||
explicit TConfig();
|
explicit TConfig();
|
||||||
|
|
||||||
bool Failed() const { return mFailed; }
|
[[nodiscard]] bool Failed() const { return mFailed; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CreateConfigFile(std::string_view name);
|
void CreateConfigFile(std::string_view name);
|
||||||
void ParseFromFile(std::string_view name);
|
void ParseFromFile(std::string_view name);
|
||||||
void PrintDebug();
|
void PrintDebug();
|
||||||
|
|
||||||
void ParseOldFormat();
|
void ParseOldFormat();
|
||||||
|
|
||||||
bool mFailed { false };
|
bool mFailed { false };
|
||||||
|
|||||||
@@ -26,9 +26,7 @@ void THeartbeatThread::operator()() {
|
|||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#ifdef DEBUG
|
debug("heartbeat (after " + std::to_string(std::chrono::duration_cast<std::chrono::seconds>(TimePassed).count()) + "s)");
|
||||||
debug("heartbeat @ " + std::to_string(std::chrono::duration_cast<std::chrono::seconds>(TimePassed).count()));
|
|
||||||
#endif // DEBUG
|
|
||||||
|
|
||||||
Last = Body;
|
Last = Body;
|
||||||
LastNormalUpdateTime = Now;
|
LastNormalUpdateTime = Now;
|
||||||
|
|||||||
+2
-5
@@ -74,15 +74,13 @@ std::any FutureWait(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg
|
|||||||
|
|
||||||
std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller, std::shared_ptr<TLuaArg> arg, bool Wait) {
|
std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller, std::shared_ptr<TLuaArg> arg, bool Wait) {
|
||||||
std::any R;
|
std::any R;
|
||||||
std::string Type;
|
|
||||||
int Ret = 0;
|
int Ret = 0;
|
||||||
for (auto& Script : Engine().LuaFiles()) {
|
for (auto& Script : Engine().LuaFiles()) {
|
||||||
if (Script->IsRegistered(Event)) {
|
if (Script->IsRegistered(Event)) {
|
||||||
if (local) {
|
if (local) {
|
||||||
if (Script->GetPluginName() == Caller->GetPluginName()) {
|
if (Script->GetPluginName() == Caller->GetPluginName()) {
|
||||||
R = FutureWait(Script.get(), Script->GetRegistered(Event), arg, Wait);
|
R = FutureWait(Script.get(), Script->GetRegistered(Event), arg, Wait);
|
||||||
Type = R.type().name();
|
if (R.type() == typeid(int)) {
|
||||||
if (Type.find("int") != std::string::npos) {
|
|
||||||
if (std::any_cast<int>(R))
|
if (std::any_cast<int>(R))
|
||||||
Ret++;
|
Ret++;
|
||||||
} else if (Event == "onPlayerAuth")
|
} else if (Event == "onPlayerAuth")
|
||||||
@@ -90,8 +88,7 @@ std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
R = FutureWait(Script.get(), Script->GetRegistered(Event), arg, Wait);
|
R = FutureWait(Script.get(), Script->GetRegistered(Event), arg, Wait);
|
||||||
Type = R.type().name();
|
if (R.type() == typeid(int)) {
|
||||||
if (Type.find("int") != std::string::npos) {
|
|
||||||
if (std::any_cast<int>(R))
|
if (std::any_cast<int>(R))
|
||||||
Ret++;
|
Ret++;
|
||||||
} else if (Event == "onPlayerAuth")
|
} else if (Event == "onPlayerAuth")
|
||||||
|
|||||||
+31
-13
@@ -9,6 +9,15 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R
|
|||||||
: mServer(Server)
|
: mServer(Server)
|
||||||
, mPPSMonitor(PPSMonitor)
|
, mPPSMonitor(PPSMonitor)
|
||||||
, mResourceManager(ResourceManager) {
|
, mResourceManager(ResourceManager) {
|
||||||
|
Application::RegisterShutdownHandler([&] {
|
||||||
|
debug("Kicking all players due to shutdown");
|
||||||
|
Server.ForEachClient([&](std::weak_ptr<TClient> client) -> bool {
|
||||||
|
if (!client.expired()) {
|
||||||
|
ClientKick(*client.lock(), "Server shutdown");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
Application::RegisterShutdownHandler([&] {
|
Application::RegisterShutdownHandler([&] {
|
||||||
if (mUDPThread.joinable()) {
|
if (mUDPThread.joinable()) {
|
||||||
debug("shutting down TCPServer");
|
debug("shutting down TCPServer");
|
||||||
@@ -18,7 +27,7 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
Application::RegisterShutdownHandler([&] {
|
Application::RegisterShutdownHandler([&] {
|
||||||
if (mUDPThread.joinable()) {
|
if (mTCPThread.joinable()) {
|
||||||
debug("shutting down TCPServer");
|
debug("shutting down TCPServer");
|
||||||
mShutdown = true;
|
mShutdown = true;
|
||||||
mTCPThread.detach();
|
mTCPThread.detach();
|
||||||
@@ -327,11 +336,10 @@ void TNetwork::Authentication(SOCKET TCPSock) {
|
|||||||
|
|
||||||
auto arg = std::make_unique<TLuaArg>(TLuaArg { { Client->GetName(), Client->GetRoles(), Client->IsGuest() } });
|
auto arg = std::make_unique<TLuaArg>(TLuaArg { { Client->GetName(), Client->GetRoles(), Client->IsGuest() } });
|
||||||
std::any Res = TriggerLuaEvent("onPlayerAuth", false, nullptr, std::move(arg), true);
|
std::any Res = TriggerLuaEvent("onPlayerAuth", false, nullptr, std::move(arg), true);
|
||||||
std::string Type = Res.type().name();
|
if (Res.type() == typeid(int) && std::any_cast<int>(Res)) {
|
||||||
if (Type.find("int") != std::string::npos && std::any_cast<int>(Res)) {
|
|
||||||
ClientKick(*Client, "you are not allowed on the server!");
|
ClientKick(*Client, "you are not allowed on the server!");
|
||||||
return;
|
return;
|
||||||
} else if (Type.find("string") != std::string::npos) {
|
} else if (Res.type() == typeid(std::string)) {
|
||||||
ClientKick(*Client, std::any_cast<std::string>(Res));
|
ClientKick(*Client, std::any_cast<std::string>(Res));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -694,19 +702,29 @@ void TNetwork::Parse(TClient& c, const std::string& Packet) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TNetwork::SendFile(TClient& c, const std::string& Name) {
|
void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) {
|
||||||
info(c.GetName() + " requesting : " + Name.substr(Name.find_last_of('/')));
|
info(c.GetName() + " requesting : " + UnsafeName.substr(UnsafeName.find_last_of('/')));
|
||||||
|
|
||||||
if (!std::filesystem::exists(Name)) {
|
if (!fs::path(UnsafeName).has_filename()) {
|
||||||
if (!TCPSend(c, "CO")) {
|
if (!TCPSend(c, "CO")) {
|
||||||
// TODO: handle
|
// TODO: handle
|
||||||
}
|
}
|
||||||
warn("File " + Name + " could not be accessed!");
|
warn("File " + UnsafeName + " is not a file!");
|
||||||
return;
|
return;
|
||||||
} else {
|
}
|
||||||
if (!TCPSend(c, "AG")) {
|
auto FileName = fs::path(UnsafeName).filename().string();
|
||||||
|
FileName = Application::Settings.Resource + "/Client/" + FileName;
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(FileName)) {
|
||||||
|
if (!TCPSend(c, "CO")) {
|
||||||
// TODO: handle
|
// TODO: handle
|
||||||
}
|
}
|
||||||
|
warn("File " + UnsafeName + " could not be accessed!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TCPSend(c, "AG")) {
|
||||||
|
// TODO: handle
|
||||||
}
|
}
|
||||||
|
|
||||||
///Wait for connections
|
///Wait for connections
|
||||||
@@ -723,14 +741,14 @@ void TNetwork::SendFile(TClient& c, const std::string& Name) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Size = size_t(std::filesystem::file_size(Name)), MSize = Size / 2;
|
size_t Size = size_t(std::filesystem::file_size(FileName)), MSize = Size / 2;
|
||||||
|
|
||||||
std::thread SplitThreads[2] {
|
std::thread SplitThreads[2] {
|
||||||
std::thread([&] {
|
std::thread([&] {
|
||||||
SplitLoad(c, 0, MSize, false, Name);
|
SplitLoad(c, 0, MSize, false, FileName);
|
||||||
}),
|
}),
|
||||||
std::thread([&] {
|
std::thread([&] {
|
||||||
SplitLoad(c, MSize, Size, true, Name);
|
SplitLoad(c, MSize, Size, true, FileName);
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ TResourceManager::TResourceManager() {
|
|||||||
++i;
|
++i;
|
||||||
File = File.substr(i,pos-i);
|
File = File.substr(i,pos-i);
|
||||||
}
|
}
|
||||||
mTrimmedList += File + ';';
|
mTrimmedList += "/" + fs::path(File).filename().string() + ';';
|
||||||
mFileSizes += std::to_string(size_t(fs::file_size(entry.path()))) + ';';
|
mFileSizes += std::to_string(size_t(fs::file_size(entry.path()))) + ';';
|
||||||
mMaxModSize += size_t(fs::file_size(entry.path()));
|
mMaxModSize += size_t(fs::file_size(entry.path()));
|
||||||
mModsLoaded++;
|
mModsLoaded++;
|
||||||
|
|||||||
+26
-9
@@ -164,10 +164,10 @@ void TServer::HandleEvent(TClient& c, const std::string& Data) {
|
|||||||
a++;
|
a++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool TServer::IsUnicycle(TClient &c, const std::string &CarJson) {
|
bool TServer::IsUnicycle(TClient& c, const std::string& CarJson) {
|
||||||
rapidjson::Document Car;
|
rapidjson::Document Car;
|
||||||
Car.Parse(CarJson.c_str(), CarJson.size());
|
Car.Parse(CarJson.c_str(), CarJson.size());
|
||||||
if(Car.HasParseError()){
|
if (Car.HasParseError()) {
|
||||||
error("Failed to parse vehicle data -> " + CarJson);
|
error("Failed to parse vehicle data -> " + CarJson);
|
||||||
} else if (Car["jbm"].IsString() && std::string(Car["jbm"].GetString()) == "unicycle") {
|
} else if (Car["jbm"].IsString() && std::string(Car["jbm"].GetString()) == "unicycle") {
|
||||||
return true;
|
return true;
|
||||||
@@ -176,11 +176,11 @@ bool TServer::IsUnicycle(TClient &c, const std::string &CarJson) {
|
|||||||
}
|
}
|
||||||
bool TServer::ShouldSpawn(TClient& c, const std::string& CarJson, int ID) {
|
bool TServer::ShouldSpawn(TClient& c, const std::string& CarJson, int ID) {
|
||||||
|
|
||||||
if(c.GetUnicycleID() > -1 && (c.GetCarCount() - 1) < Application::Settings.MaxCars){
|
if (c.GetUnicycleID() > -1 && (c.GetCarCount() - 1) < Application::Settings.MaxCars) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(IsUnicycle(c,CarJson)){
|
if (IsUnicycle(c, CarJson)) {
|
||||||
c.SetUnicycleID(ID);
|
c.SetUnicycleID(ID);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -239,12 +239,14 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
|||||||
std::make_unique<TLuaArg>(TLuaArg { { c.GetID(), VID, Packet.substr(3) } }),
|
std::make_unique<TLuaArg>(TLuaArg { { c.GetID(), VID, Packet.substr(3) } }),
|
||||||
true);
|
true);
|
||||||
|
|
||||||
if ((c.GetUnicycleID() != VID || IsUnicycle(c,Packet.substr(Packet.find('{'))))
|
auto FoundPos = Packet.find('{');
|
||||||
|
FoundPos = FoundPos == std::string::npos ? 0 : FoundPos; // attempt at sanitizing this
|
||||||
|
if ((c.GetUnicycleID() != VID || IsUnicycle(c, Packet.substr(FoundPos)))
|
||||||
&& std::any_cast<int>(Res) == 0) {
|
&& std::any_cast<int>(Res) == 0) {
|
||||||
Network.SendToAll(&c, Packet, false, true);
|
Network.SendToAll(&c, Packet, false, true);
|
||||||
Apply(c, VID, Packet);
|
Apply(c, VID, Packet);
|
||||||
} else {
|
} else {
|
||||||
if(c.GetUnicycleID() == VID){
|
if (c.GetUnicycleID() == VID) {
|
||||||
c.SetUnicycleID(-1);
|
c.SetUnicycleID(-1);
|
||||||
}
|
}
|
||||||
std::string Destroy = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(VID);
|
std::string Destroy = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(VID);
|
||||||
@@ -266,7 +268,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
|||||||
VID = stoi(vid);
|
VID = stoi(vid);
|
||||||
}
|
}
|
||||||
if (PID != -1 && VID != -1 && PID == c.GetID()) {
|
if (PID != -1 && VID != -1 && PID == c.GetID()) {
|
||||||
if(c.GetUnicycleID() == VID){
|
if (c.GetUnicycleID() == VID) {
|
||||||
c.SetUnicycleID(-1);
|
c.SetUnicycleID(-1);
|
||||||
}
|
}
|
||||||
Network.SendToAll(nullptr, Packet, true, true);
|
Network.SendToAll(nullptr, Packet, true, true);
|
||||||
@@ -312,10 +314,25 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TServer::Apply(TClient& c, int VID, const std::string& pckt) {
|
void TServer::Apply(TClient& c, int VID, const std::string& pckt) {
|
||||||
std::string Packet = pckt.substr(pckt.find('{')), VD = c.GetCarData(VID);
|
auto FoundPos = pckt.find('{');
|
||||||
|
if (FoundPos == std::string::npos) {
|
||||||
|
error("Malformed packet received, no '{' found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::string Packet = pckt.substr(FoundPos);
|
||||||
|
std::string VD = c.GetCarData(VID);
|
||||||
|
if (VD.empty()) {
|
||||||
|
error("Tried to apply change to vehicle that does not exist");
|
||||||
|
return;
|
||||||
|
}
|
||||||
std::string Header = VD.substr(0, VD.find('{'));
|
std::string Header = VD.substr(0, VD.find('{'));
|
||||||
|
|
||||||
VD = VD.substr(VD.find('{'));
|
FoundPos = VD.find('{');
|
||||||
|
if (FoundPos == std::string::npos) {
|
||||||
|
error("Malformed packet received, no '{' found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
VD = VD.substr(FoundPos);
|
||||||
rapidjson::Document Veh, Pack;
|
rapidjson::Document Veh, Pack;
|
||||||
Veh.Parse(VD.c_str());
|
Veh.Parse(VD.c_str());
|
||||||
if (Veh.HasParseError()) {
|
if (Veh.HasParseError()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user