FiveM: гараж и система штрафстоянки
Игрок купил тачку, залил стволы бампером о столб, вышел из игры — а при заходе машина стоит целая и с полным баком, как будто ничего не было. Или того хуже: припарковался криво у входа в магазин, машину эвакуировали, а забрать её неоткуда, потому что штрафстоянки на сервере просто нет. Обе ситуации убивают ощущение RP-мира быстрее, чем баг с текстурами. В этой статье разберём, как поднять связку гараж + штрафстоянка с реальным сохранением состояния транспорта и логикой эвакуации, которая работает, а не просто красиво выглядит в демо-видео мода.
Содержание
Почему стандартного спавна тачки недостаточно
В большинстве стартовых фреймворков (голый ESX или QBCore без доп. ресурсов) машина просто спавнится заново при команде /car или через NPC-дилера — без памяти о повреждениях, топливе и апгрейдах. Для RP это плохо по трём причинам.
Во-первых, пропадает цена ошибки: разбил тачку — через пять минут выехал на такой же целой. Во-вторых, ломается экономика вокруг автосервисов и топлива — если статьи про систему топлива и автосервисов не привязаны к реальному состоянию машины, они бессмысленны. В-третьих, нет наказания за неправильную парковку — игроки бросают тачки где попало, сервер захламляется entity, FPS в людных зонах проседает.
Рабочая система гаража решает всё это: машина хранится в базе с полем состояния (fuel, engine health, body health, mods), выезжает из гаража ровно в том виде, в каком её туда поставили, и пропадает из мира, когда не используется — это разгружает клиента и синхронизацию.
Выбор ресурса для гаража
На 2026 год для ESX и QBCore есть готовые ресурсы, которые закрывают 90% задачи без написания своего скрипта с нуля:
- qb-garages (для QBCore) — гараж по классам (машина/мото/лодка/самолёт), поддержка нескольких гаражей на карте, депо для эвакуированных.
- esx_advancedgarage или cd_garage (для ESX) — похожая логика, отдельные точки гаража с привязкой к job/gang для RP-фракций.
- wasabi_carlock или встроенный lock-скрипт фреймворка — нужен рядом, чтобы машина закрывалась и не угонялась пока стоит на улице.
Если у вас кастомный фреймворк или сильно переделанный ESX, проще форкнуть один из этих ресурсов и подогнать под свои таблицы БД, чем писать с нуля — там уже решены edge-кейсы вроде дублирования плейта или спавна машины внутри объекта.
Установка стандартная: скачиваете ресурс, кладёте в resources/[qb]/qb-garages (или [esx]/esx_advancedgarage), прописываете в server.cfg:
ensure qb-garages
и проверяете зависимости — обычно это oxmysql (или mysql-async для старых сборок), qb-core/es_extended, иногда PolyZone или ox_lib для UI зон.
Поднять сервер FiveM (GTA V) за пару минут
Готовый образ MAATRIX GAMES: NVMe, AMD EPYC, DDoS-защита, панель управления. Локации UK, US, RU. Оплата картой РФ, СБП или криптой.
Создать серверСохранение состояния транспорта в базе
Ключевая таблица — player_vehicles (в QBCore) или owned_vehicles (в ESX). Минимальная структура для гаража с состоянием:
CREATE TABLE IF NOT EXISTS `player_vehicles` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`citizenid` VARCHAR(50) NOT NULL,
`vehicle` VARCHAR(50) NOT NULL,
`hash` VARCHAR(50) NOT NULL,
`mods` LONGTEXT,
`plate` VARCHAR(15) NOT NULL,
`fakeplate` VARCHAR(15) DEFAULT NULL,
`garage` VARCHAR(50) DEFAULT 'pillboxgarage',
`fuel` INT DEFAULT 100,
`engine` FLOAT DEFAULT 1000,
`body` FLOAT DEFAULT 1000,
`state` INT DEFAULT 1,
`depotprice` INT DEFAULT 0,
`drivingdistance` INT DEFAULT NULL,
`status` LONGTEXT DEFAULT NULL
);
Поле state — это как раз статус машины: 0 (в гараже/despawned), 1 (снаружи, заспавнена), 2 (на штрафстоянке). Поля fuel, engine, body сохраняются при каждом заезде в гараж (событие vehicleSaved или аналог) и подтягиваются обратно при выезде через SetVehicleFuelLevel, SetVehicleEngineHealth, SetVehicleBodyHealth.
Частая грабля: если сохранение вешать только на клик "поставить в гараж", а не на периодический автосейв, при крашах клиента или дисконнекте без выхода из машины состояние теряется и откатывается к последнему ручному сохранению. Добавьте таймер автосейва раз в 2-5 минут для активных машин игроков — это недорого по нагрузке на БД (один UPDATE по plate), зато спасает от жалоб "машина скинулась в прошлое состояние".
Логика эвакуации: где и когда забирают машину
Эвакуация должна быть событийной, а не по таймеру "просто так убрать лишние entity". Рабочие триггеры:
- Неправильная парковка — зона (через
PolyZoneилиox_libpoints) у входов в важные локации (банк, магазины, спавн-точки), где машина, оставленная дольше N минут без игрока рядом, помечается на эвакуацию. - Штраф от полиции/госслужбы — job-скрипт для полицейских RP-фракций с командой
/impound [plate], переводящей машину вstate = 2и записывающейdepotprice. - Долг по штрафам ГИБДД — если у игрока накопились неоплаченные штрафы, машина при следующей встрече с патрулём или на чекпоинте эвакуируется автоматически. Логика штрафов и их накопления разобрана отдельно в статье про систему штрафов ГИБДД для RP-сервера — штрафстоянка логично работает как продолжение этой системы, а не отдельный модуль.
Пример серверной функции эвакуации на QBCore (упрощённо, под адаптацию под вашу структуру):
RegisterNetEvent('garage:impoundVehicle', function(plate, reason)
local src = source
local result = MySQL.query.await('SELECT * FROM player_vehicles WHERE plate = ?', {plate})
if result[1] then
MySQL.update('UPDATE player_vehicles SET state = ?, garage = ?, depotprice = ? WHERE plate = ?',
{2, 'impound', Config.ImpoundBasePrice, plate})
TriggerClientEvent('QBCore:Notify', src, 'Транспорт эвакуирован на штрафстоянку: ' .. reason, 'error')
end
end)
Не забудьте вызывать DeleteEntity/DeleteVehicle на клиенте у самой физической модели после записи в базу — иначе машина продолжит валяться в мире как призрак, доступный для взаимодействия, хотя формально уже "на штрафстоянке".
Штрафстоянка: точка выдачи и расчёт цены возврата
Штрафстоянка — это отдельный гараж с фильтром state = 2, куда игрок приходит и платит за возврат, прежде чем машина снова появится в мире. Логика цены обычно завязана на два фактора: базовая ставка за эвакуацию плюс дни простоя.
Config.ImpoundBasePrice = 500
Config.ImpoundDailyFee = 100
function CalculateImpoundPrice(impoundedAt)
local daysHeld = math.floor((os.time() - impoundedAt) / 86400)
return Config.ImpoundBasePrice + (daysHeld * Config.ImpoundDailyFee)
end
При выдаче деньги списываются через Player.Functions.RemoveMoney('cash', price) или банковский перевод, если у вас настроена банковская система с кредитами — логично дать игроку выбор оплаты наличкой или переводом с карты, это ощущается органичнее, чем жёсткая привязка к одному способу.
Таблица для понимания разных сценариев ценообразования, которые встречаются на реальных RP-серверах:
| Сценарий эвакуации | Базовая ставка | Доп. плата | Кому уходит доход |
|---|---|---|---|
| Неправильная парковка (авто-система) | 300-500 | нет | в бюджет сервера/казну города |
| Эвакуация полицией (нарушение) | 500-800 | +100/день простоя | в казну фракции полиции |
| Конфискация за долг по штрафам | 800-1500 | штраф не гасится автоматически | в казну сервера |
Цифры ориентировочные — баланс сильно зависит от экономики конкретного сервера (стартовый капитал, средний доход по джобам), тестируйте на своих значениях, не копируйте чужие один в один.
Гараж с несколькими точками и привязкой к локации
Если карта большая или сервер использует кастомные MLO-карты, логично сделать несколько гаражей вместо одного центрального — иначе игроки со всей карты тратят по 5-10 минут только на дорогу до машины. В qb-garages это настраивается через таблицу локаций в конфиге:
Config.Garages = {
["pillboxgarage"] = {
label = "Pillbox Garage",
vehicleTypes = { "car", "byke" },
takeVehicle = { x = -212.51, y = -1330.68, z = 31.29 },
spawnDefault = { x = -215.5, y = -1301.5, z = 31.4, h = 130.0 },
},
["sandygarage"] = {
label = "Sandy Shores Garage",
vehicleTypes = { "car", "air" },
takeVehicle = { x = 1731.0, y = 3672.0, z = 34.9 },
spawnDefault = { x = 1735.0, y = 3680.0, z = 34.9, h = 45.0 },
},
}
Машина, поставленная в один гараж, при желании может быть недоступна из другого (реалистичный вариант) — либо доступна из любого гаража того же типа (аркадный, но удобнее для игроков). Это решение стоит принять до релиза сервера: смена модели после того, как игроки привыкли, вызывает много недовольства.
Поднять сервер FiveM (GTA V) за пару минут
Готовый образ MAATRIX GAMES: NVMe, AMD EPYC, DDoS-защита, панель управления. Локации UK, US, RU. Оплата картой РФ, СБП или криптой.
Создать серверЧастые вопросы
Машина пропадает из гаража после рестарта сервера — почему?
Обычно проблема в том, что состояние сохраняется только в памяти ресурса (переменная), а не в БД сразу. Проверьте, что событие сохранения реально делает UPDATE/INSERT в таблицу, а не просто обновляет локальный кэш скрипта.
Можно ли сделать штрафстоянку без job для полиции, если RP-фракций пока нет?
Да, эвакуация может идти полностью через авто-систему по зонам неправильной парковки — job полиции нужен только для ручной эвакуации патрулём, это отдельная опциональная ветка.
Как не дать игрокам эвакуировать чужие машины ради троллинга?
Ограничьте команду эвакуации по ACE-правам (add_ace group.police impound.vehicle allow) или по job в самом ресурсе — обычный игрок не должен иметь доступа к событию garage:impoundVehicle вообще, проверяйте это и на сервере, а не только скрывайте кнопку в UI.
Что делать, если машина застряла в состоянии "эвакуирована", но на штрафстоянке её нет?
Это рассинхрон БД и мира — обычно из-за краша скрипта между записью state = 2 и удалением сущности. Добавьте админ-команду для ручного сброса state по plate через консоль или chat-команду с проверкой прав.
Нужен ли отдельный ресурс для эвакуатора (анимация подъёма машины) или можно просто телепортировать в депо?
Для атмосферы приятно, но не обязательно на старте — можно запустить с мгновенным перемещением в state = 2 и добавить визуальный эвакуатор позже, когда основная логика обкатана и стабильна.