diff --git a/Server/mods/deathmatch/logic/CGame.cpp b/Server/mods/deathmatch/logic/CGame.cpp index 910a1722dde..3b21533de4a 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -1672,6 +1672,7 @@ void CGame::AddBuiltInEvents() m_Events.AddEvent("onElementInteriorChange", "oldInterior, newInterior", nullptr, false); m_Events.AddEvent("onElementAttach", "attachSource, attachOffsetX, attachOffsetY, attachOffsetZ, attachOffsetRX, attachOffsetRY, attachOffsetRZ", nullptr, false); m_Events.AddEvent("onElementDetach", "detachSource, detachWorldX, detachWorldY, detachWorldZ, detachWorldRX, detachWorldRY, detachWorldRZ", nullptr, false); + m_Events.AddEvent("onElementPositionChange", "oldX, oldY, oldZ, newX, newY, newZ", nullptr, false); // Radar area events diff --git a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 14644dc1094..4a98dcbf21a 100644 --- a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -1305,6 +1305,9 @@ bool CStaticFunctionDefinitions::SetElementPosition(CElement* pElement, const CV player->SetTeleported(true); } + // Get element old position + CVector elementOldPosition = pElement->GetPosition(); + // Update our position for that entity. pElement->SetPosition(vecPosition); @@ -1340,6 +1343,27 @@ bool CStaticFunctionDefinitions::SetElementPosition(CElement* pElement, const CV if (ped && ped->HasJetPack()) m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(ped, GIVE_PED_JETPACK, *BitStream.pBitStream)); + // This event is triggered server-side to allow anti-cheat scripts to distinguish between + // position changes made by the server (e.g., elevators, scripted teleports) and those made by the client. + // Without this event, anti-cheat systems may falsely detect legitimate server-side teleports as cheats, + // since they only check the distance between old and new positions. + // By listening to this event, anti-cheat scripts can safely ignore server-initiated position changes. + CLuaArguments Arguments; + Arguments.PushElement(pElement); + Arguments.PushNumber(elementOldPosition.fX); + Arguments.PushNumber(elementOldPosition.fY); + Arguments.PushNumber(elementOldPosition.fZ); + Arguments.PushNumber(vecPosition.fX); + Arguments.PushNumber(vecPosition.fY); + Arguments.PushNumber(vecPosition.fZ); + Arguments.PushBool(bWarp); + + + if (!pElement->CallEvent("onElementPositionChange", Arguments)) + { + return false; + } + return true; }