mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-14 15:57:13 +00:00
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 <noreply@anthropic.com>
151 lines
5.8 KiB
C++
151 lines
5.8 KiB
C++
#include "CheatDetection.h"
|
|
#include "Database.h"
|
|
#include "Entity.h"
|
|
#include "PossessableComponent.h"
|
|
#include "Game.h"
|
|
#include "EntityManager.h"
|
|
#include "Character.h"
|
|
#include "User.h"
|
|
#include "UserManager.h"
|
|
#include "dConfig.h"
|
|
#include <optional>
|
|
#include "PlayerManager.h"
|
|
|
|
Entity* GetPossessedEntity(const LWOOBJID& objId) {
|
|
auto* entity = Game::entityManager->GetEntity(objId);
|
|
if (!entity) return nullptr;
|
|
|
|
auto* possessableComponent = entity->GetComponent<PossessableComponent>();
|
|
// If no possessable, then this entity is the most possessed entity.
|
|
if (!possessableComponent) return entity;
|
|
|
|
// If not, get the entity that possesses the fetched entity.
|
|
return Game::entityManager->GetEntity(possessableComponent->GetPossessor());
|
|
}
|
|
|
|
void ReportCheat(User* user, const SystemAddress& sysAddr, const char* messageIfNotSender, va_list args) {
|
|
if (!user) {
|
|
LOG("WARNING: User is null, using defaults.");
|
|
}
|
|
|
|
IPlayerCheatDetections::Info info;
|
|
if (user) info.userId = user->GetAccountID();
|
|
info.username = user ? user->GetUsername().c_str() : "User is null.";
|
|
|
|
// user string here because ToString is static and may change.
|
|
info.systemAddress = sysAddr.ToString();
|
|
|
|
constexpr int32_t bufSize = 4096;
|
|
char extraMsg[bufSize];
|
|
vsnprintf(extraMsg, bufSize, messageIfNotSender, args);
|
|
info.extraMessage = extraMsg;
|
|
|
|
Database::Get()->InsertCheatDetection(info);
|
|
|
|
LOG("Anti-cheat message: %s", extraMsg);
|
|
}
|
|
|
|
void LogAndSaveFailedAntiCheatCheck(const LWOOBJID& id, const SystemAddress& sysAddr, const CheckType checkType, const char* messageIfNotSender, va_list args) {
|
|
User* toReport = nullptr;
|
|
switch (checkType) {
|
|
case CheckType::Entity: {
|
|
auto* player = PlayerManager::GetPlayer(sysAddr);
|
|
auto* entity = GetPossessedEntity(id);
|
|
|
|
// If player exists and entity exists in world, use both for logging info.
|
|
if (entity && player) {
|
|
const auto* const playerChar = player->GetCharacter();
|
|
const auto& playerName = playerChar ? playerChar->GetName() : "(null player character)";
|
|
const auto* const entityChar = entity->GetCharacter();
|
|
const auto& entityName = entityChar ? entityChar->GetName() : "(null entity character)";
|
|
LOG("Player (%s) (%llu) at system address (%s) with sending player (%s) (%llu) does not match their own.",
|
|
playerName.c_str(), player->GetObjectID(),
|
|
sysAddr.ToString(),
|
|
entityName.c_str(), entity->GetObjectID());
|
|
if (playerChar) toReport = playerChar->GetParentUser();
|
|
// In the case that the target entity id did not exist, just log the player info.
|
|
} else if (player) {
|
|
const auto* const playerChar = player->GetCharacter();
|
|
const auto& playerName = playerChar ? playerChar->GetName() : "(null player character)";
|
|
LOG("Player (%s) (%llu) at system address (%s) with sending player (%llu) does not match their own.",
|
|
playerName.c_str(), player->GetObjectID(),
|
|
sysAddr.ToString(), id);
|
|
if (playerChar) toReport = playerChar->GetParentUser();
|
|
// In the rare case that the player does not exist, just log the system address and who the target id was.
|
|
} else {
|
|
LOG("Player at system address (%s) with sending player (%llu) does not match their own.",
|
|
sysAddr.ToString(), id);
|
|
}
|
|
break;
|
|
}
|
|
case CheckType::User: {
|
|
auto* user = UserManager::Instance()->GetUser(sysAddr);
|
|
|
|
if (user) {
|
|
const auto* const lastChar = user->GetLastUsedChar();
|
|
const auto& lastName = lastChar ? lastChar->GetName() : "(null last char)";
|
|
const auto lastObjID = lastChar ? lastChar->GetObjectID() : LWOOBJID_EMPTY;
|
|
LOG("User at system address (%s) (%s) (%llu) sent a packet as (%llu) which is not an id they own.",
|
|
sysAddr.ToString(), lastName.c_str(), lastObjID, id);
|
|
// Can't know sending player. Just log system address for IP banning.
|
|
} else {
|
|
LOG("No user found for system address (%s).", sysAddr.ToString());
|
|
}
|
|
toReport = user;
|
|
break;
|
|
}
|
|
};
|
|
ReportCheat(toReport, sysAddr, messageIfNotSender, args);
|
|
}
|
|
|
|
void CheatDetection::ReportCheat(User* user, const SystemAddress& sysAddr, const char* messageIfNotSender, ...) {
|
|
va_list args;
|
|
va_start(args, messageIfNotSender);
|
|
ReportCheat(user, sysAddr, messageIfNotSender, args);
|
|
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;
|
|
switch (checkType) {
|
|
case CheckType::Entity: {
|
|
// In this case, the sender may be an entity in the world.
|
|
auto* entity = GetPossessedEntity(id);
|
|
if (entity) {
|
|
invalidPacket = entity->IsPlayer() && entity->GetSystemAddress() != sysAddr;
|
|
}
|
|
break;
|
|
}
|
|
case CheckType::User: {
|
|
// In this case, the player is not an entity in the world, but may be a user still in world server if they are connected.
|
|
// Check here if the system address has a character with id matching the lwoobjid after unsetting the flag bits.
|
|
auto* sendingUser = UserManager::Instance()->GetUser(sysAddr);
|
|
if (!sendingUser) {
|
|
LOG("No user found for system address (%s).", sysAddr.ToString());
|
|
return false;
|
|
}
|
|
invalidPacket = true;
|
|
const auto characterId = id;
|
|
// Check to make sure the ID provided is one of the user's characters.
|
|
for (const auto& character : sendingUser->GetCharacters()) {
|
|
if (character && character->GetID() == characterId) {
|
|
invalidPacket = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// This will be true if the player does not possess the entity they are trying to send a packet as.
|
|
// or if the user does not own the character they are trying to send a packet as.
|
|
|
|
return !invalidPacket;
|
|
}
|