Compare commits

..

22 Commits

Author SHA1 Message Date
Lion Kortlepel 103d2dc030 update vcpkg in workflows 2023-12-03 17:50:45 +01:00
Lion Kortlepel 7f206fd0d4 bump version to 3.1.2 2023-12-03 17:49:12 +01:00
Lion Kortlepel 22805af716 fix crash in authentication 2023-12-03 17:43:41 +01:00
Lion 47e64a7343 merge release candidate v3.1.1 (#142)
patches and hotfixes!
2022-10-31 11:32:16 +01:00
Lion Kortlepel 7f5b3919f4 make destructors of virtual classes virtual
this causes warnings in clang, and rightfully so :^)
2022-10-31 11:31:16 +01:00
Lion Kortlepel 896e777e23 update changelog 2022-10-31 11:31:16 +01:00
Lion Kortlepel aa58c1e211 another potential fix for #141 2022-10-31 11:31:15 +01:00
Lion Kortlepel 49a9226dca update changelog to mention fixes 2022-10-31 11:31:15 +01:00
Lion Kortlepel b10d5d0f4e fix empty events causing issues in the server
an empty event packet, if sent just right, could crash the server
2022-10-31 11:31:15 +01:00
Lion Kortlepel 5581fd1692 fix chat message impersonation issue
instead of using the supplied name, we ignore it entirely and use the
server's internal name for the client
2022-10-31 11:31:15 +01:00
Lion Kortlepel d36bb7962c ignore empty chat messages
this could happen with a malicious client of some kind, we should simply
ignore them
2022-10-31 11:31:15 +01:00
Lion Kortlepel 4e8bd993d5 remove J packet handler
it wasn't used by anything in the launcher, mod, or during the join
sequence, so i removed it for now.
2022-10-31 11:31:15 +01:00
Lion Kortlepel abff9bfbdb fix crash when chat message is malformed 2022-10-31 11:31:15 +01:00
Lion Kortlepel b024533f90 fix crash when the header of a TCP packet is negative 2022-10-31 11:31:15 +01:00
Lion Kortlepel f9251ff92c add more warning prints on unexpected cases 2022-10-31 11:31:15 +01:00
Lion Kortlepel 99f41c28cb fix #135 by making onPlayerDisconnect blocking, and calling it before removing the player
before, the handlers were not waited for, so the client was usually
destructed before lua got to the actual event handler call. Now, the
handler is called and waited on, and once all handlers are done, the
client is properly removed from the players internally, thus making
calls to GetPlayerName, GetPlayerIdentifiers, etc. return nil etc.
2022-10-31 11:31:15 +01:00
Lion Kortlepel bbd27c9cba fix formatting bug in status (closes #143) 2022-10-31 11:31:15 +01:00
Lion Kortlepel 4682922467 add more fixes for msvc static linking 2022-10-31 11:31:15 +01:00
Lion Kortlepel 83fb387dfe potential fix to #141 2022-10-31 11:31:09 +01:00
Lion Kortlepel 2b61f11a86 fix EnsureArgsCount not properly printing min/max 2022-10-31 11:24:13 +01:00
Lion Kortlepel a8b1a205f7 bump version to 3.1.1
I'm expecting to release a 3.1.1 with some fixes
2022-10-31 11:24:13 +01:00
Lion dd9376447a Fix README errors (#138) 2022-10-26 14:34:01 +02:00
10 changed files with 122 additions and 56 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
with: with:
vcpkgArguments: 'lua zlib rapidjson openssl websocketpp curl' vcpkgArguments: 'lua zlib rapidjson openssl websocketpp curl'
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg' vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
vcpkgGitCommitId: "06b5f4a769d848d1a20fa0acd556019728b56273" vcpkgGitCommitId: "16ee2ecb31788c336ace8bb14c21801efb6836e4"
vcpkgTriplet: 'x64-windows-static' vcpkgTriplet: 'x64-windows-static'
- name: Create Build Environment - name: Create Build Environment
+1 -1
View File
@@ -85,7 +85,7 @@ jobs:
with: with:
vcpkgArguments: 'lua zlib rapidjson openssl websocketpp curl' vcpkgArguments: 'lua zlib rapidjson openssl websocketpp curl'
vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg' vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg'
vcpkgGitCommitId: '06b5f4a769d848d1a20fa0acd556019728b56273' vcpkgGitCommitId: '16ee2ecb31788c336ace8bb14c21801efb6836e4'
vcpkgTriplet: 'x64-windows-static' vcpkgTriplet: 'x64-windows-static'
- name: Create Build Environment - name: Create Build Environment
+11 -10
View File
@@ -28,6 +28,8 @@ set(SENTRY_BUILD_SHARED_LIBS OFF)
add_compile_definitions(CPPHTTPLIB_OPENSSL_SUPPORT=1) add_compile_definitions(CPPHTTPLIB_OPENSSL_SUPPORT=1)
option(WIN32_STATIC_RUNTIME "Build statically-linked runtime on windows (don't touch unless you know what you're doing)" ON)
# ------------------------ APPLE --------------------------------- # ------------------------ APPLE ---------------------------------
if(APPLE) if(APPLE)
if(IS_DIRECTORY /opt/homebrew/Cellar/lua@5.3/5.3.6) if(IS_DIRECTORY /opt/homebrew/Cellar/lua@5.3/5.3.6)
@@ -46,18 +48,14 @@ if(APPLE)
link_directories(/usr/local/opt/openssl@1.1/lib) link_directories(/usr/local/opt/openssl@1.1/lib)
endif() endif()
# ------------------------ WINDOWS --------------------------------- # ------------------------ WINDOWS ---------------------------------
option(WIN32_STATIC_RUNTIME "Build statically-linked runtime on windows (don't touch unless you know what you're doing)" ON)
elseif (WIN32) elseif (WIN32)
# this has to happen before sentry, so that crashpad on windows links with these settings.
if (WIN32_STATIC_RUNTIME) if (WIN32_STATIC_RUNTIME)
message(STATUS "MSVC -> forcing use of statically-linked runtime.") set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
STRING(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
STRING(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
endif() endif()
# ------------------------ LINUX --------------------------------- # ------------------------ UNIX ------------------------------------
elseif (UNIX) elseif (UNIX)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -fno-builtin") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2")
option(SANITIZE "Turns on thread and UB sanitizers" OFF) option(SANITIZE "Turns on thread and UB sanitizers" OFF)
if (SANITIZE) if (SANITIZE)
message(STATUS "sanitize is ON") message(STATUS "sanitize is ON")
@@ -82,9 +80,6 @@ add_subdirectory("deps/sentry-native")
# ------------------------ C++ SETUP --------------------------------- # ------------------------ C++ SETUP ---------------------------------
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
if (MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
endif ()
# ------------------------ DEPENDENCIES ------------------------------ # ------------------------ DEPENDENCIES ------------------------------
message(STATUS "Adding local source dependencies") message(STATUS "Adding local source dependencies")
@@ -177,6 +172,12 @@ if (UNIX)
-fstack-protector -fstack-protector
-Wzero-as-null-pointer-constant -Wzero-as-null-pointer-constant
) )
else()
set(BeamMP_CompileOptions
/bigobj
/INCREMENTAL:NO /NODEFAULTLIB:MSVCRT /NODEFAULTLIB:LIBCMT
)
endif() endif()
set(BeamMP_Libraries set(BeamMP_Libraries
+7
View File
@@ -1,3 +1,10 @@
# v3.1.1
- FIXED bug which caused GetPlayerIdentifiers, GetPlayerName, etc not to work in `onPlayerDisconnect`
- FIXED some issues which could cause the server to crash when receiving malformed data
- FIXED a bug which caused a server to crash during authentication when receiving malformed data
- FIXED minor vulnerability in chat message handling
- FIXED a minor formatting bug in the `status` command
# v3.1.0 # v3.1.0
+1 -1
View File
@@ -137,7 +137,7 @@ private:
static inline std::mutex mShutdownHandlersMutex {}; static inline std::mutex mShutdownHandlersMutex {};
static inline std::deque<TShutdownHandler> mShutdownHandlers {}; static inline std::deque<TShutdownHandler> mShutdownHandlers {};
static inline Version mVersion { 3, 1, 0 }; static inline Version mVersion { 3, 1, 2 };
}; };
std::string ThreadName(bool DebugModeOverride = false); std::string ThreadName(bool DebugModeOverride = false);
+2 -2
View File
@@ -76,7 +76,7 @@ public:
}; };
TLuaEngine(); TLuaEngine();
~TLuaEngine() noexcept { virtual ~TLuaEngine() noexcept {
beammp_debug("Lua Engine terminated"); beammp_debug("Lua Engine terminated");
} }
@@ -198,7 +198,7 @@ private:
public: public:
StateThreadData(const std::string& Name, TLuaStateId StateId, TLuaEngine& Engine); StateThreadData(const std::string& Name, TLuaStateId StateId, TLuaEngine& Engine);
StateThreadData(const StateThreadData&) = delete; StateThreadData(const StateThreadData&) = delete;
~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); } virtual ~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); }
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueScript(const TLuaChunk& Script); [[nodiscard]] std::shared_ptr<TLuaResult> EnqueueScript(const TLuaChunk& Script);
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args); [[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args);
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args, const std::string& EventName, CallStrategy Strategy); [[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args, const std::string& EventName, CallStrategy Strategy);
+1
View File
@@ -6,6 +6,7 @@
#include <optional> #include <optional>
void TClient::DeleteCar(int Ident) { void TClient::DeleteCar(int Ident) {
// TODO: Send delete packets
std::unique_lock lock(mVehicleDataMutex); std::unique_lock lock(mVehicleDataMutex);
auto iter = std::find_if(mVehicleData.begin(), mVehicleData.end(), [&](auto& elem) { auto iter = std::find_if(mVehicleData.begin(), mVehicleData.end(), [&](auto& elem) {
return Ident == elem.ID(); return Ident == elem.ID();
+6 -5
View File
@@ -198,10 +198,10 @@ bool TConsole::EnsureArgsCount(const std::vector<std::string>& args, size_t min,
return EnsureArgsCount(args, min); return EnsureArgsCount(args, min);
} else { } else {
if (args.size() > max) { if (args.size() > max) {
Application::Console().WriteRaw("Too many arguments. At most " + std::to_string(max) + " arguments expected, got " + std::to_string(args.size()) + " instead."); Application::Console().WriteRaw("Too many arguments. At most " + std::to_string(max) + " argument(s) expected, got " + std::to_string(args.size()) + " instead.");
return false; return false;
} else if (args.size() < min) { } else if (args.size() < min) {
Application::Console().WriteRaw("Too few arguments. At least " + std::to_string(max) + " arguments expected, got " + std::to_string(args.size()) + " instead."); Application::Console().WriteRaw("Too few arguments. At least " + std::to_string(min) + " argument(s) expected, got " + std::to_string(args.size()) + " instead.");
return false; return false;
} }
} }
@@ -350,9 +350,10 @@ std::tuple<std::string, std::vector<std::string>> TConsole::ParseCommand(const s
} }
void TConsole::Command_Settings(const std::string&, const std::vector<std::string>& args) { void TConsole::Command_Settings(const std::string&, const std::vector<std::string>& args) {
if (!EnsureArgsCount(args, 0)) { if (!EnsureArgsCount(args, 1, 2)) {
return; return;
} }
} }
void TConsole::Command_Say(const std::string& FullCmd) { void TConsole::Command_Say(const std::string& FullCmd) {
@@ -478,12 +479,12 @@ void TConsole::Command_Status(const std::string&, const std::vector<std::string>
<< "\t\tEvent handlers: " << mLuaEngine->GetRegisteredEventHandlerCount() << "\n" << "\t\tEvent handlers: " << mLuaEngine->GetRegisteredEventHandlerCount() << "\n"
<< "\tSubsystems:\n" << "\tSubsystems:\n"
<< "\t\tGood/Starting/Bad: " << SystemsGood << "/" << SystemsStarting << "/" << SystemsBad << "\n" << "\t\tGood/Starting/Bad: " << SystemsGood << "/" << SystemsStarting << "/" << SystemsBad << "\n"
<< "\t\tShutting down/Shut down: " << SystemsShuttingDown << "/" << SystemsShutdown << "\n" << "\t\tShutting down/Shut down: " << SystemsShuttingDown << "/" << SystemsShutdown << "\n"
<< "\t\tGood: [ " << SystemsGoodList << " ]\n" << "\t\tGood: [ " << SystemsGoodList << " ]\n"
<< "\t\tStarting: [ " << SystemsStartingList << " ]\n" << "\t\tStarting: [ " << SystemsStartingList << " ]\n"
<< "\t\tBad: [ " << SystemsBadList << " ]\n" << "\t\tBad: [ " << SystemsBadList << " ]\n"
<< "\t\tShutting down: [ " << SystemsShuttingDownList << " ]\n" << "\t\tShutting down: [ " << SystemsShuttingDownList << " ]\n"
<< "\t\tShut down: [ " << SystemsShutdownList << " ]\n" << "\t\tShut down: [ " << SystemsShutdownList << " ]\n"
<< ""; << "";
Application::Console().WriteRaw(Status.str()); Application::Console().WriteRaw(Status.str());
+62 -24
View File
@@ -177,16 +177,29 @@ void TNetwork::Identify(TConnection&& RawConnection) {
return; return;
} }
std::shared_ptr<TClient> Client { nullptr }; std::shared_ptr<TClient> Client { nullptr };
if (Code == 'C') { try {
Client = Authentication(std::move(RawConnection)); if (Code == 'C') {
} else if (Code == 'D') { Client = Authentication(std::move(RawConnection));
HandleDownload(std::move(RawConnection)); } else if (Code == 'D') {
} else if (Code == 'P') { HandleDownload(std::move(RawConnection));
} else if (Code == 'P') {
boost::system::error_code ec;
write(RawConnection.Socket, buffer("P"), ec);
return;
} 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);
boost::system::error_code ec; boost::system::error_code ec;
write(RawConnection.Socket, buffer("P"), ec); RawConnection.Socket.shutdown(socket_base::shutdown_both, ec);
return; if (ec) {
} else { beammp_debugf("Failed to shutdown client socket: {}", ec.message());
beammp_errorf("Invalid code got in Identify: '{}'", Code); }
RawConnection.Socket.close(ec);
if (ec) {
beammp_debugf("Failed to close client socket: {}", ec.message());
}
} }
} }
@@ -246,12 +259,24 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
return nullptr; return nullptr;
} }
nlohmann::json AuthReq { std::string key(reinterpret_cast<const char*>(Data.data()), Data.size());
{ "key", std::string(reinterpret_cast<const char*>(Data.data()), Data.size()) }
}; nlohmann::json AuthReq{};
auto Target = "/pkToUser"; std::string AuthResStr{};
unsigned int ResponseCode = 0; try {
const auto AuthResStr = Http::POST(Application::GetBackendUrlForAuth(), 443, Target, AuthReq.dump(), "application/json", &ResponseCode); AuthReq = nlohmann::json {
{ "key", key }
};
auto Target = "/pkToUser";
unsigned int ResponseCode = 0;
AuthResStr = Http::POST(Application::GetBackendUrlForAuth(), 443, Target, AuthReq.dump(), "application/json", &ResponseCode);
} catch (const std::exception& e) {
beammp_debugf("Invalid json sent by client, kicking: {}", e.what());
ClientKick(*Client, "Invalid Key (invalid UTF8 string)!");
return nullptr;
}
try { try {
nlohmann::json AuthRes = nlohmann::json::parse(AuthResStr); nlohmann::json AuthRes = nlohmann::json::parse(AuthResStr);
@@ -393,6 +418,12 @@ std::vector<uint8_t> TNetwork::TCPRcv(TClient& c) {
} }
Header = *reinterpret_cast<int32_t*>(HeaderData.data()); Header = *reinterpret_cast<int32_t*>(HeaderData.data());
if (Header < 0) {
ClientKick(c, "Invalid packet - header negative");
beammp_errorf("Client {} send negative TCP header, ignoring packet", c.GetID());
return {};
}
std::vector<uint8_t> Data; std::vector<uint8_t> Data;
// TODO: This is arbitrary, this needs to be handled another way // TODO: This is arbitrary, this needs to be handled another way
if (Header < int32_t(100 * MB)) { if (Header < int32_t(100 * MB)) {
@@ -425,7 +456,7 @@ std::vector<uint8_t> TNetwork::TCPRcv(TClient& c) {
void TNetwork::ClientKick(TClient& c, const std::string& R) { void TNetwork::ClientKick(TClient& c, const std::string& R) {
beammp_info("Client kicked: " + R); beammp_info("Client kicked: " + R);
if (!TCPSend(c, StringToVector("K" + R))) { if (!TCPSend(c, StringToVector("K" + R))) {
beammp_debugf("tried to kick player '{}' (id {}), but was already connected", c.GetName(), c.GetID()); beammp_debugf("tried to kick player '{}' (id {}), but was already disconnected", c.GetName(), c.GetID());
} }
c.Disconnect("Kicked"); c.Disconnect("Kicked");
} }
@@ -522,8 +553,14 @@ void TNetwork::UpdatePlayer(TClient& Client) {
} }
void TNetwork::OnDisconnect(const std::weak_ptr<TClient>& ClientPtr) { void TNetwork::OnDisconnect(const std::weak_ptr<TClient>& ClientPtr) {
beammp_assert(!ClientPtr.expired()); std::shared_ptr<TClient> LockedClientPtr { nullptr };
auto LockedClientPtr = ClientPtr.lock(); try {
LockedClientPtr = ClientPtr.lock();
} catch (const std::exception&) {
beammp_warn("Client expired in OnDisconnect, this is unexpected");
return;
}
beammp_assert(LockedClientPtr != nullptr);
TClient& c = *LockedClientPtr; TClient& c = *LockedClientPtr;
beammp_info(c.GetName() + (" Connection Terminated")); beammp_info(c.GetName() + (" Connection Terminated"));
std::string Packet; std::string Packet;
@@ -540,7 +577,7 @@ void TNetwork::OnDisconnect(const std::weak_ptr<TClient>& ClientPtr) {
SendToAll(&c, StringToVector(Packet), false, true); SendToAll(&c, StringToVector(Packet), false, true);
Packet.clear(); Packet.clear();
auto Futures = LuaAPI::MP::Engine->TriggerEvent("onPlayerDisconnect", "", c.GetID()); auto Futures = LuaAPI::MP::Engine->TriggerEvent("onPlayerDisconnect", "", c.GetID());
LuaAPI::MP::Engine->ReportErrors(Futures); LuaAPI::MP::Engine->WaitForAll(Futures);
c.Disconnect("Already Disconnected (OnDisconnect)"); c.Disconnect("Already Disconnected (OnDisconnect)");
mServer.RemoveClient(ClientPtr); mServer.RemoveClient(ClientPtr);
} }
@@ -860,12 +897,13 @@ void TNetwork::SendToAll(TClient* c, const std::vector<uint8_t>& Data, bool Self
bool ret = true; bool ret = true;
mServer.ForEachClient([&](std::weak_ptr<TClient> ClientPtr) -> bool { mServer.ForEachClient([&](std::weak_ptr<TClient> ClientPtr) -> bool {
std::shared_ptr<TClient> Client; std::shared_ptr<TClient> Client;
{ try {
ReadLock Lock(mServer.GetClientMutex()); ReadLock Lock(mServer.GetClientMutex());
if (!ClientPtr.expired()) { Client = ClientPtr.lock();
Client = ClientPtr.lock(); } catch (const std::exception&) {
} else // continue
return true; beammp_warn("Client expired, shouldn't happen - if a client disconnected recently, you can ignore this");
return true;
} }
if (Self || Client.get() != c) { if (Self || Client.get() != c) {
if (Client->IsSynced() || Client->IsSyncing()) { if (Client->IsSynced() || Client->IsSyncing()) {
+30 -12
View File
@@ -1,6 +1,7 @@
#include "TServer.h" #include "TServer.h"
#include "Client.h" #include "Client.h"
#include "Common.h" #include "Common.h"
#include "CustomAssert.h"
#include "TNetwork.h" #include "TNetwork.h"
#include "TPPSMonitor.h" #include "TPPSMonitor.h"
#include <TLuaPlugin.h> #include <TLuaPlugin.h>
@@ -94,13 +95,20 @@ TServer::TServer(const std::vector<std::string_view>& Arguments) {
} }
void TServer::RemoveClient(const std::weak_ptr<TClient>& WeakClientPtr) { void TServer::RemoveClient(const std::weak_ptr<TClient>& WeakClientPtr) {
if (!WeakClientPtr.expired()) { std::shared_ptr<TClient> LockedClientPtr { nullptr };
TClient& Client = *WeakClientPtr.lock(); try {
beammp_debug("removing client " + Client.GetName() + " (" + std::to_string(ClientCount()) + ")"); LockedClientPtr = WeakClientPtr.lock();
Client.ClearCars(); } catch (const std::exception&) {
WriteLock Lock(mClientsMutex); // silently fail, as there's nothing to do
mClients.erase(WeakClientPtr.lock()); return;
} }
beammp_assert(LockedClientPtr != nullptr);
TClient& Client = *LockedClientPtr;
beammp_debug("removing client " + Client.GetName() + " (" + std::to_string(ClientCount()) + ")");
// TODO: Send delete packets for all cars
Client.ClearCars();
WriteLock Lock(mClientsMutex);
mClients.erase(WeakClientPtr.lock());
} }
void TServer::ForEachClient(const std::function<bool(std::weak_ptr<TClient>)>& Fn) { void TServer::ForEachClient(const std::function<bool(std::weak_ptr<TClient>)>& Fn) {
@@ -167,14 +175,20 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::vector<uin
} }
ParseVehicle(*LockedClient, StringPacket, Network); ParseVehicle(*LockedClient, StringPacket, Network);
return; return;
case 'J':
Network.SendToAll(LockedClient.get(), Packet, false, true);
return;
case 'C': { case 'C': {
if (Packet.size() < 4 || std::find(Packet.begin() + 3, Packet.end(), ':') == Packet.end()) if (Packet.size() < 4 || std::find(Packet.begin() + 3, Packet.end(), ':') == Packet.end())
break; break;
const auto PacketAsString = std::string(reinterpret_cast<const char*>(Packet.data()), Packet.size()); const auto PacketAsString = std::string(reinterpret_cast<const char*>(Packet.data()), Packet.size());
auto Futures = LuaAPI::MP::Engine->TriggerEvent("onChatMessage", "", LockedClient->GetID(), LockedClient->GetName(), PacketAsString.substr(PacketAsString.find(':', 3) + 2)); std::string Message = "";
const auto ColonPos = PacketAsString.find(':', 3);
if (ColonPos != std::string::npos && ColonPos + 2 < PacketAsString.size()) {
Message = PacketAsString.substr(ColonPos + 2);
}
if (Message.empty()) {
beammp_debugf("Empty chat message received from '{}' ({}), ignoring it", LockedClient->GetName(), LockedClient->GetID());
return;
}
auto Futures = LuaAPI::MP::Engine->TriggerEvent("onChatMessage", "", LockedClient->GetID(), LockedClient->GetName(), Message);
TLuaEngine::WaitForAll(Futures); TLuaEngine::WaitForAll(Futures);
LogChatMessage(LockedClient->GetName(), LockedClient->GetID(), PacketAsString.substr(PacketAsString.find(':', 3) + 1)); LogChatMessage(LockedClient->GetName(), LockedClient->GetID(), PacketAsString.substr(PacketAsString.find(':', 3) + 1));
if (std::any_of(Futures.begin(), Futures.end(), if (std::any_of(Futures.begin(), Futures.end(),
@@ -185,7 +199,8 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::vector<uin
})) { })) {
break; break;
} }
Network.SendToAll(nullptr, Packet, true, true); std::string SanitizedPacket = fmt::format("C:{}: {}", LockedClient->GetName(), Message);
Network.SendToAll(nullptr, StringToVector(SanitizedPacket), true, true);
return; return;
} }
case 'E': case 'E':
@@ -198,7 +213,6 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::vector<uin
case 'Z': // position packet case 'Z': // position packet
PPSMonitor.IncrementInternalPPS(); PPSMonitor.IncrementInternalPPS();
Network.SendToAll(LockedClient.get(), Packet, false, false); Network.SendToAll(LockedClient.get(), Packet, false, false);
HandlePosition(*LockedClient, StringPacket); HandlePosition(*LockedClient, StringPacket);
default: default:
return; return;
@@ -208,6 +222,10 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::vector<uin
void TServer::HandleEvent(TClient& c, const std::string& RawData) { void TServer::HandleEvent(TClient& c, const std::string& RawData) {
// E:Name:Data // E:Name:Data
// Data is allowed to have ':' // Data is allowed to have ':'
if (RawData.size() < 2) {
beammp_debugf("Client '{}' ({}) tried to send an empty event, ignoring", c.GetName(), c.GetID());
return;
}
auto NameDataSep = RawData.find(':', 2); auto NameDataSep = RawData.find(':', 2);
if (NameDataSep == std::string::npos) { if (NameDataSep == std::string::npos) {
beammp_warn("received event in invalid format (missing ':'), got: '" + RawData + "'"); beammp_warn("received event in invalid format (missing ':'), got: '" + RawData + "'");