>
Guide 2026-04-12

Système de prison pour FiveM

TDYSKY

TDYSKY

Fondateur et développeur principal chez Agency Scripts

Architecture du système pénitentiaire

Un système carcéral est l'une des fonctionnalités les plus percutantes que tu puissies ajouter à un serveur de jeu de rôle FiveM, car il crée des conséquences significatives pour les activités criminelles tout en donnant aux joueurs incarcérés quelque chose d'intéressant à faire au lieu de regarder un mur pendant trente minutes. L'architecture s'articule autour d'un chronomètre de prison qui compte à rebours en temps réel, d'un ensemble d'activités pénitentiaires qui permettent aux détenus de réduire leur peine grâce au travail et d'un système de délimitation qui restreint les joueurs à l'enceinte de la prison jusqu'à ce que leur peine soit purgée. L'emplacement du pénitencier de Bolingbroke dans GTA V est le choix standard car il présente un intérieur entièrement modélisé avec des blocs cellulaires, une cour et des installations environnantes. Ton système a besoin de trois composants principaux : la logique de détermination de la peine sur le serveur qui attribue une peine de prison et supprime les armes et la contrebande, le contrôle des limites côté client qui téléporte les joueurs s'ils quittent le périmètre de la prison, et le cadre d'activité qui donne aux détenus des tâches productives pour passer le temps.

Schéma de base de données et détermination de la peine

La base de données suit les peines actives, l'historique des peines pour les casiers judiciaires et les progrès des détenus dans leurs activités. Stockez l'heure de fin de la phrase sous forme d'horodatage absolu plutôt que de durée restante afin que le compte à rebours continue même lorsque le joueur est hors ligne, empêchant ainsi l'exploit où les joueurs se déconnectent pour mettre leur phrase en pause. Incluez des champs pour l'agent qui a procédé à l'arrestation, les accusations et la durée initiale de la peine à des fins de tenue de dossiers :

CREATE TABLE IF NOT EXISTS prison_sentences (
    id INT AUTO_INCREMENT PRIMARY KEY,
    citizenid VARCHAR(50) NOT NULL,
    sentence_minutes INT NOT NULL,
    time_served INT DEFAULT 0,
    reduction_earned INT DEFAULT 0,
    jailed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    release_at TIMESTAMP NOT NULL,
    charges TEXT DEFAULT NULL,
    arresting_officer VARCHAR(50) DEFAULT NULL,
    status ENUM('active', 'released', 'escaped', 'pardoned') DEFAULT 'active',
    parole_eligible BOOLEAN DEFAULT FALSE,
    INDEX idx_citizen (citizenid),
    INDEX idx_status (status)
);

CREATE TABLE IF NOT EXISTS prison_activity_log (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    citizenid VARCHAR(50) NOT NULL,
    sentence_id INT NOT NULL,
    activity_type ENUM('mining', 'gym', 'cleaning', 'library', 'yard') NOT NULL,
    reduction_minutes INT DEFAULT 0,
    completed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (sentence_id) REFERENCES prison_sentences(id)
);

Condamner un joueur

Lorsque la police utilise la commande prison, le serveur valide les autorisations de l'officier, calcule la peine en fonction des accusations, retire les armes et les objets illégaux du détenu et les téléporte vers un point d'apparition à l'intérieur de la prison. La phrase doit être conservée immédiatement dans la base de données afin qu'elle survive aux redémarrages du serveur. Implémentez la détermination de la peine sous la forme d'une commande côté serveur que seuls les policiers autorisés peuvent exécuter :

RegisterCommand('jail', function(source, args)
    local src = source
    local Officer = QBCore.Functions.GetPlayer(src)
    if not Officer then return end

    -- Check police job
    if Officer.PlayerData.job.name ~= 'police' then
        TriggerClientEvent('QBCore:Notify', src, 'Unauthorized', 'error')
        return
    end

    local targetId = tonumber(args[1])
    local minutes = tonumber(args[2])
    local charges = table.concat(args, ' ', 3)

    if not targetId or not minutes or minutes <= 0 then
        TriggerClientEvent('QBCore:Notify', src, 'Usage: /jail [id] [minutes] [charges]', 'error')
        return
    end

    local Target = QBCore.Functions.GetPlayer(targetId)
    if not Target then
        TriggerClientEvent('QBCore:Notify', src, 'Player not found', 'error')
        return
    end

    local citizenid = Target.PlayerData.citizenid
    local releaseAt = os.date('!%Y-%m-%d %H:%M:%S', os.time() + (minutes * 60))

    -- Insert sentence
    local sentenceId = MySQL.insert.await(
        'INSERT INTO prison_sentences (citizenid, sentence_minutes, release_at, charges, arresting_officer) VALUES (?, ?, ?, ?, ?)',
        { citizenid, minutes, releaseAt, charges, Officer.PlayerData.citizenid }
    )

    -- Strip weapons and contraband
    local contraband = {'weapon_pistol', 'weapon_smg', 'lockpick', 'thermite'}
    for _, item in ipairs(contraband) do
        local playerItem = Target.Functions.GetItemByName(item)
        if playerItem then
            Target.Functions.RemoveItem(item, playerItem.amount)
        end
    end

    -- Set jail metadata and teleport
    Target.Functions.SetMetaData('injail', sentenceId)
    TriggerClientEvent('prison:client:enter', targetId, sentenceId, minutes)
    TriggerClientEvent('QBCore:Notify', src, 'Jailed ' .. GetPlayerName(targetId) .. ' for ' .. minutes .. ' minutes', 'success')
end, false)

Activités pénitentiaires

Les activités en prison constituent la boucle de jeu principale qui maintient l'engagement des joueurs incarcérés. Sans eux, la prison est une punition qui pousse les joueurs à se déconnecter, ce qui est mauvais pour la population des serveurs. Concevez des activités qui offrent un compromis : effectuer un travail réduit ton peine, donnant ainsi aux détenus le pouvoir de gérer leur expérience. Chaque activité doit avoir un temps de recharge pour éviter le spam et un plafond de réduction maximum par cycle d'activité afin qu'une phrase de 60 minutes ne puisse pas être effacée en 5 minutes. Les activités les plus courantes sont l'exploitation minière dans la carrière, l'exercice dans le gymnase, le nettoyage des installations et le temps passé dans la bibliothèque de la prison. Chaque activité utilise un mini-jeu ou un modèle d'interaction différent pour offrir de la variété :

Config.PrisonActivities = {
    mining = {
        label = 'Quarry Mining',
        coords = vector3(1690.64, 2592.63, 45.56),
        radius = 15.0,
        reductionPerCycle = 2,   -- minutes reduced per completion
        cycleDuration = 45,       -- seconds per mining cycle
        cooldown = 30,            -- seconds between cycles
        maxReductionPerSession = 10,
        animation = { dict = 'amb@world_human_hammering@male@base', anim = 'base' },
        requiredProp = 'prop_tool_pickaxe',
    },
    gym = {
        label = 'Prison Gym',
        coords = vector3(1662.67, 2527.84, 45.56),
        radius = 10.0,
        reductionPerCycle = 1,
        cycleDuration = 30,
        cooldown = 60,
        maxReductionPerSession = 6,
        animation = { dict = 'amb@world_human_muscle_free_weights@male@barbell@base', anim = 'base' },
    },
    cleaning = {
        label = 'Facility Cleaning',
        coords = vector3(1653.18, 2499.26, 45.56),
        radius = 20.0,
        reductionPerCycle = 1,
        cycleDuration = 25,
        cooldown = 20,
        maxReductionPerSession = 8,
        animation = { dict = 'amb@world_human_janitor@male@base', anim = 'base' },
        requiredProp = 'prop_cs_broom',
    },
    library = {
        label = 'Prison Library',
        coords = vector3(1680.21, 2513.45, 45.56),
        radius = 8.0,
        reductionPerCycle = 1,
        cycleDuration = 60,
        cooldown = 45,
        maxReductionPerSession = 4,
        animation = { dict = 'amb@world_human_clipboard@male@base', anim = 'base' },
    },
}

-- Server-side activity completion handler
RegisterNetEvent('prison:server:completeActivity', function(activityType, sentenceId)
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end

    local citizenid = Player.PlayerData.citizenid
    local activity = Config.PrisonActivities[activityType]
    if not activity then return end

    -- Validate sentence is active
    local sentence = MySQL.single.await(
        'SELECT * FROM prison_sentences WHERE id = ? AND citizenid = ? AND status = "active"',
        { sentenceId, citizenid }
    )
    if not sentence then return end

    -- Check session reduction cap
    local sessionReduction = MySQL.scalar.await(
        'SELECT COALESCE(SUM(reduction_minutes), 0) FROM prison_activity_log WHERE sentence_id = ? AND activity_type = ? AND completed_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)',
        { sentenceId, activityType }
    )
    if sessionReduction >= activity.maxReductionPerSession then
        TriggerClientEvent('QBCore:Notify', src, 'Maximum reduction reached for this activity', 'error')
        return
    end

    -- Apply reduction
    local reduction = activity.reductionPerCycle
    MySQL.update('UPDATE prison_sentences SET reduction_earned = reduction_earned + ?, release_at = DATE_SUB(release_at, INTERVAL ? MINUTE) WHERE id = ?',
        { reduction, reduction, sentenceId })

    MySQL.insert('INSERT INTO prison_activity_log (citizenid, sentence_id, activity_type, reduction_minutes) VALUES (?, ?, ?, ?)',
        { citizenid, sentenceId, activityType, reduction })

    TriggerClientEvent('QBCore:Notify', src, 'Sentence reduced by ' .. reduction .. ' minute(s)', 'success')
    TriggerClientEvent('prison:client:updateTimer', src, reduction)
end)

Système de libération conditionnelle

Un système de libération conditionnelle ajoute une couche de jeu de rôle entre la prison et la liberté totale. Une fois qu'un détenu a purgé un pourcentage configurable de sa peine, généralement 60 à 75 %, il devient éligible à la libération conditionnelle. La libération conditionnelle peut être accordée par un juge ou un officier supérieur de la police par le biais d'un ordre, qui libère le joueur plus tôt mais le soumet à des restrictions. Les joueurs libérés sur parole peuvent être soumis à un couvre-feu qui les oblige à rester dans leur propriété à certaines heures, à l'obligation de s'enregistrer auprès d'un agent de libération conditionnelle à intervalles réguliers et à l'interdiction de posséder des armes ou d'entrer dans certaines zones. Si un joueur en liberté conditionnelle enfreint une condition, il est automatiquement renvoyé en prison avec sa peine restante plus une période supplémentaire pour la violation. Suivez l'état de la libération conditionnelle dans les métadonnées du joueur et effectuez des vérifications périodiques sur le serveur pour vérifier le respect des conditions.

Événements d'évasion de prison

Les évasions de prison sont des événements à l'échelle du serveur qui créent un jeu de rôle à enjeux élevés pour les criminels et les forces de l'ordre. Une évasion de prison doit nécessiter une préparation importante, plusieurs participants et donner à la police suffisamment d'avertissement pour organiser une réponse. Mettez-le en œuvre sous forme d'événement en plusieurs phases : une équipe extérieure doit d'abord acquérir des objets spécifiques comme un hélicoptère, des explosifs et des déguisements. Ensuite, ils doivent pirater le système de sécurité de la prison à travers un mini-jeu difficile sur un panneau de contrôle extérieur. Une fois la sécurité désactivée, les portes des cellules s'ouvrent sur une fenêtre limitée et les détenus peuvent tenter de s'échapper par des itinéraires prédéterminés tandis que les PNJ gardiens et la police qui répondent tentent de les arrêter. Le système ne devrait autoriser les évasions de prison que lorsqu’un nombre minimum de policiers sont en ligne pour garantir une réponse équitable :

Config.PrisonBreak = {
    minPoliceOnline = 5,
    cooldown = 28800, -- 8 hours between break attempts
    requiredItems = {
        { name = 'electronickit', amount = 2, label = 'Electronic Kit' },
        { name = 'thermite', amount = 3, label = 'Thermite Charge' },
    },
    phases = {
        {
            name = 'hack_security',
            label = 'Hack Security Grid',
            coords = vector3(1746.23, 2488.90, 45.80),
            duration = 60,
            minigame = { type = 'hack', difficulty = 'hard', attempts = 3 },
        },
        {
            name = 'breach_wall',
            label = 'Breach Perimeter Wall',
            coords = vector3(1651.78, 2569.33, 45.56),
            duration = 30,
            minigame = { type = 'thermite', time = 10 },
        },
        {
            name = 'disable_lockdown',
            label = 'Disable Lockdown Protocol',
            coords = vector3(1691.45, 2565.12, 45.56),
            duration = 45,
            minigame = { type = 'hack', difficulty = 'expert', attempts = 2 },
        },
    },
    escapeWindow = 300, -- 5 minutes to escape after all phases complete
    escapeRoutes = {
        { label = 'Main Gate', coords = vector3(1845.12, 2585.87, 45.67) },
        { label = 'Drainage Tunnel', coords = vector3(1617.34, 2523.90, 44.12) },
        { label = 'Helipad', coords = vector3(1700.89, 2565.34, 52.34) },
    },
}

Système de garde PNJ

Les PNJ gardiens assurent la sécurité ambiante autour de la prison et empêchent les détenus de sortir par hasard. Placez des gardes aux points d'étranglement clés comme la porte principale, les entrées des blocs cellulaires et le long du mur d'enceinte. Chaque garde doit disposer d'un itinéraire de patrouille avec des points de cheminement qu'il traverse, créant ainsi des fenêtres où certaines zones ne sont pas gardées. Les surveillants détectent les détenus qui quittent les zones autorisées et réagissent en donnant un avertissement verbal, puis en poursuivant et en attaquant si le détenu n'obtempère pas. Mettez en œuvre la détection des gardes à l'aide de contrôles de proximité qui tiennent compte de la ligne de vue afin que les détenus puissent se faufiler devant les gardes en restant à l'abri. Lors d'un événement d'évasion de prison, générez des gardes supplémentaires à des postes clés et augmentez leur niveau d'agressivité. Utiliser les fonctions natives du FiveM TaskPatrol et SetPedCombatAttributes pour donner aux gardes un comportement de patrouille réaliste. Assurez-tu que leur groupe relationnel soit hostile envers les détenus tout en restant neutre envers la police et les visiteurs, évitant ainsi les incidents de tirs amis lors des réponses aux évasions de prison.

Application des limites et libération

Le système de démarcation maintient les détenus dans le périmètre de la prison en utilisant une zone polygonale qui couvre l'ensemble de l'établissement. Lorsqu'un joueur avec une peine active quitte cette zone, il reçoit une notification d'avertissement et dispose de 10 secondes pour revenir avant d'être téléporté au point d'apparition de la prison avec un temps supplémentaire ajouté à sa peine en guise de pénalité pour tentative d'évasion. Du côté client, effectuez une vérification périodique toutes les 2 secondes qui compare la position du joueur par rapport au polygone des limites de la prison. Pour la publication, le serveur exécute une boucle de minuterie qui vérifie toutes les phrases actives toutes les 30 secondes. Lorsqu'une peine expire, le système met à jour l'état de la base de données sur libéré, efface les métadonnées de prison du joueur, restaure son inventaire à partir d'une sauvegarde pré-prison si tu en avez implémenté une, et le téléporte à la sortie de la prison avec une notification indiquant qu'il est libre. Si le joueur est hors ligne à l'expiration de sa phrase, la libération est gérée lors de sa prochaine connexion en vérifiant les phrases actives expirées dans l'événement de chargement du lecteur. Incluez une commande permettant à la police de libérer manuellement les joueurs plus tôt dans des situations telles qu'un emprisonnement injustifié ou des accords de plaidoyer négociés via un jeu de rôle.

Intégration avec les systèmes policier et judiciaire

Ton système pénitentiaire doit s'intégrer étroitement au système de police MDT et à tout système judiciaire sur le serveur. Lorsqu'un joueur est emprisonné, son casier judiciaire dans le MDT devrait automatiquement être mis à jour avec les accusations, la durée de la peine et l'agent qui l'a arrêté. Si ton serveur dispose d'un système judiciaire, les peines prononcées par un juge doivent passer par la même fonction d'emprisonnement pour garantir la cohérence. Mettre en œuvre un système d'appel de la peine grâce auquel les détenus peuvent soumettre un appel à partir de la bibliothèque de la prison, ce qui crée un ticket que les juges peuvent examiner, ce qui pourrait conduire à des réductions de peine ou à une libération anticipée. Suivez la récidive en comptant le nombre de fois où chaque citoyen a été emprisonné et utilisez ces données pour mettre en œuvre des peines plus sévères pour les récidivistes grâce à un système multiplicateur. Exportez ton fonction d'emprisonnement afin que d'autres ressources telles que le système judiciaire, la détection automatisée des crimes ou les commandes d'administration puissent toutes envoyer les joueurs en prison par le même chemin validé.

Partager cet article

Prêt à améliorer votre serveur ?

Découvrez nos scripts FiveM premium dans la boutique Agency Scripts ou rejoignez notre communauté Discord pour le support et les mises à jour.