Skip to content

Add client-side Lua event sequence management and anti-replay checks#4990

Open
QueryOfficial wants to merge 2 commits into
multitheftauto:masterfrom
QueryOfficial:fix-event-blocking
Open

Add client-side Lua event sequence management and anti-replay checks#4990
QueryOfficial wants to merge 2 commits into
multitheftauto:masterfrom
QueryOfficial:fix-event-blocking

Conversation

@QueryOfficial

@QueryOfficial QueryOfficial commented Jun 30, 2026

Copy link
Copy Markdown

Summary

Hardens Lua event sync against verbatim replay and protects server→client event delivery from Lua-level cancellation.

  • Introduced a monotonic sequence for Lua events to prevent replay attacks (eBitStreamVersion::ClientLuaEventSequence)
  • Added methods to manage the client Lua event sequence in CClientGame and CPlayer
  • Implemented checks in CGame and CLuaEventPacket to validate the sequence before dispatch
  • Enhanced event handling in CEvents to support remote server event pulses (scoped cancelEvent + debug-hook guard)
  • Updated relevant classes and methods to accommodate the new sequence logic

Client → server (anti-replay)

  • Client appends monotonic uint32 sequence on each triggerServerEvent / triggerLatentServerEvent
  • Server tracks next expected sequence per player; stale sequences are dropped (monotonic, not strict equality)
  • Legitimate duplicate payloads remain valid — same event/args twice sends two distinct sequences

Server → client (event blocking mitigation)

  • Remote event pulse during CPacketHandler::Packet_LuaEvent dispatch
  • cancelEvent accepted only from the handler's own Lua VM (injected scripts cannot cancel foreign handlers)
  • addDebugHook preEvent "skip" ignored during the pulse

Docs: Server/mods/deathmatch/README.event-sync-guard.md

Requires matching client + server build (new bitstream feature). No crypto/secrets — open-source-safe design.

Out of scope: client-side outbound event blocking before the packet is sent (not fixable server-side).


Motivation

Cheats can resend captured PACKET_ID_LUA_EVENT packets verbatim (ResendPacket / packet filter) and block server→client events via injected scripts (cancelEvent, debug-hook "skip").

The server previously had no replay gate for Lua events, and any injected resource could cancel handlers for server-triggered triggerClientEvent.

Because MTA's client is open source, this fix uses only:

  1. Server-side state the client cannot rewind (expected sequence counter)
  2. Logical ownership checks (who may cancel an in-flight remote event)

No session keys or client-side signatures — those would be trivially bypassed in a recompiled cheat.


Test plan

Build client + server from this branch (matching bitstream). Private test server, 2 clients if testing server→client blocking.

Client → server

-- server.lua
addEvent("testReplay", true)
addEventHandler("testReplay", root, function(msg)
    outputChatBox("received: " .. tostring(msg))
end)

-- client.lua
triggerServerEvent("testReplay", localPlayer, "same")
triggerServerEvent("testReplay", localPlayer, "same")  -- both must arrive (different sequences)
  • Call triggerServerEvent twice with identical args → both reach the server
  • Capture and resend the same PACKET_ID_LUA_EVENT packet verbatim (cheat ResendPacket) → server drops the replay

Server → client

  • Server triggerClientEvent to a registered handler
  • Injected/foreign resource calls cancelEvent() during dispatch → intended handler still runs
  • Debug hook with preEvent "skip" during server remote event → event still dispatches

Expected results

  • Verbatim Lua event replay → rejected (stale sequence)
  • Legitimate duplicate triggerServerEvent calls → both accepted
  • Foreign cancelEvent / debug-hook skip on server events → ignored
  • Client-side block before send (packet never arrives) → unchanged (existing rate limits still apply)

Checklist

  • Your code should follow the coding guidelines.
  • Smaller pull requests are easier to review. If your pull request is beefy, your pull request should be reviewable commit-by-commit.

- Introduced a monotonic sequence for Lua events to prevent replay attacks.
- Added methods to manage the client Lua event sequence in CClientGame and CPlayer.
- Implemented checks in CGame and CLuaEventPacket to validate the sequence.
- Enhanced event handling in CEvents to support remote server event pulses.
- Updated relevant classes and methods to accommodate the new sequence logic.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant