Защита игры: не доверяй клиенту

Раздел про защиту своей игры, а не про обход чужой. Всё, что делает клиент, можно подделать — задача разработчика в том, чтобы от этого ничего не зависело.

Главное правило

Клиент — источник запросов, а не решений. Он говорит «я нажал кнопку», а не «начисли мне тысячу монет». Сервер решает, можно ли, и только он меняет состояние игры.

Типовые дыры

  • RemoteEvent без проверок: сервер верит любым аргументам от клиента
  • Цена и количество приходят с клиента вместо того, чтобы лежать на сервере
  • Ценные данные хранятся в объектах, доступных клиенту
  • Нет ограничения частоты: один игрок вызывает событие тысячу раз в секунду
  • Проверка «можно ли» сделана в LocalScript и на сервере не повторяется

Как проверять аргументы

ServerScriptService/BuyItem.lua
  1. local ReplicatedStorage = game:GetService("ReplicatedStorage")
  2. local buy = ReplicatedStorage:WaitForChild("BuyItem")
  3.  
  4. local PRICES = {
  5. sword = 100,
  6. shield = 150,
  7. }
  8.  
  9. buy.OnServerEvent:Connect(function(player, itemName)
  10. if typeof(itemName) ~= "string" then
  11. return
  12. end
  13.  
  14. local price = PRICES[itemName]
  15.  
  16. if not price then
  17. return
  18. end
  19.  
  20. local coins = player:FindFirstChild("leaderstats")
  21. and player.leaderstats:FindFirstChild("Coins")
  22.  
  23. if not coins or coins.Value < price then
  24. return
  25. end
  26.  
  27. coins.Value -= price
  28. -- выдать предмет
  29. end)

Цена лежит на сервере, тип аргумента проверен, баланс сверен. Клиент не передаёт ни цену, ни количество монет.

Чего этот раздел не делает

Здесь нет ни одного примера «как получить преимущество в чужой игре»: это запрещено правилами Roblox и правилами портала. Как устроены такие программы и чем заканчиваются, разобрано в разделе о безопасности игрока.