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

FiveM शिकार लाइसेंस प्रणाली: परमिट, जुर्माने और क्षेत्र

FiveM में एक कार्यशील शिकार लाइसेंस प्रणाली जोड़ें। परमिट, संरक्षित क्षेत्र, जुर्माने, रेंजर नौकरियां और यथार्थवादी वन्यजीव कानून के लिए शीर्ष स्क्रिप्ट।

Agency Scripts

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

रोलप्ले इन्फ्रास्ट्रक्चर के रूप में लाइसेंस

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

लचीला लाइसेंस प्रकार कॉन्फ़िगरेशन

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

-- shared/config.lua
Config = {}

Config.LicenseTypes = {
    driving = {
        label       = 'Driving License',
        description = 'Required to legally operate motor vehicles',
        cost        = 500,
        requiresExam = true,
        examType    = 'practical',
        validDays   = 365,
        issuedBy    = {'dmv', 'police'},
        restrictions = {'vehicle_operation'},
    },
    weapons = {
        label       = 'Weapons Permit',
        description = 'Required to legally carry firearms',
        cost        = 2500,
        requiresExam = true,
        examType    = 'written',
        validDays   = 180,
        issuedBy    = {'police'},
        restrictions = {'weapon_carry'},
        prerequisites = {'driving'},
    },
    hunting = {
        label       = 'Hunting License',
        description = 'Required for legal hunting activities',
        cost        = 750,
        requiresExam = true,
        examType    = 'written',
        validDays   = 90,
        issuedBy    = {'ranger', 'dmv'},
        restrictions = {'hunting_activity'},
    },
    fishing = {
        label       = 'Fishing Permit',
        description = 'Required for legal fishing activities',
        cost        = 200,
        requiresExam = false,
        validDays   = 30,
        issuedBy    = {'dmv', 'ranger'},
        restrictions = {'fishing_activity'},
    },
    business = {
        label       = 'Business License',
        description = 'Required to operate a commercial business',
        cost        = 5000,
        requiresExam = false,
        validDays   = 365,
        issuedBy    = {'government'},
        restrictions = {'business_operation'},
    },
    pilot = {
        label       = 'Pilot License',
        description = 'Required to operate aircraft',
        cost        = 10000,
        requiresExam = true,
        examType    = 'practical',
        validDays   = 180,
        issuedBy    = {'faa'},
        restrictions = {'aircraft_operation'},
        prerequisites = {'driving'},
    },
}

आवेदन और परीक्षा प्रवाह

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

-- server/licenses.lua
function ApplyForLicense(playerId, licenseType)
    local config = Config.LicenseTypes[licenseType]
    if not config then return false, 'Invalid license type' end

    local identifier = GetPlayerIdentifier(playerId, 0)

    -- Check prerequisites
    if config.prerequisites then
        for _, prereq in ipairs(config.prerequisites) do
            local has = HasValidLicense(identifier, prereq)
            if not has then
                return false, 'Missing prerequisite: ' ..
                    Config.LicenseTypes[prereq].label
            end
        end
    end

    -- Check if already has active license
    local existing = MySQL.single.await([[
        SELECT id, status, expires_at FROM licenses
        WHERE identifier = ? AND license_type = ?
        AND status IN ('active','suspended')
    ]], {identifier, licenseType})

    if existing and existing.status == 'active' then
        return false, 'You already hold this license'
    end

    -- Deduct application fee
    local paid = exports['framework']:RemoveMoney(
        playerId, config.cost, 'bank')
    if not paid then
        return false, 'Insufficient funds ($' .. config.cost .. ' required)'
    end

    -- Create pending application
    local appId = MySQL.insert.await([[
        INSERT INTO license_applications
            (identifier, license_type, status, applied_at)
        VALUES (?, ?, 'pending', NOW())
    ]], {identifier, licenseType})

    if config.requiresExam then
        return true, 'Application submitted. Please complete the exam.', appId
    else
        -- No exam required, issue directly
        IssueLicense(identifier, licenseType)
        return true, 'License issued successfully!'
    end
end

function IssueLicense(identifier, licenseType)
    local config = Config.LicenseTypes[licenseType]
    local expiresAt = os.time() + (config.validDays * 86400)

    MySQL.insert([[
        INSERT INTO licenses
            (identifier, license_type, status, issued_at, expires_at)
        VALUES (?, ?, 'active', NOW(), FROM_UNIXTIME(?))
        ON DUPLICATE KEY UPDATE
            status = 'active', issued_at = NOW(),
            expires_at = FROM_UNIXTIME(?)
    ]], {identifier, licenseType, expiresAt, expiresAt})
end

function HasValidLicense(identifier, licenseType)
    local result = MySQL.single.await([[
        SELECT id FROM licenses
        WHERE identifier = ? AND license_type = ?
        AND status = 'active' AND expires_at > NOW()
    ]], {identifier, licenseType})
    return result ~= nil
end

exports('HasValidLicense', HasValidLicense)

कानून प्रवर्तन एकीकरण

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

-- server/enforcement.lua
RegisterNetEvent('licenses:checkCitizen', function(targetId)
    local src = source
    -- Verify requesting player is law enforcement
    local job = exports['framework']:GetPlayerJob(src)
    if job ~= 'police' and job ~= 'ranger' and job ~= 'sheriff' then
        return
    end

    local targetIdentifier = GetPlayerIdentifier(targetId, 0)
    local licenses = MySQL.query.await([[
        SELECT license_type, status, issued_at, expires_at,
               suspended_reason, suspended_until
        FROM licenses WHERE identifier = ?
    ]], {targetIdentifier})

    TriggerClientEvent('licenses:showResults', src, licenses)
end)

RegisterNetEvent('licenses:suspend', function(targetIdentifier, licType, reason, days)
    local src = source
    local job = exports['framework']:GetPlayerJob(src)
    if job ~= 'police' and job ~= 'judge' then return end

    local suspendUntil = os.time() + (days * 86400)

    MySQL.update([[
        UPDATE licenses SET
            status = 'suspended',
            suspended_reason = ?,
            suspended_until = FROM_UNIXTIME(?)
        WHERE identifier = ? AND license_type = ?
    ]], {reason, suspendUntil, targetIdentifier, licType})

    -- Notify the affected player if online
    local targetPlayer = GetPlayerFromIdentifier(targetIdentifier)
    if targetPlayer then
        TriggerClientEvent('licenses:notify', targetPlayer,
            'Your ' .. Config.LicenseTypes[licType].label ..
            ' has been suspended for ' .. days .. ' days. Reason: ' .. reason)
    end

    -- Log the action
    MySQL.insert([[
        INSERT INTO license_logs
            (identifier, license_type, action, performed_by, reason)
        VALUES (?, ?, 'suspend', ?, ?)
    ]], {targetIdentifier, licType, GetPlayerIdentifier(src, 0), reason})
end)

नवीनीकरण प्रणाली और समाप्ति प्रबंधन

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

स्क्रिप्ट्स में प्रतिबंध प्रवर्तन

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

डेटाबेस स्कीमा और एडमिन टूल्स

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

-- SQL schema
CREATE TABLE IF NOT EXISTS licenses (
    id                INT AUTO_INCREMENT PRIMARY KEY,
    identifier        VARCHAR(64) NOT NULL,
    license_type      VARCHAR(32) NOT NULL,
    status            ENUM('active','suspended','revoked','expired')
                      DEFAULT 'active',
    issued_at         TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    expires_at        TIMESTAMP NULL,
    suspended_reason  VARCHAR(255) DEFAULT NULL,
    suspended_until   TIMESTAMP NULL,
    UNIQUE KEY unique_license (identifier, license_type),
    INDEX idx_status (status),
    INDEX idx_expires (expires_at)
);

CREATE TABLE IF NOT EXISTS license_applications (
    id            INT AUTO_INCREMENT PRIMARY KEY,
    identifier    VARCHAR(64) NOT NULL,
    license_type  VARCHAR(32) NOT NULL,
    status        ENUM('pending','passed','failed','cancelled')
                  DEFAULT 'pending',
    exam_score    INT DEFAULT NULL,
    applied_at    TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    completed_at  TIMESTAMP NULL
);

CREATE TABLE IF NOT EXISTS license_logs (
    id            INT AUTO_INCREMENT PRIMARY KEY,
    identifier    VARCHAR(64) NOT NULL,
    license_type  VARCHAR(32) NOT NULL,
    action        VARCHAR(32) NOT NULL,
    performed_by  VARCHAR(64),
    reason        VARCHAR(255),
    created_at    TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_identifier (identifier)
);

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

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