Initial MVP: drivable RX-7 vertical slice + server-authoritative systems
CI / quality (push) Failing after 5m53s
CI / quality (push) Failing after 5m53s
- Rojo + Wally project; ProfileStore (loleris) for safe saves - Shared: GameConfig, Cars (RX-7 free starter), ProfileTemplate, Remotes - Server: DataManager, Economy, CarManager, RaceManager, MapBuilder, AntiCheat - Client: neon HUD + Garage/Dealership UI (buy/equip/upgrade), race/daily - Procedural Tokyo expressway (spawn pad, finish line, neon pylons, kill walls) - CI workflow (Gitea/GitHub), Makefile, selene+stylua clean, rojo builds - Docs: README, GDD, realistic solo ROADMAP
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
--!strict
|
||||
--[[
|
||||
AntiCheat.luau (SERVER)
|
||||
MVP-level movement sanity check. Not a fortress — a speed bump that catches
|
||||
obvious speed/teleport hacks. Because money & car ownership are ALREADY
|
||||
server-authoritative (Economy validates everything), the main client attack
|
||||
surface left is movement, so we watch that.
|
||||
|
||||
Approach: sample each player's HRP position periodically; if they cover
|
||||
ground implying a sustained speed above MaxPlausibleSpeed, snap them back
|
||||
to their last valid position. Generous threshold to avoid false positives
|
||||
from legit fast cars.
|
||||
]]
|
||||
|
||||
local Players = game:GetService("Players")
|
||||
local RunService = game:GetService("RunService")
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
|
||||
local GameConfig = require(ReplicatedStorage.Shared.GameConfig)
|
||||
|
||||
local AntiCheat = {}
|
||||
|
||||
type Track = { lastPos: Vector3, lastValid: CFrame }
|
||||
|
||||
local tracked: { [Player]: Track } = {}
|
||||
local SAMPLE_INTERVAL = 0.5
|
||||
|
||||
local function hrpOf(player: Player): BasePart?
|
||||
local char = player.Character
|
||||
return char and char:FindFirstChild("HumanoidRootPart") :: BasePart?
|
||||
end
|
||||
|
||||
function AntiCheat.init()
|
||||
local function watch(player: Player)
|
||||
player.CharacterAdded:Connect(function(char)
|
||||
local hrp = char:WaitForChild("HumanoidRootPart", 10) :: BasePart?
|
||||
if hrp then
|
||||
tracked[player] = { lastPos = hrp.Position, lastValid = hrp.CFrame }
|
||||
end
|
||||
end)
|
||||
end
|
||||
for _, p in ipairs(Players:GetPlayers()) do
|
||||
watch(p)
|
||||
end
|
||||
Players.PlayerAdded:Connect(watch)
|
||||
Players.PlayerRemoving:Connect(function(p)
|
||||
tracked[p] = nil
|
||||
end)
|
||||
|
||||
local accum = 0
|
||||
RunService.Heartbeat:Connect(function(dt)
|
||||
accum += dt
|
||||
if accum < SAMPLE_INTERVAL then
|
||||
return
|
||||
end
|
||||
local interval = accum
|
||||
accum = 0
|
||||
|
||||
for player, track in pairs(tracked) do
|
||||
local hrp = hrpOf(player)
|
||||
if not hrp then
|
||||
continue
|
||||
end
|
||||
local pos = hrp.Position
|
||||
-- Falling doesn't count (avoid flagging big drops). Only guard XZ plane.
|
||||
local horizontal = Vector3.new(pos.X - track.lastPos.X, 0, pos.Z - track.lastPos.Z).Magnitude
|
||||
local hSpeed = horizontal / interval
|
||||
|
||||
if hSpeed > GameConfig.MaxPlausibleSpeed then
|
||||
-- Snap back: likely a speed/teleport hack.
|
||||
hrp.CFrame = track.lastValid
|
||||
hrp.AssemblyLinearVelocity = Vector3.zero
|
||||
else
|
||||
track.lastValid = hrp.CFrame
|
||||
end
|
||||
track.lastPos = hrp.Position
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
return AntiCheat
|
||||
Reference in New Issue
Block a user