FiveM Система запирания дверей: скрипт безопасных дверей и ключей
Добавьте дверные замки на любой сервер FiveM. Конфигурация, ключи, двери только для определённых профессий и лучшие скрипты дверных замков для полицейских участков, домов и бизнес-интерьеров.
Agency Scripts
Основатель и ведущий разработчик Agency Scripts
Почему замки на дверях важны в ролевой игре
Системы запирания дверей , одна из самых недооцененных, но при этом ключевых составляющих любого серьезного FiveM ролевого сервера. Без правильного управления дверями игроки могут свободно заходить в полицейские арсеналы, больничные помещения и логова банд, что полностью разрушает погружение. Хорошо реализованная система запирания дверей позволяет владельцам серверов контролировать доступ ко всем интерьерам на карте, от государственных зданий до бизнесов игроков. Она создает естественные границы, стимулирующие ролевые взаимодействия, такие как обмен ключами, сценарии взлома и скоординированные проникновения. В этом руководстве мы пройдемся по созданию полной системы запирания дверей на базе ox_doorlock, а затем расширим ее кастомными механиками взлома, аутентификацией по ключ-картам, группами дверей и контролем доступа для бизнеса.
Настройка ox_doorlock
Ресурс ox_doorlock , самое широко используемое решение для дверных замков в экосистеме FiveM, предлагающее чистый API, встроенный админский UI для размещения дверей и поддержку как двойных дверей, так и анимированных дверей гаражного типа. Перед написанием кастомного кода нужна надежная база. Установите ox_doorlock вместе с ox_lib, от которого он зависит для UI-компонентов и утилит. Ваш server.cfg требуется обеспечить запуск ресурсов в правильном порядке: сначала ваша база данных, затем ox_lib и, наконец, ox_doorlock. После запуска встроенная команда администратора позволяет подойти к любой двери в игровом мире и зарегистрировать её одним кликом.
-- server.cfg load order
ensure oxmysql
ensure ox_lib
ensure ox_doorlock
-- Grant admin access for door placement
add_ace group.admin command.doorlock allow
После регистрации дверей через админ-интерфейс ox_doorlock сохраняет каждую запись в базе данных с координатами, направлением, хэшем модели и состоянием замка. Вы можете программно запрашивать и управлять ими. Ресурс предоставляет как экспорты, так и события для переключения замков, проверки состояния и управления доступом. Понимание структуры данных критично перед созданием пользовательских функций на её основе.
-- Checking if a specific door is locked
local doorId = 1
local isLocked = exports.ox_doorlock:getDoorState(doorId)
-- Toggle a door lock from server side
exports.ox_doorlock:setDoorState(doorId, not isLocked)
-- Listen for door state changes
AddEventHandler('ox_doorlock:stateChanged', function(id, state, source)
print(('Door %d changed to %s by player %s'):format(id, state and 'locked' or 'unlocked', source))
end)
Создание механики взлома замков
Взлом замков добавляет игровой слой, который делает запертые двери значимыми для криминальных персонажей. Вместо того чтобы двери были абсолютными преградами, они становятся задачами, требующими навыков. Лучший подход , мини-игра, требующая точного времени и аккуратности, благодаря чему результат ощущается заслуженным, а не случайным. Мы используем встроенную систему skillcheck из ox_lib для создания процесса взлома замков, который требует наличия предмета lockpick в инвентаре игрока, запускает анимацию и выполняет многоступенчатую проверку навыков. Провал skillcheck должен иметь последствия, например, ломать lockpick или оповещать ближайшую полицию через событие dispatch.
-- client/lockpicking.lua
local function AttemptLockpick(doorId)
local hasLockpick = exports.ox_inventory:Search('count', 'lockpick')
if hasLockpick < 1 then
lib.notify({ title = 'No Lockpick', type = 'error' })
return
end
-- Play lockpicking animation
local ped = PlayerPedId()
TaskPlayAnim(ped, 'anim@amb@clubhouse@tutorial@bkr_tut_ig3@', 'machinic_loop_mechandlenry', 8.0, -8.0, -1, 1, 0, false, false, false)
-- Multi-stage skillcheck: easy, medium, hard
local success = lib.skillCheck({'easy', 'medium', 'hard'}, {'w', 'a', 's', 'd'})
ClearPedTasks(ped)
if success then
TriggerServerEvent('doorlock:lockpick:success', doorId)
lib.notify({ title = 'Lock Picked', description = 'The door clicks open.', type = 'success' })
else
TriggerServerEvent('doorlock:lockpick:fail', doorId)
lib.notify({ title = 'Failed', description = 'The lockpick snapped.', type = 'error' })
end
end
На стороне сервера необходимо проверить попытку взлома замка, удалить отмычку при неудаче, открыть дверь при успехе и при необходимости отправить сигнал полиции. Важно ограничивать частоту попыток, чтобы предотвратить спам. Храните таймаут для каждого игрока и каждой двери, чтобы они не могли сразу повторить попытку после неудачи. Обработчик сервера также должен проверять, что игрок действительно находится рядом с дверью, которую пытается вскрыть, предотвращая удалённую эксплуатацию.
-- server/lockpicking.lua
local cooldowns = {}
RegisterNetEvent('doorlock:lockpick:success', function(doorId)
local src = source
local playerCoords = GetEntityCoords(GetPlayerPed(src))
-- Verify proximity to door (anti-cheat)
local doorData = exports.ox_doorlock:getDoor(doorId)
if not doorData then return end
if #(playerCoords - doorData.coords) > 3.0 then return end
-- Check cooldown
local key = ('%s:%s'):format(src, doorId)
if cooldowns[key] and os.time() - cooldowns[key] < 30 then return end
exports.ox_doorlock:setDoorState(doorId, 0) -- Unlock
cooldowns[key] = os.time()
-- Auto-relock after 60 seconds
SetTimeout(60000, function()
exports.ox_doorlock:setDoorState(doorId, 1)
end)
end)
RegisterNetEvent('doorlock:lockpick:fail', function(doorId)
local src = source
exports.ox_inventory:RemoveItem(src, 'lockpick', 1)
-- Alert police dispatch
TriggerEvent('dispatch:alert', {
coords = GetEntityCoords(GetPlayerPed(src)),
message = 'Attempted break-in reported',
code = '10-31',
job = 'police'
})
end)
Реализация системы ключ-карт
Ключ-карты обеспечивают более структурированный механизм контроля доступа по сравнению с простыми ключевыми предметами. Они хорошо подходят для корпоративных зданий, правительственных объектов и зон с ограниченным доступом, где доступ должен быть привязан к определенным уровням допуска. Система ключ-карт присваивает каждой двери уровень безопасности от 1 до 5, и игроки должны иметь ключ-карту с равным или более высоким уровнем допуска для открытия. Это создает естественную иерархию, где ключ-карта уровня 3 открывает все двери уровней 1, 2 и 3, но не дает доступа к зонам уровней 4 и 5. Ключ-карты могут выдаваться через системы профессий, находиться в луте или изготавливаться через систему прогрессии.
-- shared/config.lua
Config = {}
Config.KeycardLevels = {
{ name = 'keycard_1', label = 'Green Keycard', level = 1 },
{ name = 'keycard_2', label = 'Blue Keycard', level = 2 },
{ name = 'keycard_3', label = 'Yellow Keycard', level = 3 },
{ name = 'keycard_4', label = 'Red Keycard', level = 4 },
{ name = 'keycard_5', label = 'Black Keycard', level = 5 },
}
Config.DoorSecurity = {
[10] = { level = 1, name = 'Office Lobby' },
[11] = { level = 2, name = 'Server Room' },
[12] = { level = 3, name = 'Executive Floor' },
[13] = { level = 4, name = 'Vault Anteroom' },
[14] = { level = 5, name = 'Main Vault' },
}
-- client/keycard.lua
local function TryKeycardAccess(doorId)
local security = Config.DoorSecurity[doorId]
if not security then return false end
for _, card in ipairs(Config.KeycardLevels) do
if card.level >= security.level then
local count = exports.ox_inventory:Search('count', card.name)
if count > 0 then
-- Play card swipe animation
lib.requestAnimDict('anim@heists@keycard@')
TaskPlayAnim(PlayerPedId(), 'anim@heists@keycard@', 'exit', 5.0, 1.0, -1, 16, 0, 0, 0, 0)
Wait(1200)
ClearPedTasks(PlayerPedId())
return true, card
end
end
end
return false
end
Группы дверей и доступ для бизнеса
Управление отдельными дверями становится не масштабируемым, когда на вашем сервере сотни запертых дверей. Группы дверей решают эту проблему, позволяя назначать несколько дверей в именованную группу и контролировать доступ ко всей группе сразу. В полицейском участке может быть 15 запертых дверей, к которым должен иметь доступ весь полицейский персонал. Вместо настройки каждой двери по отдельности, вы назначаете их все в группу "police_station" и предоставляете доступ на основе названия работы. Когда администратор нанимает нового офицера, он автоматически получает доступ ко всем дверям группы без ручной настройки.
-- server/door_groups.lua
local DoorGroups = {
police_station = {
doors = {1, 2, 3, 4, 5, 6, 7, 8},
access = {
{ type = 'job', name = 'police', minGrade = 0 },
{ type = 'job', name = 'sheriff', minGrade = 0 },
}
},
pillbox_hospital = {
doors = {20, 21, 22, 23, 24},
access = {
{ type = 'job', name = 'ambulance', minGrade = 0 },
{ type = 'job', name = 'doctor', minGrade = 2 },
}
},
vangelico = {
doors = {30, 31},
access = {
{ type = 'job', name = 'jeweler', minGrade = 0 },
{ type = 'item', name = 'vangelico_key' },
}
},
}
function HasGroupAccess(source, groupName)
local group = DoorGroups[groupName]
if not group then return false end
for _, rule in ipairs(group.access) do
if rule.type == 'job' then
local job = GetPlayerJob(source)
if job and job.name == rule.name and job.grade >= (rule.minGrade or 0) then
return true
end
elseif rule.type == 'item' then
local count = exports.ox_inventory:GetItem(source, rule.name, nil, true)
if count and count > 0 then return true end
end
end
return false
end
Двери бизнеса добавляют новое измерение, связывая доступ к дверям с записями о владении. Когда игрок покупает бизнес, он автоматически получает контроль над всеми дверями, связанными с этим объектом. Система должна проверять владение бизнесом в реальном времени, так как объекты могут продаваться или передаваться. Реализуйте callback, который запрашивает вашу таблицу бизнеса для проверки текущего владельца, затем кешируйте результат с коротким TTL, чтобы избежать чрезмерных запросов к базе данных при каждом взаимодействии с дверью.
Синхронизация состояния дверей между клиентами
Синхронизация дверей , одна из самых сложных частей системы запирания дверей. Когда один игрок открывает дверь, все близлежащие игроки должны видеть ее открытой. Нативные контролы дверей FiveM работают на стороне каждого клиента отдельно, то есть каждый клиент самостоятельно управляет состоянием дверей. Без правильной синхронизации один игрок видит дверь открытой, а другой , закрытой. ox_doorlock обрабатывает базовую синхронизацию состояний, но кастомные расширения должны аккуратно распространять изменения. Используйте state bags или серверные авторитетные события для трансляции изменений дверей всем игрокам в разумном радиусе. Избегайте синхронизации на весь сервер при каждом переключении двери, так как это создает ненужную нагрузку на сеть на больших серверах с сотнями дверей.
-- server: broadcast door state to nearby players only
local function SyncDoorToNearby(doorId, state, coords, range)
range = range or 100.0
local players = GetPlayers()
for _, playerId in ipairs(players) do
local ped = GetPlayerPed(playerId)
if ped and DoesEntityExist(ped) then
local playerCoords = GetEntityCoords(ped)
if #(playerCoords - coords) <= range then
TriggerClientEvent('doorlock:sync', tonumber(playerId), doorId, state)
end
end
end
end
-- client: apply synced door state
RegisterNetEvent('doorlock:sync', function(doorId, state)
local door = exports.ox_doorlock:getDoor(doorId)
if door then
door.state = state
end
end)
Вопросы безопасности и анти-эксплойт
Системы запирания дверей часто становятся целью эксплойтеров, так как обход двери часто дает доступ к ограниченным предметам, оружию или деньгам. Каждый запрос на переключение двери должен проверяться на стороне сервера. Никогда не доверяйте ID дверей, отправленным клиентом, без проверки близости игрока и прав доступа. Реализуйте логирование каждого изменения состояния двери, чтобы администраторы могли анализировать подозрительные действия, например, когда игрок открывает двери хранилищ, к которым у него не должно быть доступа. Ограничьте частоту всех взаимодействий с дверями, чтобы предотвратить перебор попыток взлома. Рассмотрите возможность добавления серверного логгера событий, который записывает Steam ID игрока, ID двери, временную метку и способ доступа для аудита. Эти логи становятся незаменимыми при расследовании эксплойтов или разрешении споров между игроками о том, кто и когда получил доступ к чему.
Объединение всего вместе
Готовая к продакшену система замков дверей объединяет все компоненты в единую структуру. Точка входа , взаимодействие с системой target, будь то ox_target или qb-target, которая определяет, когда игрок смотрит на зарегистрированную дверь, и показывает контекстные опции в зависимости от уровня доступа. Если у игрока есть доступ по работе, показывается кнопка разблокировки. Если есть ключ-карта , опция свайпа. Если есть отмычка и дверь помечена как взламываемая , опция отмычки. Каждый путь ведет к соответствующему обработчику, который проверяет на сервере и синхронизирует результат с ближайшими клиентами. Конфигурация должна быть основана на данных через записи в базе или общие конфиги, чтобы владельцы серверов могли добавлять, удалять и изменять доступ к дверям без правок кода. Такая модульная архитектура позволяет заменить мини-игру взлома, добавить новые методы доступа, например биометрические сканеры, или интегрироваться с другими системами инвентаря без переписывания основной логики.