From 081e26ded8560ac2e28f8d04eb51879dfd49c846 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Tue, 10 Nov 2026 13:06:27 -0600 Subject: [PATCH] fix: a player's own messages have to be sent for that player The die request, the smash request, the loaded notice, chat commands and the racing pickup notice were handled for whichever object they named. They are now only handled for the player that sent them, or for the vehicle that player drives where the client sends them for it. A racing pickup also has to be an object of a race track that is not a player, a vehicle or the zone's own object. Co-Authored-By: Claude Fable 5.2 --- dGame/dGameMessages/CombatMessages.cpp | 13 +++++++++++++ dGame/dGameMessages/PlayerMessages.cpp | 6 ++++++ dGame/dGameMessages/RacingMessages.cpp | 17 +++++++++++++++++ dGame/dGameMessages/ZoneMessages.cpp | 6 ++++++ dGame/dUtilities/CheatDetection.cpp | 6 ++++++ dGame/dUtilities/CheatDetection.h | 9 +++++++++ 6 files changed, 57 insertions(+) diff --git a/dGame/dGameMessages/CombatMessages.cpp b/dGame/dGameMessages/CombatMessages.cpp index dba6dc2e4..b9283a5b6 100644 --- a/dGame/dGameMessages/CombatMessages.cpp +++ b/dGame/dGameMessages/CombatMessages.cpp @@ -1,5 +1,6 @@ #include "CombatMessages.h" +#include "CheatDetection.h" #include "BitStreamUtils.h" #include "ControllablePhysicsComponent.h" #include "CppScripts.h" @@ -69,6 +70,13 @@ namespace GameMessages { void RequestDie::Handle(Entity& entityRef, const SystemAddress& sysAddr) { Entity* entity = &entityRef; + + // A player asks for their own death or that of the vehicle they drive + if (!CheatDetection::IsSendersPlayer(entity->GetObjectID(), sysAddr)) { + LOG("Object %llu (LOT %i) is not the player that asked for its death or their vehicle", entity->GetObjectID(), entity->GetLOT()); + return; + } + auto* zoneController = Game::zoneManager->GetZoneControlObject(); auto* racingControlComponent = zoneController->GetComponent(); @@ -103,6 +111,11 @@ namespace GameMessages { } void RequestSmashPlayer::Handle(Entity& entity, const SystemAddress& sysAddr) { + // A player asks for their own smash + if (!entity.IsPlayer() || entity.GetSystemAddress() != sysAddr) { + LOG("Object %llu (LOT %i) is not the player that asked for its smash", entity.GetObjectID(), entity.GetLOT()); + return; + } entity.Smash(entity.GetObjectID()); } diff --git a/dGame/dGameMessages/PlayerMessages.cpp b/dGame/dGameMessages/PlayerMessages.cpp index 52bff2c8c..909ccf619 100644 --- a/dGame/dGameMessages/PlayerMessages.cpp +++ b/dGame/dGameMessages/PlayerMessages.cpp @@ -1,5 +1,6 @@ #include "PlayerMessages.h" +#include "Logger.h" #include "BitStreamUtils.h" #include "Character.h" #include "CharacterComponent.h" @@ -190,6 +191,11 @@ namespace GameMessages { } void ParseChatMessage::Handle(Entity& entity, const SystemAddress& sysAddr) { + // A chat command runs for the player that typed it + if (!entity.IsPlayer() || entity.GetSystemAddress() != sysAddr) { + LOG("Object %llu (LOT %i) is not the player that sent a chat command for it", entity.GetObjectID(), entity.GetLOT()); + return; + } if (!wsString.empty() && wsString[0] == L'/') { SlashCommandHandler::HandleChatCommand(wsString, &entity, sysAddr); } diff --git a/dGame/dGameMessages/RacingMessages.cpp b/dGame/dGameMessages/RacingMessages.cpp index 32b7679cb..b4ac589a9 100644 --- a/dGame/dGameMessages/RacingMessages.cpp +++ b/dGame/dGameMessages/RacingMessages.cpp @@ -1,5 +1,7 @@ #include "RacingMessages.h" +#include "CheatDetection.h" +#include "PossessorComponent.h" #include "BitStreamUtils.h" #include "CharacterComponent.h" #include "Entity.h" @@ -215,6 +217,21 @@ namespace GameMessages { return; } + // A player reports the pickups their own vehicle drove through + if (!CheatDetection::IsSendersPlayer(vehicle.GetObjectID(), sysAddr)) { + LOG("Object %llu (LOT %i) is not the vehicle of the player that reported a pickup for it", vehicle.GetObjectID(), vehicle.GetLOT()); + return; + } + + // Pickups are objects of a race track, and never a player, a vehicle or the zone's own object + const auto* const zoneControl = Game::zoneManager->GetZoneControlObject(); + if (!zoneControl || !zoneControl->GetComponent() + || pickup == zoneControl || pickup->IsPlayer() + || pickup->GetComponent() || pickup->GetComponent()) { + LOG("Object %llu (LOT %i) is not a pickup of a race track", pickupObjID, pickup->GetLOT()); + return; + } + // The sender is the vehicle; credit its driver if it has one. Entity* entity = &vehicle; auto* possessableComponent = entity->GetComponent(); diff --git a/dGame/dGameMessages/ZoneMessages.cpp b/dGame/dGameMessages/ZoneMessages.cpp index e70882337..5937a01e4 100644 --- a/dGame/dGameMessages/ZoneMessages.cpp +++ b/dGame/dGameMessages/ZoneMessages.cpp @@ -1,4 +1,5 @@ #include "ZoneMessages.h" +#include "Logger.h" #include "MovementMessages.h" #include "MissionMessages.h" @@ -63,6 +64,11 @@ namespace GameMessages { void PlayerLoaded::Handle(Entity& entityRef, const SystemAddress& sysAddr) { auto* entity = &entityRef; + // A player reports that they themselves have loaded + if (!entity->IsPlayer() || entity->GetSystemAddress() != sysAddr) { + LOG("Object %llu (LOT %i) is not the player that reported it as loaded", entity->GetObjectID(), entity->GetLOT()); + return; + } const auto* const zoneControlEntity = Game::entityManager->GetZoneControlEntity(); SendPlayerReady(entity->GetObjectID(), zoneControlEntity ? zoneControlEntity->GetObjectID() : LWOOBJID_EMPTY, sysAddr); entity->SetPlayerReadyForUpdates(); diff --git a/dGame/dUtilities/CheatDetection.cpp b/dGame/dUtilities/CheatDetection.cpp index acc415a69..9c34a00e6 100644 --- a/dGame/dUtilities/CheatDetection.cpp +++ b/dGame/dUtilities/CheatDetection.cpp @@ -105,6 +105,12 @@ void CheatDetection::ReportCheat(User* user, const SystemAddress& sysAddr, const va_end(args); } +bool CheatDetection::IsSendersPlayer(const LWOOBJID& id, const SystemAddress& sysAddr) { + // The player themselves, or the player that possesses the object + const auto* const entity = GetPossessedEntity(id); + return entity && entity->IsPlayer() && entity->GetSystemAddress() == sysAddr; +} + bool CheatDetection::VerifyLwoobjidIsSender(const LWOOBJID& id, const SystemAddress& sysAddr, const CheckType checkType, const char* messageIfNotSender, ...) { // Get variables we'll need for the whole function bool invalidPacket = false; diff --git a/dGame/dUtilities/CheatDetection.h b/dGame/dUtilities/CheatDetection.h index ee0dce89b..a80a25782 100644 --- a/dGame/dUtilities/CheatDetection.h +++ b/dGame/dUtilities/CheatDetection.h @@ -24,6 +24,15 @@ namespace CheatDetection { */ bool VerifyLwoobjidIsSender(const LWOOBJID& id, const SystemAddress& sysAddr, const CheckType checkType, const char* messageIfNotSender, ...); void ReportCheat(User* user, const SystemAddress& sysAddr, const char* messageIfNotSender, ...); + + /** + * @brief Whether an object is the player at a system address or the object that player possesses, such as their vehicle. + * Unlike VerifyLwoobjidIsSender this is false for every other object, so it is for messages only a player sends about themselves. + * + * @param id The object ID a message was sent for + * @param sysAddr The system address which sent the message + */ + bool IsSendersPlayer(const LWOOBJID& id, const SystemAddress& sysAddr); }; #endif //!__CHEATDETECTION__H__