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

FiveM कार डीलर सिस्टम: डीलरशिप्स को सही तरीके से स्क्रिप्ट करें

एक यथार्थवादी FiveM कार डीलर बनाएं। टेस्ट ड्राइव, शो रूम सेटअप, NPC और खिलाड़ी-चालित विकल्प, साथ ही QBCore और ESX के लिए सर्वश्रेष्ठ डीलरशिप स्क्रिप्ट।

Agency Scripts

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

वाहन डीलरशिप संरचना

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

वाहन कैटलॉग और श्रेणी प्रणाली

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

Config.VehicleCatalog = {
    categories = {
        {id = 'sedan', label = 'Sedans', icon = 'fa-car'},
        {id = 'sport', label = 'Sports Cars', icon = 'fa-flag-checkered'},
        {id = 'suv', label = 'SUVs & Trucks', icon = 'fa-truck'},
        {id = 'muscle', label = 'Muscle Cars', icon = 'fa-bolt'},
        {id = 'motorcycle', label = 'Motorcycles', icon = 'fa-motorcycle'},
    },
    vehicles = {
        -- Sedans
        {model = 'sultan', label = 'Karin Sultan', price = 24500,
         category = 'sedan', seats = 4, testDrive = true},
        {model = 'schafter2', label = 'Benefactor Schafter', price = 38000,
         category = 'sedan', seats = 4, testDrive = true},
        -- Sports
        {model = 'elegy2', label = 'Annis Elegy RH8', price = 95000,
         category = 'sport', seats = 2, testDrive = true},
        {model = 'comet2', label = 'Pfister Comet', price = 110000,
         category = 'sport', seats = 2, testDrive = true},
        -- SUVs
        {model = 'baller', label = 'Gallivanter Baller', price = 55000,
         category = 'suv', seats = 4, testDrive = true},
        -- Muscle
        {model = 'dominator', label = 'Vapid Dominator', price = 42000,
         category = 'muscle', seats = 2, testDrive = true},
        -- Motorcycles
        {model = 'bati', label = 'Pegassi Bati 801', price = 18000,
         category = 'motorcycle', seats = 2, testDrive = true},
    },
}

-- Build lookup table for fast server-side price validation
Config.VehiclePrices = {}
for _, v in ipairs(Config.VehicleCatalog.vehicles) do
    Config.VehiclePrices[v.model] = v.price
end

मूल्य खोज तालिका Config.VehiclePrices सर्वर पर O(1) मूल्य सत्यापन सक्षम करता है, जिससे क्लाइंट्स द्वारा छेड़े गए दाम भेजने से रोका जाता है। खरीदारी प्रक्रिया से पहले हमेशा इस तालिका के खिलाफ मॉडल नाम को सत्यापित करें। यदि आप चाहते हैं कि सर्वर व्यवस्थापक बिना सर्वर पुनः आरंभ किए प्रशासन पैनल के माध्यम से कीमतें समायोजित कर सकें, तो config फ़ाइल के बजाय डेटाबेस तालिका से वाहन की कीमतें लोड करने पर विचार करें।

शोरूम प्रीव्यू सिस्टम

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

-- client/showroom.lua
local previewVehicle = nil
local previewCam = nil
local camAngle = 0.0

function ShowVehiclePreview(modelName)
    -- Clean up previous preview
    DestroyPreview()

    local model = GetHashKey(modelName)
    RequestModel(model)
    while not HasModelLoaded(model) do Wait(10) end

    local showroomPos = Config.ShowroomPosition -- vector4
    previewVehicle = CreateVehicle(model, showroomPos.x, showroomPos.y,
        showroomPos.z, showroomPos.w, false, false)

    SetEntityInvincible(previewVehicle, true)
    SetVehicleDoorsLocked(previewVehicle, 2)
    FreezeEntityPosition(previewVehicle, true)
    SetVehicleOnGroundProperly(previewVehicle)
    SetModelAsNoLongerNeeded(model)

    -- Create orbiting camera
    previewCam = CreateCam('DEFAULT_SCRIPTED_CAMERA', true)
    UpdateCameraPosition()
    SetCamActive(previewCam, true)
    RenderScriptCams(true, true, 500, true, true)
end

function UpdateCameraPosition()
    if not previewCam or not previewVehicle then return end
    local vehPos = GetEntityCoords(previewVehicle)
    local radius = 6.0
    local height = 2.0
    local rad = math.rad(camAngle)
    local camX = vehPos.x + radius * math.cos(rad)
    local camY = vehPos.y + radius * math.sin(rad)
    SetCamCoord(previewCam, camX, camY, vehPos.z + height)
    PointCamAtEntity(previewCam, previewVehicle, 0.0, 0.0, 0.0, true)
end

function DestroyPreview()
    if previewVehicle then
        DeleteEntity(previewVehicle)
        previewVehicle = nil
    end
    if previewCam then
        SetCamActive(previewCam, false)
        RenderScriptCams(false, true, 500, true, true)
        DestroyCam(previewCam, false)
        previewCam = nil
    end
end

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

खरीद और लेनदेन प्रसंस्करण

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

RegisterNetEvent('dealer:server:purchaseVehicle', function(modelName, paymentType)
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end

    -- Validate model exists and get price
    local price = Config.VehiclePrices[modelName]
    if not price then
        TriggerClientEvent('QBCore:Notify', src, 'Vehicle not available', 'error')
        return
    end

    -- Check payment
    local moneyType = paymentType == 'bank' and 'bank' or 'cash'
    if Player.PlayerData.money[moneyType] < price then
        TriggerClientEvent('QBCore:Notify', src, 'Insufficient funds', 'error')
        return
    end

    -- Generate unique plate
    local plate = GenerateUniquePlate()

    -- Process payment
    Player.Functions.RemoveMoney(moneyType, price, 'vehicle-purchase-' .. modelName)

    -- Create vehicle record
    local vehicleHash = GetHashKey(modelName)
    MySQL.insert([[
        INSERT INTO player_vehicles
        (citizenid, vehicle, hash, plate, garage, state, fuel, engine, body)
        VALUES (?, ?, ?, ?, ?, 1, 100, 1000.0, 1000.0)
    ]], {
        Player.PlayerData.citizenid,
        modelName,
        tostring(vehicleHash),
        plate,
        'pillboxgarage',
    })

    -- Log transaction
    MySQL.insert([[
        INSERT INTO vehicle_sales (citizenid, vehicle, plate, price, sold_at)
        VALUES (?, ?, ?, ?, NOW())
    ]], {Player.PlayerData.citizenid, modelName, plate, price})

    TriggerClientEvent('QBCore:Notify', src,
        'Vehicle purchased! Plate: ' .. plate, 'success')
    TriggerClientEvent('dealer:client:vehiclePurchased', src, modelName, plate)
end)

function GenerateUniquePlate()
    local plate
    repeat
        plate = ''
        local chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
        for i = 1, 8 do
            local idx = math.random(1, #chars)
            plate = plate .. chars:sub(idx, idx)
        end
        local exists = MySQL.scalar.await(
            'SELECT 1 FROM player_vehicles WHERE plate = ?', {plate}
        )
    until not exists
    return plate
end

यह GenerateUniquePlate function एक पुनः प्रयास लूप का उपयोग करता है ताकि पूरे डेटाबेस में प्लेट की अनूठापन सुनिश्चित हो सके। जबकि 8 अल्फ़ान्यूमेरिक अक्षरों के साथ टकराव सांख्यिकीय रूप से दुर्लभ हैं, यह जांच पूर्ण सुरक्षा सुनिश्चित करती है। प्रशासनिक समीक्षा और सर्वर अर्थव्यवस्था विश्लेषण के लिए हर बिक्री को एक अलग तालिका में लॉग करें, यह ट्रैक करते हुए कि कौन से वाहन सबसे लोकप्रिय हैं और डीलरशिप के माध्यम से कुल नकदी प्रवाह कितना है।

टेस्ट ड्राइव सिस्टम

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

-- Client: test drive logic
local testDriveVehicle = nil
local testDriveTimer = 0
local testDriveActive = false

function StartTestDrive(modelName, duration)
    local model = GetHashKey(modelName)
    RequestModel(model)
    while not HasModelLoaded(model) do Wait(10) end

    local spawnPos = Config.TestDriveSpawn
    testDriveVehicle = CreateVehicle(model, spawnPos.x, spawnPos.y,
        spawnPos.z, spawnPos.w, true, false)

    SetVehicleNumberPlateText(testDriveVehicle, 'TESTDRVE')
    TaskWarpPedIntoVehicle(PlayerPedId(), testDriveVehicle, -1)
    SetModelAsNoLongerNeeded(model)

    testDriveTimer = duration
    testDriveActive = true

    -- Timer and boundary check thread
    CreateThread(function()
        while testDriveActive and testDriveTimer > 0 do
            Wait(1000)
            testDriveTimer = testDriveTimer - 1

            -- Show remaining time
            SendNUIMessage({
                action = 'updateTestDrive',
                timeLeft = testDriveTimer
            })

            -- Check boundary
            local playerPos = GetEntityCoords(PlayerPedId())
            local dealerPos = Config.DealerLocation
            if #(playerPos - dealerPos) > Config.TestDriveRadius then
                QBCore.Functions.Notify('Too far from dealer, returning...', 'error')
                EndTestDrive()
                return
            end
        end

        if testDriveActive then
            EndTestDrive()
        end
    end)
end

function EndTestDrive()
    testDriveActive = false
    if testDriveVehicle and DoesEntityExist(testDriveVehicle) then
        DeleteEntity(testDriveVehicle)
        testDriveVehicle = nil
    end

    local returnPos = Config.DealerLocation
    SetEntityCoords(PlayerPedId(), returnPos.x, returnPos.y, returnPos.z)
    QBCore.Functions.Notify('Test drive ended', 'info')
end

भौगोलिक सीमा खिलाड़ियों को परीक्षण वाहन को पूरे मानचित्र पर चलाने और उसे छोड़ने से रोकती है। त्रिज्या को एक उचित दूरी पर सेट करें जो पास की सड़कों के माध्यम से सार्थक ड्राइव की अनुमति देता है जबकि वाहन को पुनर्प्राप्त किया जा सके। "TESTDRVE" प्लेट अन्य खिलाड़ियों के लिए एक दृश्य संकेतक के रूप में कार्य करती है कि वाहन अस्थायी है।

वित्तपोषण और भुगतान योजनाएं

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

Employee Sales and Commission Tracking

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

ऑप्टिमाइज़ेशन और एंटी-एक्सप्लॉइट उपाय

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

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

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