Bloga Geri Dön
Tutorial9 dk. okuma

FiveM Kapı Kilidi Sistemi: Güvenli Kapılar ve Anahtarlar Scriptleyin

Herhangi bir FiveM sunucusuna kapı kilitleri ekleyin. Polis merkezleri, evler ve işletme iç mekanları için config, anahtarlar, yalnızca mesleklere özel kapılar ve en iyi kapı kilidi scriptleri.

Agency Scripts

Agency Scripts Kurucusu ve Baş Geliştiricisi

Roleplay'de Kapı Kilitleri Neden Önemlidir?

Kapı kilidi sistemleri, ciddi bir FiveM roleplay sunucusunun en çok hafife alınan ancak en temel bileşenlerinden biridir. Düzgün bir kapı yönetimi olmadan oyuncular polis cephaneliklerine, hastane personel odalarına ve çete sığınaklarına hiçbir kısıtlama olmaksızın girebilir ve rol ortamını tamamen bozabilir. İyi uygulanmış bir kapı kilidi sistemi, sunucu sahiplerinin haritadaki kamu binalarından oyuncu mülkiyetindeki işletmelere kadar her iç mekana (interior) erişimi kontrol etmesini sağlar. Anahtar teslimleri, maymuncuk senaryoları ve koordineli baskınlar gibi roleplay etkileşimlerini teşvik eden doğal sınırlar oluşturur. Bu rehberde, temel olarak ox_doorlock kullanarak eksiksiz bir kapı kilidi sistemi oluşturmayı ve ardından bunu özel maymuncuk mekanikleri, anahtar kartı kimlik doğrulaması, kapı grupları ve işletmelere özel erişim kontrolü ile genişletmeyi ele alacağız.

ox_doorlock Kurulumu

ox_doorlock resource'u, FiveM ekosisteminde en çok benimsenen kapı kilidi çözümüdür; temiz bir API, kapı yerleştirmek için yerleşik yetkili arayüzü sunar ve hem çift kapıları hem de garaj tipi animasyonlu kapıları destekler. Özel bir kod yazmadan önce sağlam bir temele ihtiyacınız vardır. UI bileşenleri ve yardımcı fonksiyonlar için bağımlı olduğu ox_lib ile birlikte ox_doorlock kurulumunu yapın. Sizin server.cfg kaynakların doğru sırada başlatılmasını sağlamalıdır: önce veritabanınız, ardından ox_lib ve son olarak ox_doorlock çalışmalıdır. Çalıştıktan sonra, yerleşik yönetici komutu oyun dünyasındaki herhangi bir kapıya yaklaşıp tek bir tıklamayla kaydetmenizi sağlar.

-- 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

Kapıları admin arayüzü üzerinden kaydettikten sonra ox_doorlock, her girdiyi koordinatlar, heading, model hash ve kilit durumuyla birlikte veritabanında saklar. Bunları programatik olarak sorgulayabilir ve değiştirebilirsiniz. Kaynak, kilitleri açıp kapatmak, durumu kontrol etmek ve erişimi yönetmek için hem export hem de event sunar. Bunun üzerine özel özellikler inşa etmeden önce veri yapısını anlamak kritik öneme sahiptir.

-- 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)

Maymuncuk Mekaniği Geliştirme

Maymuncukla kilit açma, kilitli kapıları illegal karakterler için anlamlı kılan bir oynanış katmanı ekler. Kapılar mutlak engeller olmak yerine yeteneğe dayalı mücadelelere dönüşür. En iyi yaklaşım, zamanlama ve hassasiyet gerektiren, sonucun rastgele değil hak edilmiş hissettirdiği bir mini oyundur. Oyuncunun envanterinde bir lockpick eşyası bulunmasını gerektiren, bir animasyon tetikleyen ve çok aşamalı bir skillcheck çalıştıran bir maymuncuk akışı oluşturmak için ox_lib'in yerleşik skillcheck sistemini kullanacağız. Skillcheck'te başarısız olmanın, maymuncuğun kırılması veya bir dispatch eventiyle yakındaki polislere bildirilmesi gibi sonuçları olmalıdır.

-- 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 tarafında maymuncukla kilit açma girişimini doğrulamanız, başarısızlık durumunda lockpick eşyasını silmeniz, başarı durumunda kapı kilidini açmanız ve isteğe bağlı olarak bir polis bildirimi göndermeniz gerekir. Spam girişimlerini önlemek için burada istek sınırlaması önemlidir. Başarısızlıktan hemen sonra tekrar deneyememeleri için kapı başına ve oyuncu başına bir bekleme süresi kaydedin. Server handler'ı ayrıca oyuncunun kilit açtığını iddia ettiği kapının gerçekten yakınında olduğunu doğrulamalı ve uzaktan kötüye kullanımı engellemelidir.

-- 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)

Kartlı Geçiş Sistemi Uygulama

Keycard'lar, basit anahtar eşyalarına kıyasla daha yapılandırılmış bir erişim kontrol mekanizması sağlar. Şirket binaları, devlet tesisleri ve erişimin belirli yetki düzeylerine bağlı olması gereken kısıtlı alanlar için çok uygundur. Keycard sistemi her kapıya 1 ile 5 arasında bir güvenlik seviyesi atar ve oyuncuların kilidi açmak için eşit veya daha yüksek yetkiye sahip bir keycard'a sahip olması gerekir. Bu, Seviye 3 bir keycard'ın tüm Seviye 1, 2 ve 3 kapılarını açtığı ancak Seviye 4 veya 5 kısıtlı alanlara erişemediği doğal bir hiyerarşi oluşturur. Keycard'lar meslek sistemleri tarafından verilebilir, ganimet olarak bulunabilir veya bir ilerleme sistemi üzerinden üretilebilir.

-- 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

Kapı Grupları ve İşletme Erişimi

Sunucunuzda yüzlerce kilitli kapı olduğunda kapıları tek tek yönetmek ölçeklenemez hale gelir. Kapı grupları, birden fazla kapıyı adlandırılmış bir gruba atamanıza ve tüm gruba erişimi tek seferde kontrol etmenize olanak tanıyarak bu sorunu çözer. Bir polis karakolunda polis mesleği tarafından erişilebilmesi gereken 15 kilitli kapı olabilir. Her kapıyı ayrı ayrı yapılandırmak yerine hepsini "police_station" grubuna atar ve meslek adına göre erişim yetkisi verirsiniz. Bir yönetici yeni bir memuru işe aldığında, herhangi bir manuel yapılandırma gerekmeksizin gruptaki her kapıya otomatik olarak erişim kazanır.

-- 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

İşletme kapıları, kapı erişimini mülkiyet kayıtlarına bağlayarak başka bir boyut kazandırır. Bir oyuncu bir işletme satın aldığında, o mülkle ilişkili tüm kapıların kontrolünü otomatik olarak elde etmelidir. Mülkler satılabileceği veya devredilebileceği için sistemin işletme mülkiyetini gerçek zamanlı olarak kontrol etmesi gerekir. Mevcut sahibi doğrulamak için işletme tablonuzu sorgulayan bir callback uygulayın, ardından her kapı etkileşiminde veritabanını aşırı yüklememek için sonucu kısa bir TTL ile önbelleğe alın.

Kapı Durumlarını İstemciler Arasında Senkronize Etme

Kapı senkronizasyonu, bir kapı kilidi sisteminin en karmaşık yönlerinden biridir. Bir oyuncu bir kapının kilidini açtığında, yakındaki her oyuncunun kapıyı açık görmesi gerekir. FiveM native kapı kontrolleri client başına çalışır, yani her client kapı durumlarını bağımsız olarak yönetir. Düzgün bir senkronizasyon olmadan, bir oyuncu kapıyı açık görürken bir diğeri kapalı görür. ox_doorlock temel durum senkronizasyonunu yönetir, ancak özel eklentilerin durum iletimi konusunda dikkatli olması gerekir. Kapı değişikliklerini makul bir mesafe içindeki tüm oyunculara iletmek için state bags veya server-authoritative event'ler kullanın. Her kapı açma kapama işleminde tüm sunucuya senkronizasyon yapmaktan kaçının, çünkü bu durum yüzlerce kapıya sahip büyük sunucularda gereksiz ağ yükü (network overhead) oluşturur.

-- 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)

Güvenlik Hususları ve Anti-Exploit

Kapı kilidi sistemleri hileciler için yaygın bir hedeftir, çünkü bir kapıyı atlamak genellikle kısıtlı eşyalara, silahlara veya paraya erişim sağlar. Her kapı açma kapama isteği server-side olarak doğrulanmalıdır. Oyuncunun yakınlığını ve erişim haklarını doğrulamadan client tarafından gönderilen kapı kimliklerine (door ID) asla güvenmeyin. Yöneticilerin şüpheli durumları inceleyebilmesi için her kapı durumu değişikliğini kaydedin (logging), örneğin bir oyuncunun erişimi olmaması gereken kasa kapılarının kilidini açması gibi. Maymuncuk sistemine yönelik kaba kuvvet (brute-force) girişimlerini önlemek için tüm kapı etkileşimlerine hız sınırı (rate limit) uygulayın. Denetim izleri oluşturmak için oyuncunun Steam ID'sini, kapı ID'sini, zaman damgasını ve erişim yöntemini kaydeden bir server-side event logger eklemeyi değerlendirin. Bu kayıtlar, hileleri araştırırken veya oyuncular arasında kimin neye ne zaman eriştiğine dair anlaşmazlıkları çözerken paha biçilemez hale gelir.

Hepsini Bir Araya Getirme

Canlı kullanıma hazır bir kapı kilidi sistemi, tüm bu bileşenleri birleşik bir altyapıda toplar. Giriş noktası, bir oyuncunun kayıtlı bir kapıya baktığını algılayan ve erişim düzeyine göre bağlamsal seçenekler sunan bir hedef sistemi etkileşimidir (ox_target veya qb-target). Meslek erişimleri varsa bir kilit açma düğmesi gösterin. Manyetik kartları varsa okutma seçeneği gösterin. Maymuncukları varsa ve kapı kilit açılabilir olarak işaretlenmişse maymuncuk seçeneğini gösterin. Her yol sunucuda doğrulama yapan ve sonucu yakındaki istemcilere senkronize eden uygun handler'a yönlendirilir. Yapılandırma, veritabanı kayıtları veya paylaşılan yapılandırma dosyaları aracılığıyla veriye dayalı olmalıdır; böylece sunucu sahipleri koda dokunmadan kapı erişimi ekleyebilir, kaldırabilir ve değiştirebilir. Bu modüler mimari, kilit açma minioyununu değiştirebileceğiniz, biyometrik tarayıcılar gibi yeni erişim yöntemleri ekleyebileceğiniz veya çekirdek mantığı yeniden yazmadan tamamen farklı envanter sistemleriyle entegre olabileceğiniz anlamına gelir.

Eşleşen script

Sunucunuz için hazır, güncellemeler dahil.

Başlamaya Hazır mısınız?

Scriptleri mağazamızdan edinin veya destek, güncellemeler ve gelecek yeniliklere göz atmak için Discord'a katılın.