Bloga Geri Dön
Tutorial8 dk. okuma

FiveM Garaj Sistemi: Araçları Saklayın, Çıkarın ve Çekin

Sağlam bir FiveM garaj sistemi tasarlayın. Kişisel ve meslek garajları, impound akışı, anahtarlar, envanter bağlantıları ve en iyi ücretsiz ile premium garaj scriptleri.

Agency Scripts

Agency Scripts Kurucusu ve Baş Geliştiricisi

Garaj Sistemi Mimarisini Anlamak

Bir garaj sistemi, oyuncuların araçlarını saklamalarının, çıkarmalarının ve yönetmelerinin birincil yolu olarak her FiveM roleplay sunucusundaki en temel özelliklerden biridir. Temelinde bir garaj sistemi birbirine bağlı üç katmandan oluşur: Araç sahipliğini ve durumunu kalıcı kılan bir veritabanı katmanı, doğrulama ile spawn ve despawn işlemlerini yöneten sunucu tarafı mantık katmanı ve oyuncuların kayıtlı araçlarıyla etkileşime girmesini sağlayan istemci tarafı UI katmanı. Kod yazmaya başlamadan önce, garajların konuma bağlı mı yoksa genel mi olacağı, oyuncuların herhangi bir garaja mı yoksa yalnızca belirli garajlara mı erişebileceği ve modifikasyonlar, yakıt seviyesi ile hasar durumu gibi araç özelliklerini nasıl ele almak istediğiniz gibi temel mimari kararları vermeniz gerekir. En iyi garaj sistemleri, bir oyuncu aracını geri aldığında özel boya işleri, performans yükseltmeleri ve hatta gövdedeki kir seviyesi de dahil olmak üzere tam olarak bıraktığı gibi gelmesi için eksiksiz araç özellikleri nesnesini saklar.

Veri Tabanı Şeması ve Araç Kalıcılığı

Veritabanı şemanız tüm garaj sisteminin temelini oluşturur. Araç sahipliğini, güncel durumunu ve kayıtlı özelliklerini takip eden bir tabloya ihtiyacınız vardır. State sütunu kritiktir, çünkü bir aracın o anda dünyada spawn edilmiş, garajda saklanmış veya impound otoparkında bekliyor olup olmadığını belirler. Temel alanları kapsayan pratik bir şema:

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 DEFAULT '{}',
    plate VARCHAR(8) NOT NULL,
    fakeplate VARCHAR(8) DEFAULT NULL,
    garage VARCHAR(50) DEFAULT 'pillboxgarage',
    fuel INT DEFAULT 100,
    engine FLOAT DEFAULT 1000.0,
    body FLOAT DEFAULT 1000.0,
    state INT DEFAULT 1,  -- 0 = out, 1 = garaged, 2 = impounded
    depotprice INT DEFAULT 0,
    drivingdistance INT DEFAULT 0,
    INDEX idx_citizenid (citizenid),
    INDEX idx_plate (plate),
    INDEX idx_state (state)
);

The mods sütunu, aşağıdaki gibi fonksiyonlar tarafından döndürülen ve tüm araç modifikasyonlarını içeren JSON formatında bir nesne saklar: QBCore.Functions.GetVehicleProperties(vehicle) veya ESX'teki eşdeğeri. Şunu indekslemek: citizenid, plate, ve state sütunları, oyuncu tabanınız binlere ulaşsa bile aramaların hızlı kalmasını sağlar. SQL injection saldırılarını önlemek için bu tabloyla etkileşim kurarken her zaman parametreli sorgular kullanın.

Sunucu Taraflı Spawn ve Despawn Mantığı

Sunucu tarafı, tüm kritik doğrulamaların yapıldığı yerdir. Bir oyuncu aracı garajdan çıkarmayı talep ettiğinde sunucu: oyuncunun gerçekten o araca sahip olduğunu, aracın şu anda garajda durumunda olduğunu ve geçerli bir spawn noktasının mevcut olduğunu doğrulamalıdır. Client'ın spawn konumunu doğrudan belirlemesine asla izin vermeyin, aksi takdirde hileciler haritanın herhangi bir yerinde araç spawn edebilir. Bunun yerine sunucuda spawn noktaları tanımlayın ve mevcut en yakın olanı seçin. İşte güvenli bir sunucu tarafı takeout handler örneği:

RegisterNetEvent('garage:server:takeVehicle', function(vehicleId, garageId)
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end

    local citizenid = Player.PlayerData.citizenid
    local result = MySQL.query.await(
        'SELECT * FROM player_vehicles WHERE id = ? AND citizenid = ? AND state = 1',
        {vehicleId, citizenid}
    )

    if not result or not result[1] then
        TriggerClientEvent('QBCore:Notify', src, 'Vehicle not found', 'error')
        return
    end

    local vehData = result[1]
    local spawnPoint = GetAvailableSpawnPoint(garageId)

    if not spawnPoint then
        TriggerClientEvent('QBCore:Notify', src, 'No parking spots available', 'error')
        return
    end

    MySQL.update('UPDATE player_vehicles SET state = 0 WHERE id = ?', {vehicleId})
    TriggerClientEvent('garage:client:spawnVehicle', src, vehData, spawnPoint)
end)

Despawn işlemi için sunucunun aracı dünyadan kaldırmadan önce mevcut araç özelliklerini yakalaması gerekir. Bu, son depolamadan bu yana yapılan modifikasyonların kaydedilmesini sağlar. Her şeyin doğru şekilde kalıcı olması için mod JSON'ının yanı sıra yakıt, motor sağlığı ve gövde sağlığını her zaman güncelleyin. Depolama işlemlerine izin vermeden önce oyuncunun gerçekten bir garaj konumunun yakınında olduğundan emin olmak için sunucu tarafında bir mesafe kontrolü uygulayın.

NUI ile Client Tarafı Garaj Arayüzü

Garaj UI, oyuncuların sistemle etkileşime girdiği yerdir ve iyi tasarlanmış bir arayüz, sinir bozucu bir deneyim ile sorunsuz bir deneyim arasındaki farkı belirler. Mevcut garajda kayıtlı tüm araçları gösteren duyarlı bir panel oluşturmak için HTML, CSS ve JavaScript ile NUI kullanın. Her araç girdisi: araç adını, plakasını, yakıt seviyesini ve genel durumunu bir bakışta göstermelidir. Oyuncuların ne seçtiklerini görebilmeleri için araç modelini geçici olarak spawn eden bir önizleme sistemi ekleyin, bu özellik bir oyuncu aynı türden birden fazla araca sahip olduğunda özellikle kullanışlıdır. Garaj menüsünü açmak ve araç verilerini toplamak için istemci tarafı mantığı şöyledir:

RegisterNetEvent('garage:client:openMenu', function(garageId)
    QBCore.Functions.TriggerCallback('garage:server:getVehicles', function(vehicles)
        if not vehicles or #vehicles == 0 then
            QBCore.Functions.Notify('No vehicles stored here', 'info')
            return
        end

        SetNuiFocus(true, true)
        SendNUIMessage({
            action = 'openGarage',
            vehicles = vehicles,
            garageName = Config.Garages[garageId].label
        })
    end, garageId)
end)

RegisterNUICallback('takeVehicle', function(data, cb)
    SetNuiFocus(false, false)
    TriggerServerEvent('garage:server:takeVehicle', data.vehicleId, currentGarage)
    cb('ok')
end)

JavaScript tarafında, her aracı dışarı çıkarma veya başka bir garaja transfer etme işlem butonları içeren bir kart olarak render edin. Geniş koleksiyonlara sahip oyuncuların ihtiyaç duydukları aracı hızla bulabilmeleri için sıralama ve filtreleme seçenekleri eklemeyi değerlendirin. Plaka numarasına veya araç adına göre filtreleme yapan bir arama çubuğu, oyuncuların zamanla çok sayıda araç biriktirdiği sunucularda kullanılabilirliği ciddi ölçüde artıran küçük bir eklentidir.

Araç Özelliklerini Kaydetme ve Geri Yükleme

Araç özelliklerini düzgün bir şekilde kaydetmek ve geri yüklemek, garaj sistemi geliştirmenin en zor kısımlarından biridir. Properties nesnesi renkler, kaplamalar, neon ışıkları, cam filmleri, lastik dumanı rengi, ekstralar ve tüm performans modifikasyonları dahil onlarca alan içerir. Bir aracı garaja koyarken en güncel durumu aldığınızdan emin olmak için entity'yi silmeden hemen önce özellikleri yakalayın. Bir aracı tekrar çıkarırken (spawn ederken) özellikleri uygulamadan önce entity'nin tamamen yüklenmesini beklemeniz gerekir, aksi takdirde özel jantlar veya motor yükseltmeleri gibi modifikasyonlar sessizce başarısız olur. Kısa bir gecikme veya düzgün bir entity varlık kontrolü döngüsü kullanın:

function SpawnAndApplyMods(vehData, spawnPoint)
    local model = GetHashKey(vehData.vehicle)
    RequestModel(model)

    while not HasModelLoaded(model) do
        Wait(10)
    end

    local veh = CreateVehicle(model, spawnPoint.x, spawnPoint.y, spawnPoint.z,
        spawnPoint.w, true, false)

    while not DoesEntityExist(veh) do
        Wait(10)
    end

    local props = json.decode(vehData.mods)
    if props then
        QBCore.Functions.SetVehicleProperties(veh, props)
    end

    SetVehicleFuelLevel(veh, vehData.fuel + 0.0)
    SetVehicleEngineHealth(veh, vehData.engine + 0.0)
    SetVehicleBodyHealth(veh, vehData.body + 0.0)
    SetEntityAsMissionEntity(veh, true, true)
    SetModelAsNoLongerNeeded(model)
    TaskWarpPedIntoVehicle(PlayerPedId(), veh, -1)
end

Addon araçlara özellikle dikkat edin, çünkü bazen vanilla GTA araçlarından farklı davranan özel ekstra parçalara veya kaplama (livery) dizinlerine sahiptirler. Uç durumları erkenden yakalamak için özellik kaydetme ve geri yükleme döngünüzü çeşitli araç türleriyle kapsamlı bir şekilde test edin.

Çekilmiş Araç Sistemi Entegrasyonu

Bir araç çekme (impound) sistemi, garajınızla el ele çalışır ve rol yapma sunucularının talep ettiği gerçekçilik katmanını ekler. Araçlar birkaç nedenden dolayı otoparka çekilir: tutuklama sırasında polis tarafından el konulması, sunucu yeniden başlatılmasının ardından terk edilmiş araçların otomatik olarak temizlenmesi veya kural ihlalleri nedeniyle yönetici müdahalesi. Bir araç bağlandığında, veritabanındaki durumunu 2 olarak güncelleyin ve isteğe bağlı olarak oyuncunun aracı geri almak için ödemesi gereken bir çekilmiş araç bedeli belirleyin. Çekilmiş araç alanı bir garaja benzer şekilde çalışmalı, ancak teslimden önce ödeme şartı eklenmelidir. Haritada kendi spawn noktalarına ve çekim ücretini belirgin şekilde gösteren NUI arayüzüne sahip ayrı bir çekilmiş araç konumu oluşturun.

RegisterNetEvent('police:server:impoundVehicle', function(plate, price)
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end

    -- Verify the player has police job authorization
    if Player.PlayerData.job.name ~= 'police' then return end

    local result = MySQL.update.await(
        'UPDATE player_vehicles SET state = 2, depotprice = ? WHERE plate = ?',
        {price or 500, plate}
    )

    if result > 0 then
        TriggerClientEvent('QBCore:Notify', src, 'Vehicle impounded', 'success')
    end
end)

Aynı araç her çekildiğinde impound ücretinin arttığı kademeli bir fiyatlandırma sistemi uygulamayı değerlendirin, bu oyuncuların çekilmiş araç otoparkını ücretsiz otopark gibi görmesini engeller. Ayrıca, yapılandırılabilir sayıda gerçek dünya gününden daha uzun süre impound'da kalan araçların hiçbir ücret ödemeden otomatik olarak garaja geri bırakıldığı zamana dayalı bir mekanik de ekleyebilir, böylece oyuncuları hayal kırıklığına uğratan kalıcı kayıp senaryolarının önüne geçebilirsiniz.

Garaj Blip'leri ve Target Entegrasyonu

Garajların keşfedilebilir ve etkileşimi kolay olmasını sağlamak, uygun blip yerleşimi ve etkileşim alanları gerektirir. Oyuncuların minimap üzerinde bulabilmeleri için her garaj konumu adına harita blip'leri ekleyin ve etkileşim tetikleyicisi için mesafe tabanlı marker'lar ya da target sistemi entegrasyonu kullanın. ox_target veya qb-target gibi target sistemleri, yalnızca oyuncu belirli bir noktaya nişan aldığında etkileşim seçeneklerini göstererek ekran kalabalığını azalttığı için daha temiz bir deneyim sunar. Koordinatları, spawn noktalarını ve ayarları senkronize tutarak hem client hem de server'ın başvurabileceği paylaşılan bir yapılandırma dosyasında garaj konumlarınızı tanımlayın:

Config.Garages = {
    ['pillboxgarage'] = {
        label = 'Pillbox Garage',
        coords = vector3(215.83, -810.18, 30.73),
        spawnPoints = {
            vector4(218.32, -803.28, 30.73, 248.5),
            vector4(222.41, -799.84, 30.73, 248.5),
            vector4(226.52, -796.41, 30.73, 248.5),
        },
        blip = { sprite = 357, color = 3, scale = 0.7 },
        vehicleType = 'car',  -- car, boat, aircraft
    },
}

Su kenarında veya havalimanlarında uygun doğma noktalarıyla tekneler ve hava araçları için ayrı garajlar oluşturarak birden fazla araç türünü destekleyin. vehicleType filtresi, oyuncuların sokak garajında yalnızca kara araçlarını, marinada ise yalnızca tekneleri görmesini sağlayarak karışıklığı ve spawn sorunlarını önler. Bir oyuncu bir garaja yaklaştığında, o konumda aracı olmayan oyuncuların gereksiz menü açmasını önlemek için etkileşim istemini göstermeden önce orada depolanmış aracı olup olmadığını kontrol edin.

Performans Optimizasyonu İpuçları

Garaj sistemleri dikkatli uygulanmadığında, özellikle her biri birden fazla araca sahip yüzlerce eşzamanlı oyuncunun bulunduğu sunucularda bir performans darboğazı haline gelebilir. Bir oyuncu garaj menüsünü her açtığında veritabanını sorgulamak yerine araç listelerini sunucu tarafında önbelleğe alın ve önbelleği yalnızca bir araç durumu değiştiğinde geçersiz kılın. İstemci tarafında, JavaScript zamanlayıcıları veya animasyonlar çalıştırıyorlarsa gizli NUI çerçeveleri bile kaynak tükettiğinden, ihtiyaç duyulmadığında NUI çerçevelerini açık tutmaktan kaçının. Araçları spawn ederken, oyuncu onları depoladıktan sonra artık gerekli değil olarak ayarlayarak entity'leri düzgün şekilde temizlediğinizden emin olun ve çökmeler veya bağlantı kopmaları nedeniyle düzgün bir şekilde despawn edilmemiş sahipsiz araç entity'lerini yakalamak için düzenli aralıklarla çalışan bir yedek temizleme rutini uygulayın. Şu native fonksiyonları kullanın: GetGamePool('CVehicle') idareli kullanın ve dünyadaki mevcut oyuncu araçlarını kontrol etmeniz gerektiğinde sonuçları önbelleğe alın. Son olarak, veritabanı sorgularını sınırlı tutmak ve tek bir oyuncunun geri alma işlemlerini yavaşlatabilecek yüzlerce aracı depolamasını önlemek için garaj başına maksimum araç sınırı uygulamayı düşünün.

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.