Skip to content

Conversation

@imfelipedev
Copy link
Contributor

I developed this event to detect players using teleport cheats. The onPlayerTeleport event will only be triggered when the player's position is significantly distant from the position registered on the server, and this change was not caused by the setElementPosition function. This implementation is based on the suggestion provided in the issue: #3293.

imfelipedev and others added 2 commits January 9, 2025 23:58
Co-authored-by: Nico <122193236+Nico8340@users.noreply.github.com>
imfelipedev and others added 2 commits January 10, 2025 01:58
Co-authored-by: Nico <122193236+Nico8340@users.noreply.github.com>
Co-authored-by: Nico <122193236+Nico8340@users.noreply.github.com>
@CArg22
Copy link
Contributor

CArg22 commented Jan 10, 2025

This event will give people false belief that they are protected from cheaters. Cheater can just teleport X times by 49 units.

Also,. take an account years of backlog where people teleporting players clientside what means they will get false positive detection. Refactoring may not be an option.

If you want to protect your server from cheaters that using teleport hacks, calculate traveled distance over time and implement your "customSetElementPosition" that skips accumulation and use it only server side. Code will be look like:

local playersTraveledDistance = {}

local function customSetElementPosition(element, x, y, z)
    if isElement(element) and getElementType(element) == "player" then
        local playerData = playersTraveledDistance[element]
        if playerData then
            playerData.lastPosition = { x, y, z }
        end
        setElementPosition(element, x, y, z)
    end
end

setTimer(function()
    for _, player in ipairs(getElementsByType("player")) do
        if isElement(player) then
            local x, y, z = getElementPosition(player)
            
            if not playersTraveledDistance[player] then
                playersTraveledDistance[player] = {
                    lastPosition = { x, y, z },
                    totalDistance = 0
                }
            end

            local playerData = playersTraveledDistance[player]
            local lastPos = playerData.lastPosition

            local dx, dy, dz = x - lastPos[1], y - lastPos[2], z - lastPos[3]
            local distance = math.sqrt(dx * dx + dy * dy + dz * dz)

            playerData.totalDistance = playerData.totalDistance + distance
            playerData.lastPosition = { x, y, z }
        end
    end
end, 1000, 0)

@Proxy-99
Copy link
Contributor

Proxy-99 commented Jan 10, 2025

building project "Deathmatch.vcxproj" -- FAILED.
4>F:\MTA\mtasa-blue\Server\mods\deathmatch\logic\packets\CPlayerPuresyncPacket.cpp(136,32): error C2660: 'CElement::CallEvent': function does not take 1 arguments

 pSourcePlayer->CallEvent("onPlayerTeleport");

imfelipedev and others added 3 commits January 10, 2025 15:40
Co-authored-by: Nico <122193236+Nico8340@users.noreply.github.com>
@imfelipedev imfelipedev reopened this Jan 10, 2025
@imfelipedev
Copy link
Contributor Author

imfelipedev commented Jan 10, 2025

This event will give people false belief that they are protected from cheaters. Cheater can just teleport X times by 49 units.

Also,. take an account years of backlog where people teleporting players clientside what means they will get false positive detection. Refactoring may not be an option.

If you want to protect your server from cheaters that using teleport hacks, calculate traveled distance over time and implement your "customSetElementPosition" that skips accumulation and use it only server side. Code will be look like:

local playersTraveledDistance = {}

local function customSetElementPosition(element, x, y, z)
    if isElement(element) and getElementType(element) == "player" then
        local playerData = playersTraveledDistance[element]
        if playerData then
            playerData.lastPosition = { x, y, z }
        end
        setElementPosition(element, x, y, z)
    end
end

setTimer(function()
    for _, player in ipairs(getElementsByType("player")) do
        if isElement(player) then
            local x, y, z = getElementPosition(player)
            
            if not playersTraveledDistance[player] then
                playersTraveledDistance[player] = {
                    lastPosition = { x, y, z },
                    totalDistance = 0
                }
            end

            local playerData = playersTraveledDistance[player]
            local lastPos = playerData.lastPosition

            local dx, dy, dz = x - lastPos[1], y - lastPos[2], z - lastPos[3]
            local distance = math.sqrt(dx * dx + dy * dy + dz * dz)

            playerData.totalDistance = playerData.totalDistance + distance
            playerData.lastPosition = { x, y, z }
        end
    end
end, 1000, 0)

Regarding the minimum value, I found it valid to change. As for altering the player's position on the client side, I believe that if someone truly wants to protect their server and avoid desynchronizations, they wouldn't use this approach. Furthermore, in larger servers, your example code wouldn't be optimized and would likely cause issues. I tested the first code version with a "hacker" colleague, and it worked very well.

@ffsPLASMA
Copy link
Contributor

I would rather put it on server network level whenever the client sends a new position sync update and distance is greater than a server config var, trigger the event. That behaviour should also ignore sync updates done by server itself via setElementPosition or respawnPlayer or whatever.

Also account for all client updates done in whatever way, in your example, what does the client stop from executing custom lua code with setElementPosition.

@imfelipedev
Copy link
Contributor Author

I would rather put it on server network level whenever the client sends a new position sync update and distance is greater than a server config var, trigger the event. That behaviour should also ignore sync updates done by server itself via setElementPosition or respawnPlayer or whatever.

Also account for all client updates done in whatever way, in your example, what does the client stop from executing custom lua code with setElementPosition.

But the code is doing exactly that. The onPlayerTeleport event will only be triggered when the client sends a significantly distant position to the server. This code was not designed to prevent a client-side script from using setElementPosition but rather to alert the server when a client moves irregularly without prior server action.

@tederis tederis added the enhancement New feature or request label Jan 18, 2025
@Dutchman101
Copy link
Member

Dutchman101 commented Jan 28, 2025

This is useful for classic hacks (non-lua injector) and it's server-side, only one extra tool to raise the border. Especially useful now that most Lua injector hacks were patched this month, at least for now they are rare.

Server owners should be mindful of its limitations, log first, and add enough checks.

Please make a follow up PR for an event to also keep track of multiple smaller moves that add up to a large move together, if within a certain interval it'll trigger that.

Thanks! @imfelipedev (and please add the new event to wiki)

@Dutchman101 Dutchman101 merged commit 4000ea4 into multitheftauto:master Jan 28, 2025
MTABot pushed a commit that referenced this pull request Jan 28, 2025
@botder botder added this to the 1.6.1 milestone Jan 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants