FiveM: система штрафов ГИБДД для RP-сервера
Игрок за рулём проезжает на красный, полицейский открывает MDT, выписывает штраф на конкретную статью — и деньги списываются с банковского счёта нарушителя, а не появляются из воздуха командой /give. Если у вас на сервере штрафы всё ещё выдаются через /fine <id> <сумма> в чат, вы теряете и атмосферу, и контроль над экономикой. Разберём, как собрать рабочую систему: MDT для оформления, штрафстоянку для эвакуированных тачек и баланс сумм, который не обвалит и не обесценит вашу игровую валюту.
Содержание
Зачем на RP-сервере нужна нормальная система штрафов
Штрафы — это не декорация для полицейской РП-линии, а один из немногих легальных способов изымать деньги из экономики сервера. Без постоянного оттока валюты донат и фарм быстро разгоняют инфляцию: через пару недель штраф в 500$ превращается в чаевые, которые никто не замечает.
Плюс есть чисто ролевая сторона: если штраф выписывается командой в консоль, это ломает immersion — игрок видит системное сообщение вместо диалога с офицером. MDT (Mobile Data Terminal) решает обе задачи разом: полицейский оформляет протокол через игровой интерфейс на планшете/ноутбуке в машине, штраф автоматически списывается с banking-счёта, а в базе остаётся история нарушений конкретного персонажа — что удобно для судов, амнистий и разборов жалоб.
Полезно заранее продумать, как эта система стыкуется с общей экономикой RP-сервера и с системой работ — штрафы и зарплаты полицейских должны быть в одной весовой категории, иначе either коп нищий, либо штраф — не наказание, а мелочь.
MDT: связующее звено между полицией и базой данных
MDT на FiveM — это NUI-интерфейс (HTML/CSS/JS поверх игры), который открывается по команде или клавише и общается с сервером через events и exports. В большинстве RP-проектов MDT — самостоятельный ресурс, завязанный на фреймворк:
- для ESX — типовые связки вроде
esx_mdt/esx_policejobc таблицами в базе черезoxmysql; - для QBCore —
qb-mdtв паре сqb-policejob, где штрафы, ордера и досье персонажей идут через один интерфейс.
Логика везде похожа: MDT дергает серверный event, event проверяет права (job.name == 'police' и нужный job.grade), пишет запись в таблицу mdt_fines (или как она названа в конкретном ресурсе) и вызывает списание денег у игрока-нарушителя. Если у вас ESX, минимальный конфиг подключения обычно лежит в config.lua ресурса и требует указать имя таблицы банковского счёта — на новых сборках ESX Legacy это bank_accounts, на старых версиях может быть просто поле bank в users. Перед установкой сверьтесь, какая версия ESX стоит у вас — иначе списание денег просто не сработает и штраф "выпишется", а счёт не тронется.
Отдельно стоит продумать права доступа: стажёр (grade 0) не должен иметь возможность аннулировать чужие штрафы, а вот выписывать — да. Это настраивается в конфиге job'а полиции, обычно массивом grade → набор permissions.
Поднять сервер FiveM (GTA V) за пару минут
Готовый образ MAATRIX GAMES: NVMe, AMD EPYC, DDoS-защита, панель управления. Локации UK, US, RU. Оплата картой РФ, СБП или криптой.
Создать серверВыписка штрафа: от клика в MDT до записи в базу
Разберём упрощённо, как выглядит серверная часть на ESX-подобном фреймворке — с чем реально придётся работать, если вы кастомизируете готовый MDT под свой сервер.
Таблица в базе (MySQL/oxmysql):
CREATE TABLE IF NOT EXISTS `mdt_fines` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`citizenid` VARCHAR(50) NOT NULL,
`officer_id` VARCHAR(50) NOT NULL,
`charge` VARCHAR(255) NOT NULL,
`amount` INT(11) NOT NULL,
`date` DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
Серверный обработчик события (пример на ESX, oxmysql):
RegisterNetEvent('mdt:issueFine')
AddEventHandler('mdt:issueFine', function(targetId, charge, amount)
local src = source
local xPlayer = ESX.GetPlayerFromId(src)
local xTarget = ESX.GetPlayerFromId(targetId)
if xPlayer.job.name ~= 'police' then return end
if not xTarget then return end
xTarget.removeAccountMoney('bank', amount)
MySQL.insert('INSERT INTO mdt_fines (citizenid, officer_id, charge, amount) VALUES (?, ?, ?, ?)', {
xTarget.identifier, xPlayer.identifier, charge, amount
})
TriggerClientEvent('esx:showNotification', targetId, ('Штраф: %s (~r~%s$~s~)'):format(charge, amount))
end)
Ключевые моменты, о которые обычно спотыкаются:
- Проверка баланса перед списанием. Если у игрока на счету меньше суммы штрафа,
removeAccountMoneyв части фреймворков просто уйдёт в минус, в других — не выполнится вовсе. Проверяйте баланс заранее и, если денег не хватает, переводите часть суммы в долг (система "штраф в рассрочку" — актуально, если на сервере есть банковские кредиты) или конвертируйте в реальный срок на штрафстоянке/аресте. - Rate limit на выписку. Без него нечистый на руку "коп" может накрутить десятки штрафов за секунды через дублирующий клик или спам события. Добавьте
os.time()-проверку минимального интервала между вызовами от одногоsource. - Логирование в Discord. Полезно продублировать каждый штраф в лог-канал через webhook — это снимает половину споров "а мне выписали незаконно" в тикетах. Если у вас уже настроена Discord-интеграция сервера, добавить туда ещё один embed для штрафов — вопрос десяти строк кода.
Баланс: сколько должен стоить штраф в вашей экономике
Это самая частая ошибка новых RP-проектов: суммы штрафов копируют с чужого сервера или из реальной жизни, не глядя на собственную зарплатную сетку. Если водитель такси зарабатывает 300$ за смену, а штраф за превышение скорости — 1500$, никто просто не будет ездить по правилам, потому что бояться нечего — штраф всё равно не заплатить, и он спишется в минус или будет проигнорирован через impound.
Рабочий подход — considerить штрафы не в абсолютных цифрах, а в долях от средней часовой ставки на сервере. Если средний легальный заработок — условно 800$/час, диапазон штрафов стоит держать в пределах 5-40% от этой суммы за рядовые нарушения и выше — за тяжкие (оружие, сбитый пешеход, побег от полиции).
Пример конфига статей (config.lua MDT/policejob):
Config.Charges = {
{ code = 'ПДД-1', name = 'Превышение скорости', fine = 150, jail = 0 },
{ code = 'ПДД-4', name = 'Проезд на красный', fine = 250, jail = 0 },
{ code = 'УК-12', name = 'Незаконное оружие', fine = 1500, jail = 5 },
{ code = 'УК-19', name = 'Побег от полиции', fine = 2000, jail = 8 },
{ code = 'УК-27', name = 'Наезд со смертельным исходом', fine = 5000, jail = 15 },
}
Таблица для ориентира — как раскладывать статьи по группам тяжести (конкретные суммы подгоняйте под свою экономику, это не готовые цифры для копирования):
| Категория | Доля от почасовой ставки | Пример |
|---|---|---|
| Мелкие ПДД | 5-15% | превышение скорости, парковка |
| Средние нарушения | 20-40% | вождение без прав, ДТП с виновностью |
| Тяжкие статьи | 100-300%+ | оружие, сопротивление, побег |
| Особо тяжкие | 500%+ и арест | убийство, крупный грабёж |
Отдельно проверьте, не дублируются ли штрафы с системой фракций и банд — если у вас есть нелегальные фракции, для которых штрафы ГИБДД вообще не должны применяться (например, персонаж без легального имени), это стоит явно исключить в проверке job'а нарушителя.
Штрафстоянка: куда уезжает тачка нарушителя
Штрафстоянка (impound) логически продолжает систему штрафов: если машину эвакуировали за нарушение (неправильная парковка, брошенное авто в розыске, конфискация при аресте), игрок должен либо оплатить штраф за эвакуацию и хранение, либо потерять машину.
Механика обычно такая:
- Полицейский или скрипт-триггер (например, при аресте владельца рядом с машиной) помечает vehicle-запись в базе как
state = 2(impounded) — это тот же принцип, что используется при обычном гараже. - На точке штрафстоянки висит NPC/зона, которая через
oxmysql-запрос ищет все машины игрока со статусом impound и показывает список с суммой к оплате. - Сумма обычно считается как
base_fee + (hours_since_impound * hourly_rate)— чем дольше не забираешь тачку, тем дороже. Это дополнительный денежный сток и стимул не забивать на нарушения.
exports.oxmysql:execute('SELECT * FROM owned_vehicles WHERE owner = ? AND state = 2', {citizenid}, function(vehicles)
-- отрисовка списка в NUI штрафстоянки с расчётом суммы
end)
Частая недоработка — забыть развязать штрафстоянку с гаражной системой. Если игрок забирает машину со штрафстоянки, но в скрипте гаража она всё ещё числится "на споуне" или "в другом гараже", получите дюп машины или зависшую запись в базе. Тестируйте связку эвакуация → штрафстоянка → возврат в гараж на тестовом персонаже перед тем, как выкатывать на прод.
Частые грабли при настройке штрафов и MDT
- Несовпадение identifier'ов. Часть MDT-ресурсов ждёт
citizenid(QBCore), другая —identifier(ESX, обычно steam-hex или license). Если ставите MDT не "из коробки" под свой фреймворк, а адаптируете чужой — первым делом проверьте, какое поле используется для связки игрока с записью в базе, иначе штрафы будут привязываться не к тем персонажам. - NUI не открывается у части полицейских. Обычно это права доступа (ACE permissions или проверка
job.gradeв клиентском коде) либо конфликт биндов клавиш с другим ресурсом — смотритеf8-консоль на клиенте на предмет ошибок NUI. - Штраф списывается, а событие в Discord не летит. Обычно webhook просто не проверяет код ответа — Discord может тихо ронять запросы при превышении rate limit (429). Добавьте проверку статуса ответа и повторную отправку с задержкой.
- Дублирование штрафов при лаге. Если клиент не получил подтверждение и жмёт кнопку повторно, а сервер не проверяет debounce — нарушителю могут выписать один и тот же штраф дважды. Решается серверным флагом "заявка в обработке" на время выполнения запроса.
- MySQL таймауты при большом онлайне. На нагруженных серверах INSERT в
mdt_finesможет попадать в очередь, если используете синхронныйMySQL.Syncвместо асинхронногоMySQL.insert/MySQL.Async.execute. Всегда используйте асинхронные методы oxmysql — синхронные вызовы блокируют серверный поток и на пике онлайна ощутимо аукаются лагами по всему серверу.
Поднять сервер FiveM (GTA V) за пару минут
Готовый образ MAATRIX GAMES: NVMe, AMD EPYC, DDoS-защита, панель управления. Локации UK, US, RU. Оплата картой РФ, СБП или криптой.
Создать серверЧастые вопросы
Какой MDT лучше ставить — платный или бесплатный?
Для старта хватает open-source решений под ESX/QBCore — они закрывают базовый функционал (профили, штрафы, ордера). Платные версии обычно дают более гибкий UI и готовую интеграцию со штрафстоянкой и судебной системой, но переносить кастомные статьи и права всё равно придётся руками под свой конфиг.
Можно ли выписывать штрафы без ареста игрока?
Да, это стандартный сценарий — MDT-штраф не требует физического задержания, если нарушение мелкое (ПДД). Арест актуален для тяжких статей, где предусмотрен tюремный срок в конфиге.
Что делать, если у игрока нет денег на счету?
Варианты: списание в минус (если в экономике сервера разрешён отрицательный баланс банка), рассрочка через штраф-долг с автоматическим списанием при следующем поступлении денег, либо конвертация в дополнительное время на штрафстоянке/аресте — выберите вариант, который не ломает баланс остальной экономики.
Нужно ли синхронизировать суммы штрафов между несколькими RP-серверами одного проекта (например, RU и EU-инстансами)?
Если экономики раздельные (разные базы данных, разный курс валюты) — не обязательно, суммы подбираются под каждый инстанс отдельно. Если база общая — да, иначе игроки будут выбирать сервер с более мягкими штрафами.
Как связать штрафы с судебной RP-системой (адвокаты, обжалование)?
Обычно это отдельный модуль поверх MDT: запись штрафа получает статус "оспаривается", судья/админ через отдельную панель может отменить или снизить сумму — эта функциональность не входит в базовые MDT-сборки и почти всегда дописывается кастомно под правила конкретного сервера.