ब्लॉग पर वापस
Tutorial10 मिनट पढ़ें

FiveM गैराज प्रणाली: वाहन स्टोर, स्पॉन और जब्त करें

एक मजबूत FiveM गैराज प्रणाली डिजाइन करें। व्यक्तिगत और नौकरी गैराज, जब्ती प्रवाह, चाबियाँ, इन्वेंटरी संबंध और शीर्ष मुफ्त और प्रीमियम गैराज स्क्रिप्ट।

Agency Scripts

Agency Scripts के संस्थापक और प्रमुख डेवलपर

गाराज सिस्टम आर्किटेक्चर को समझना

एक गैराज सिस्टम किसी भी FiveM रोलप्ले सर्वर में सबसे आवश्यक विशेषताओं में से एक है, जो खिलाड़ियों को उनके वाहनों को संग्रहित, पुनः प्राप्त, और प्रबंधित करने का प्राथमिक तरीका प्रदान करता है। मूल रूप से, एक गैराज सिस्टम में तीन परस्पर जुड़े परतें होती हैं: एक डेटाबेस परत जो वाहन स्वामित्व और स्थिति को स्थायी बनाती है, एक सर्वर-साइड लॉजिक परत जो स्पॉन और डिस्पॉन ऑपरेशनों को सत्यापन के साथ संभालती है, और एक क्लाइंट-साइड UI परत जो खिलाड़ियों को उनके संग्रहित वाहनों के साथ इंटरैक्ट करने देती है। कोई भी कोड लिखने से पहले, आपको प्रमुख वास्तुशिल्प विकल्पों पर निर्णय लेना होगा जैसे कि गैराज स्थान-आधारित हैं या वैश्विक, क्या खिलाड़ी किसी भी गैराज तक पहुंच सकते हैं या केवल विशिष्ट गैराज, और आप वाहन गुणों जैसे संशोधनों, ईंधन स्तर, और क्षति स्थिति को कैसे संभालना चाहते हैं। सबसे अच्छे गैराज सिस्टम पूरी वाहन गुण वस्तु को संग्रहीत करते हैं ताकि जब खिलाड़ी अपनी कार पुनः प्राप्त करे, तो वह बिल्कुल वैसे ही वापस आए जैसा उन्होंने छोड़ा था, जिसमें कस्टम पेंट जॉब्स, प्रदर्शन उन्नयन, और यहां तक कि शरीर पर गंदगी का स्तर भी शामिल है।

डेटाबेस स्कीमा और वाहन स्थिरता

आपका डेटाबेस स्कीमा पूरे गैराज सिस्टम की नींव बनाता है। आपको एक तालिका चाहिए जो वाहन स्वामित्व, वर्तमान स्थिति, और संग्रहीत संपत्तियों को ट्रैक करे। स्थिति कॉलम महत्वपूर्ण है क्योंकि यह निर्धारित करता है कि वाहन वर्तमान में दुनिया में स्पॉन है, गैराज में संग्रहीत है, या जब्त स्थल में रखा है। यहाँ एक व्यावहारिक स्कीमा है जो आवश्यक फ़ील्ड कवर करता है:

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

यह mods कॉलम में JSON-एन्कोडेड ऑब्जेक्ट होता है जिसमें सभी वाहन संशोधनों को शामिल किया जाता है जो functions जैसे द्वारा लौटाए जाते हैं QBCore.Functions.GetVehicleProperties(vehicle) या ESX में समकक्ष। इंडेक्सिंग citizenid, plate, और state कॉलम यह सुनिश्चित करता है कि खोज तेज बनी रहे भले ही आपका खिलाड़ी आधार हजारों में बढ़ जाए। इस तालिका के साथ इंटरैक्ट करते समय SQL इंजेक्शन हमलों से बचने के लिए हमेशा पैरामीटरयुक्त क्वेरीज़ का उपयोग करें।

सर्वर-साइड स्पॉन और डिस्पॉन लॉजिक

सर्वर-साइड वह जगह है जहाँ सभी महत्वपूर्ण सत्यापन होते हैं। जब कोई खिलाड़ी गैराज से वाहन निकालने का अनुरोध करता है, तो सर्वर को सत्यापित करना चाहिए कि खिलाड़ी वास्तव में उस वाहन का मालिक है, वाहन वर्तमान में गैराज स्थिति में है, और एक वैध स्पॉन पॉइंट उपलब्ध है। कभी भी क्लाइंट को सीधे स्पॉन स्थिति निर्धारित करने न दें क्योंकि चीटर कहीं भी वाहन स्पॉन कर सकते हैं। इसके बजाय, सर्वर पर स्पॉन पॉइंट्स परिभाषित करें और निकटतम उपलब्ध एक का चयन करें। यहाँ एक सुरक्षित सर्वर-साइड टेकेआउट हैंडलर का उदाहरण है:

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)

डिस्पॉन प्रक्रिया के लिए, सर्वर को वर्तमान वाहन गुणों को पकड़ने की आवश्यकता होती है इससे पहले कि इसे दुनिया से हटा दिया जाए। यह सुनिश्चित करता है कि अंतिम संग्रहण के बाद किए गए संशोधन सहेजे गए हैं। हमेशा ईंधन, इंजन स्वास्थ्य, और बॉडी स्वास्थ्य को मॉड JSON के साथ अपडेट करें ताकि सब कुछ सही ढंग से बना रहे। सर्वर साइड पर दूरी जांच लागू करें ताकि यह सुनिश्चित हो सके कि खिलाड़ी वास्तव में गैराज स्थान के पास है इससे पहले कि संग्रहण संचालन की अनुमति दी जाए।

NUI के साथ क्लाइंट-साइड गैराज UI

गैरेज UI वह जगह है जहां खिलाड़ी प्रणाली के साथ इंटरैक्ट करते हैं, और एक अच्छी तरह डिज़ाइन किया गया इंटरफ़ेस निराशाजनक अनुभव और सहज अनुभव के बीच अंतर बनाता है। HTML, CSS, और JavaScript के साथ NUI का उपयोग करके एक प्रतिक्रियाशील पैनल बनाएं जो वर्तमान गैरेज में संग्रहीत सभी वाहनों को प्रदर्शित करता है। प्रत्येक वाहन प्रविष्टि में वाहन का नाम, लाइसेंस प्लेट, ईंधन स्तर, और समग्र स्थिति एक नजर में दिखानी चाहिए। एक पूर्वावलोकन प्रणाली शामिल करें जो अस्थायी रूप से वाहन मॉडल को स्पॉन करे ताकि खिलाड़ी देख सकें कि वे क्या चुन रहे हैं, विशेष रूप से जब किसी खिलाड़ी के पास एक ही प्रकार के कई वाहन हों। गैरेज मेनू खोलने और वाहन डेटा एकत्र करने के लिए क्लाइंट-साइड लॉजिक यहां है:

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 पक्ष पर, प्रत्येक वाहन को एक कार्ड के रूप में रेंडर करें जिसमें वाहन निकालने या इसे दूसरे गैराज में स्थानांतरित करने के लिए क्रिया बटन हों। बड़े संग्रह वाले खिलाड़ियों के लिए जल्दी से आवश्यक वाहन खोजने के लिए छंटाई और फ़िल्टरिंग विकल्प जोड़ने पर विचार करें। एक खोज बार जो प्लेट नंबर या वाहन नाम से फ़िल्टर करता है, एक छोटा सा जोड़ है जो उन सर्वरों पर उपयोगिता को नाटकीय रूप से बढ़ाता है जहां खिलाड़ी समय के साथ कई वाहन जमा करते हैं।

वाहन संपत्ति भंडारण और पुनर्स्थापना

वाहन गुणों को सही ढंग से सहेजना और पुनर्स्थापित करना गैराज सिस्टम विकास के सबसे जटिल हिस्सों में से एक है। प्रॉपर्टीज ऑब्जेक्ट में दर्जनों फ़ील्ड होते हैं जिनमें रंग, लिवरी, नीयन लाइट्स, विंडो टिंट्स, टायर स्मोक कलर, एक्स्ट्रा, और हर प्रदर्शन संशोधन शामिल हैं। वाहन स्टोर करते समय, एंटिटी को हटाने से ठीक पहले प्रॉपर्टीज कैप्चर करें ताकि आपको सबसे वर्तमान स्थिति मिले। वाहन को वापस स्पॉन करते समय, प्रॉपर्टीज लागू करने से पहले एंटिटी के पूरी तरह लोड होने का इंतजार करें, अन्यथा कस्टम व्हील्स या इंजन अपग्रेड जैसे संशोधन चुपचाप विफल हो जाएंगे। एक छोटा विलंब या उचित एंटिटी अस्तित्व जांच लूप का उपयोग करें:

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

एडऑन वाहनों पर विशेष ध्यान दें क्योंकि उनमें कभी-कभी कस्टम एक्स्ट्रा या लिवरी इंडेक्स होते हैं जो वैनिला GTA वाहनों से अलग व्यवहार करते हैं। अपनी संपत्ति सहेजने और पुनर्स्थापित करने के चक्र का विभिन्न प्रकार के वाहनों के साथ पूरी तरह परीक्षण करें ताकि शुरुआती समस्याओं को जल्दी पकड़ा जा सके।

इंपाउंड सिस्टम एकीकरण

एक इम्पाउंड सिस्टम आपके गैराज के साथ मिलकर काम करता है और एक यथार्थवाद की परत जोड़ता है जिसकी रोलप्ले सर्वर मांग करते हैं। वाहन कई कारणों से इम्पाउंड में जाते हैं: गिरफ्तारी के दौरान पुलिस जब्ती, सर्वर पुनःप्रारंभ के बाद परित्यक्त वाहनों की स्वचालित सफाई, या नियम उल्लंघनों के लिए एडमिन कार्रवाई। जब कोई वाहन इम्पाउंड होता है, तो डेटाबेस में उसकी स्थिति को 2 पर अपडेट करें और वैकल्पिक रूप से एक डिपो कीमत सेट करें जिसे खिलाड़ी को वाहन पुनः प्राप्त करने के लिए भुगतान करना होगा। इम्पाउंड लॉट को गैराज की तरह काम करना चाहिए लेकिन रिहाई से पहले भुगतान की अतिरिक्त आवश्यकता के साथ। नक्शे पर एक अलग इम्पाउंड स्थान बनाएं जिसमें इसके अपने स्पॉन पॉइंट और NUI इंटरफ़ेस हों जो इम्पाउंड शुल्क को प्रमुखता से दिखाए।

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)

एक स्तरित मूल्य निर्धारण प्रणाली लागू करने पर विचार करें जहाँ इम्पाउंड शुल्क हर बार जब वही वाहन इम्पाउंड किया जाता है तो बढ़ता है, जिससे खिलाड़ी इम्पाउंड को मुफ्त पार्किंग के रूप में उपयोग करने से बचें। आप एक समय-आधारित मैकेनिक भी जोड़ सकते हैं जहाँ इम्पाउंड में वाहन जो कॉन्फ़िगर करने योग्य वास्तविक दुनिया के दिनों से अधिक समय तक रहते हैं, उन्हें बिना शुल्क के स्वचालित रूप से गैराज में वापस रिलीज़ कर दिया जाता है, जिससे स्थायी नुकसान की स्थिति से बचा जा सके जो खिलाड़ियों को निराश करती है।

गैरेज ब्लिप्स और टारगेट इंटीग्रेशन

गाराज को खोजने योग्य और इंटरैक्ट करने में आसान बनाने के लिए उचित ब्लिप प्लेसमेंट और इंटरैक्शन ज़ोन आवश्यक हैं। प्रत्येक गाराज स्थान के लिए मैप ब्लिप जोड़ें ताकि खिलाड़ी उन्हें मिनीमैप पर पा सकें, और इंटरैक्शन ट्रिगर के लिए निकटता-आधारित मार्कर या टारगेट सिस्टम इंटीग्रेशन का उपयोग करें। ox_target या qb-target जैसे टारगेट सिस्टम एक साफ अनुभव प्रदान करते हैं क्योंकि वे केवल तब इंटरैक्शन विकल्प दिखाते हैं जब खिलाड़ी किसी विशिष्ट बिंदु पर निशाना लगाता है, स्क्रीन की भीड़ कम करते हैं। अपने गाराज स्थानों को एक साझा कॉन्फ़िग फ़ाइल में परिभाषित करें जिसे क्लाइंट और सर्वर दोनों संदर्भित कर सकें, समन्वित निर्देशांक, स्पॉन पॉइंट्स और सेटिंग्स के साथ:

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

कई वाहन प्रकारों का समर्थन करें, नावों और विमान के लिए अलग-अलग गैराज बनाकर और पानी के पास या हवाई अड्डों पर उपयुक्त स्पॉन स्थानों के साथ। vehicleType फ़िल्टर सुनिश्चित करता है कि खिलाड़ी केवल स्ट्रीट गैराज पर लैंड वाहन देखें और केवल मरीना पर नावें देखें, भ्रम और स्पॉन समस्याओं को रोकता है। जब कोई खिलाड़ी गैराज के पास पहुंचता है, तो इंटरैक्शन प्रॉम्प्ट दिखाने से पहले जांचें कि क्या उनके पास वहां कोई वाहन संग्रहीत है ताकि उन खिलाड़ियों के लिए अनावश्यक मेनू खुलने से बचा जा सके जिनके पास उस स्थान पर कोई वाहन नहीं है।

प्रदर्शन अनुकूलन सुझाव

गैरेज सिस्टम सावधानी से लागू न किए जाने पर प्रदर्शन में बाधा बन सकते हैं, खासकर उन सर्वरों पर जहाँ सैकड़ों समवर्ती खिलाड़ी होते हैं जिनके पास कई वाहन होते हैं। सर्वर साइड पर वाहन सूचियों को कैश करें बजाय इसके कि हर बार जब कोई खिलाड़ी गैरेज मेनू खोले तो डेटाबेस क्वेरी करें, और कैश को केवल तब अमान्य करें जब वाहन की स्थिति बदलती है। क्लाइंट साइड पर, जब जरूरत न हो तो NUI फ्रेम्स को खुला न रखें क्योंकि छिपे हुए NUI फ्रेम भी संसाधन उपयोग करते हैं यदि वे JavaScript टाइमर या एनिमेशन चला रहे हों। वाहन स्पॉन करते समय, सुनिश्चित करें कि आप एंटिटीज़ को ठीक से साफ कर रहे हैं, उन्हें तब 'नो लॉन्गर नीडेड' सेट करें जब खिलाड़ी उन्हें स्टोर कर ले, और एक फॉलबैक क्लीनअप रूटीन लागू करें जो समय-समय पर चलता रहे ताकि क्रैश या डिस्कनेक्शन के कारण ठीक से डिस्पॉन न हुए वाहन एंटिटीज़ को पकड़ सके। नेटिव फंक्शंस का उपयोग करें जैसे GetGamePool('CVehicle') संभलकर और जब आपको विश्व में मौजूदा खिलाड़ी वाहनों की जांच करनी हो तो परिणाम कैश करें। अंत में, डेटाबेस क्वेरीज़ को सीमित रखने और किसी एक खिलाड़ी द्वारा सैकड़ों वाहन स्टोर करने से रोकने के लिए प्रति गैराज अधिकतम वाहन सीमा लागू करने पर विचार करें, जो पुनः प्राप्ति संचालन को धीमा कर सकता है।

शुरू करने के लिए तैयार?

हमारी शॉप से स्क्रिप्ट लें, या सपोर्ट, अपडेट और आगे आने वाली चीज़ों की झलक के लिए Discord से जुड़ें।