>
Tutoriel 2026-04-09

Système immobilier pour FiveM

OntelMonke

OntelMonke

Administrateur et développeur chez Agency Scripts

Architecture du système de propriété

Un système immobilier transforme ton serveur FiveM d'un ensemble de bâtiments inaccessibles en un marché immobilier résidentiel où les joueurs achètent, vendent et louent des maisons, des appartements et des espaces commerciaux. L'architecture est centrée sur une base de données de registre immobilier qui suit chaque propriété disponible sur le serveur avec son emplacement, son type, son propriétaire actuel, sa valeur marchande et sa configuration intérieure. Contrairement aux simples scripts de logement qui permettent uniquement aux joueurs d'acheter une liste prédéterminée de propriétés, un système immobilier bien conçu comprend un travail d'agent immobilier qui crée des transactions pilotées par les joueurs avec des visites, des négociations et des structures de commissions. Le système doit s'intégrer à la ressource de verrouillage de porte de ton serveur afin que la propriété accorde ou révoque automatiquement l'accès au bâtiment physique, ainsi qu'aux systèmes de chargement intérieur tels que les MLO personnalisés ou les intérieurs instanciés. Prévoyez dès le départ trois catégories de propriétés : des propriétés résidentielles pour le logement des joueurs, des propriétés commerciales pour les entreprises appartenant aux joueurs et des propriétés locatives qui génèrent des revenus passifs pour les propriétaires.

Base de données et listes de propriétés

La base de données de propriétés constitue la base sur laquelle s'appuient tous les autres composants. Chaque enregistrement de propriété nécessite des métadonnées détaillées, notamment la coque ou l'intérieur du MLO qu'il utilise, les coordonnées d'entrée et de sortie, la disponibilité du garage, la capacité de stockage et l'historique des prix. Maintenez un tableau d'annonces distinct qui suit les propriétés actuellement sur le marché, leur prix demandé, l'agent inscripteur et depuis combien de temps elles sont inscrites. Cette séparation tu permet de suivre l'historique du marché et de calculer les tendances de la valeur des propriétés au fil du temps. Voici un schéma de base de données qui prend en charge le cycle de vie complet de l'immobilier :

CREATE TABLE IF NOT EXISTS properties (
    id INT AUTO_INCREMENT PRIMARY KEY,
    property_id VARCHAR(50) NOT NULL UNIQUE,
    label VARCHAR(100) NOT NULL,
    property_type ENUM('house', 'apartment', 'commercial', 'penthouse') NOT NULL,
    owner VARCHAR(50) DEFAULT NULL,
    price INT NOT NULL,
    original_price INT NOT NULL,
    interior VARCHAR(50) DEFAULT 'standard_1bed',
    enter_coords JSON NOT NULL,
    exit_coords JSON DEFAULT NULL,
    garage_slots INT DEFAULT 0,
    stash_slots INT DEFAULT 50,
    has_wardrobe TINYINT DEFAULT 1,
    is_furnished TINYINT DEFAULT 1,
    neighborhood VARCHAR(50) DEFAULT 'downtown',
    purchased_at BIGINT DEFAULT NULL,
    INDEX idx_owner (owner),
    INDEX idx_type (property_type),
    INDEX idx_neighborhood (neighborhood)
);

CREATE TABLE IF NOT EXISTS property_listings (
    id INT AUTO_INCREMENT PRIMARY KEY,
    property_id VARCHAR(50) NOT NULL,
    asking_price INT NOT NULL,
    listing_agent VARCHAR(50) DEFAULT NULL,
    listed_at BIGINT NOT NULL,
    status ENUM('active', 'pending', 'sold', 'cancelled') DEFAULT 'active',
    buyer VARCHAR(50) DEFAULT NULL,
    sold_at BIGINT DEFAULT NULL,
    sold_price INT DEFAULT NULL,
    INDEX idx_status (status),
    INDEX idx_agent (listing_agent)
);

Remplissez le tableau des propriétés avec toutes les propriétés disponibles sur ton serveur lors de la configuration initiale. Chaque propriété doit avoir des coordonnées soigneusement définies pour les points d'entrée qui s'alignent avec les positions des portes ou les zones d'interaction. Le original_price Le champ préserve la valeur de base pour les calculs de marché tandis que le price Le champ reflète la valeur marchande actuelle qui change en fonction de la demande du quartier, des améliorations apportées par le propriétaire et des conditions économiques globales du serveur.

Système d'emploi d'agent immobilier

C’est dans le métier d’agent immobilier que l’élément humain fait briller ce système. Les agents servent d'intermédiaires entre les acheteurs et les vendeurs, organisant des visites de propriétés, négociant les prix et gagnant une commission sur les ventes réalisées. Définissez des niveaux d'emploi qui débloquent progressivement de meilleures annonces : les agents juniors s'occupent des appartements et des petites maisons, les agents seniors accèdent aux propriétés de luxe et aux espaces commerciaux, et le propriétaire de l'agence gère l'équipe et prend une part de toutes les commissions. Lorsqu'un joueur intéressé par l'achat d'une propriété contacte un agent, celui-ci peut lancer une visite qui téléporte les deux joueurs à l'intérieur de la propriété pour une visite pas à pas. Voici la logique des transactions de présentation et de vente :

RegisterNetEvent('realestate:server:startShowing', function(propertyId, buyerId)
    local src = source
    local Agent = QBCore.Functions.GetPlayer(src)
    local Buyer = QBCore.Functions.GetPlayer(buyerId)

    if not Agent or not Buyer then return end
    if Agent.PlayerData.job.name ~= 'realtor' then return end

    local property = MySQL.query.await(
        'SELECT * FROM properties WHERE property_id = ? AND owner IS NULL',
        {propertyId}
    )

    if not property or not property[1] then
        return TriggerClientEvent('QBCore:Notify', src, 'Property not available', 'error')
    end

    local prop = property[1]
    local enterCoords = json.decode(prop.enter_coords)

    -- Teleport both to property
    TriggerClientEvent('realestate:client:enterShowing', src, prop, enterCoords)
    TriggerClientEvent('realestate:client:enterShowing', buyerId, prop, enterCoords)
    TriggerClientEvent('QBCore:Notify', buyerId,
        'Showing: ' .. prop.label .. ' - $' .. prop.price, 'info')
end)

RegisterNetEvent('realestate:server:purchaseProperty', function(propertyId, buyerId, agreedPrice)
    local src = source
    local Agent = QBCore.Functions.GetPlayer(src)
    local Buyer = QBCore.Functions.GetPlayer(buyerId)

    if not Agent or not Buyer then return end
    if Agent.PlayerData.job.name ~= 'realtor' then return end

    local property = MySQL.query.await(
        'SELECT * FROM properties WHERE property_id = ? AND owner IS NULL',
        {propertyId}
    )
    if not property or not property[1] then return end

    if not Buyer.Functions.RemoveMoney('bank', agreedPrice, 'property-purchase') then
        return TriggerClientEvent('QBCore:Notify', src, 'Buyer cannot afford this', 'error')
    end

    local commission = math.floor(agreedPrice * 0.05)
    Agent.Functions.AddMoney('bank', commission, 'realtor-commission')

    MySQL.update(
        'UPDATE properties SET owner = ?, price = ?, purchased_at = ? WHERE property_id = ?',
        {Buyer.PlayerData.citizenid, agreedPrice, os.time(), propertyId}
    )

    MySQL.update(
        'UPDATE property_listings SET status = "sold", buyer = ?, sold_at = ?, sold_price = ? WHERE property_id = ? AND status = "active"',
        {Buyer.PlayerData.citizenid, os.time(), agreedPrice, propertyId}
    )

    TriggerClientEvent('QBCore:Notify', src,
        'Sale complete! Commission: $' .. commission, 'success')
    TriggerClientEvent('QBCore:Notify', buyerId,
        'Congratulations! You now own ' .. property[1].label, 'success')

    TriggerEvent('doorlock:server:updateProperty', propertyId, Buyer.PlayerData.citizenid)
end)

Le taux de commission doit être configurable et potentiellement variable en fonction du niveau scolaire de l'agent. Les agents seniors peuvent gagner 7 % de commission tandis que les juniors ne gagnent que 3 %, ce qui les motive à progresser dans leur carrière dans le secteur immobilier. Suivez le volume total des ventes et les revenus de commissions de chaque agent dans la base de données pour créer des classements de performances et débloquer des bonus pour les plus performants.

Système hypothécaire et de financement

La plupart des acteurs ne peuvent pas se permettre des propriétés de grande valeur, c'est pourquoi un système hypothécaire augmente considérablement le bassin d'acheteurs potentiels et rend le marché immobilier accessible aux nouveaux acteurs. Lorsqu'un acheteur ne peut pas payer le prix total, proposez une option hypothécaire qui nécessite un pourcentage de mise de fonds configurable, généralement 10 à 20 % de la valeur de la propriété. Le solde restant est payé en versements réguliers, automatiquement déduits du compte bancaire du joueur à chaque redémarrage du serveur ou à intervalles programmés. Suivez les détails du prêt hypothécaire, notamment le montant principal, le taux d'intérêt, le solde restant, le montant du paiement mensuel et l'historique des paiements. Si un joueur manque trop de paiements consécutifs, déclenchez un processus de saisie qui reprend possession de la propriété et la remet en vente sur le marché à prix réduit. Le taux d'intérêt crée un coût d'emprunt réel qui encourage les joueurs à rembourser leur hypothèque plus tôt lorsque cela est possible, et le système génère une ponction constante sur la masse monétaire qui aide à lutter contre l'inflation sur l'économie des serveurs.

Revenus locatifs et système de propriétaire

Les joueurs qui possèdent plusieurs propriétés devraient pouvoir les louer pour un revenu passif, créant ainsi une dynamique propriétaire-locataire qui génère un jeu de rôle continu. Lorsqu'un propriétaire souhaite louer sa propriété, il fixe un montant de loyer mensuel et l'indique comme disponible à la location via l'agence immobilière ou une annonce directe. Les locataires bénéficient d'un accès clé à la propriété et peuvent utiliser son espace de stockage, sa garde-robe et son garage pendant toute la durée de leur bail. Le loyer est collecté automatiquement selon un calendrier configurable et déposé sur le compte bancaire du propriétaire. Voici la mise en œuvre du système de location :

Config.RentalSettings = {
    minRentPrice = 100,
    maxRentPrice = 50000,
    collectionInterval = 86400, -- every 24 hours real time
    maxMissedPayments = 3,
    depositMultiplier = 2, -- security deposit = 2x monthly rent
}

RegisterNetEvent('realestate:server:listForRent', function(propertyId, rentPrice)
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end

    local property = MySQL.query.await(
        'SELECT * FROM properties WHERE property_id = ? AND owner = ?',
        {propertyId, Player.PlayerData.citizenid}
    )
    if not property or not property[1] then return end

    if rentPrice < Config.RentalSettings.minRentPrice or
       rentPrice > Config.RentalSettings.maxRentPrice then
        return TriggerClientEvent('QBCore:Notify', src, 'Invalid rent price', 'error')
    end

    MySQL.insert(
        'INSERT INTO property_rentals (property_id, landlord, rent_price, deposit, listed_at) VALUES (?, ?, ?, ?, ?)',
        {propertyId, Player.PlayerData.citizenid, rentPrice,
         rentPrice * Config.RentalSettings.depositMultiplier, os.time()}
    )

    TriggerClientEvent('QBCore:Notify', src,
        'Property listed for rent at $' .. rentPrice .. '/day', 'success')
end)

-- Automated rent collection thread
CreateThread(function()
    while true do
        Wait(1000 * 60 * 60) -- Check every hour

        local rentals = MySQL.query.await(
            'SELECT * FROM property_rentals WHERE tenant IS NOT NULL AND status = "active"'
        )

        for _, rental in ipairs(rentals or {}) do
            if os.time() - rental.last_collected >= Config.RentalSettings.collectionInterval then
                local success = ProcessRentPayment(rental)
                if not success then
                    HandleMissedPayment(rental)
                end
            end
        end
    end
end)

Le dépôt de garantie protège les propriétaires des locataires qui endommagent la propriété ou partent sans préavis. Lorsqu'un locataire met fin à son bail, la caution est restituée déduction faite des éventuelles déductions pour dommages. Mettez en œuvre un système d’état de la propriété qui suit l’usure, donnant aux propriétaires une raison légitime de retenir une partie de la caution si le locataire n’a pas fait attention à la propriété. Cela crée des scénarios naturels de résolution de conflits qui stimulent le jeu de rôle entre propriétaires et locataires.

Tarification dynamique du marché

Un marché immobilier statique devient rapidement obsolète une fois que toutes les propriétés recherchées sont achetées. La tarification dynamique maintient le marché intéressant en ajustant la valeur des propriétés en fonction de l'activité réelle sur le serveur. Suivez plusieurs facteurs qui influencent la valeur du quartier : le nombre de ventes récentes dans une zone, la proximité d'endroits populaires comme les agences pour l'emploi et les magasins, les statistiques de criminalité du système de police et le ratio global de propriétés occupées par rapport aux propriétés vacantes. Lorsqu'un quartier connaît une forte demande avec de nombreux achats récents, la valeur des propriétés augmente progressivement, créant un élément spéculatif dont les premiers acheteurs des zones émergentes profitent à mesure que le quartier se développe. À l’inverse, les zones à forte criminalité ou les quartiers comptant de nombreuses propriétés vacantes voient leur valeur baisser. Exécutez l'ajustement du marché en tant que tâche serveur planifiée qui recalcule toutes les valeurs des propriétés chaque semaine, en appliquant les modifications progressivement pour éviter les chocs soudains du marché. Affichez les tendances du marché dans le panneau NUI de l'agence immobilière afin que les agents et les acheteurs puissent prendre des décisions éclairées sur où investir.

Visites de propriétés et interface NUI

L'interface NUI est l'endroit où les agents et les acheteurs parcourent les propriétés disponibles, examinent les détails et lancent des transactions. Créez une page de liste de propriétés propre et d'aspect professionnel qui affiche chaque propriété disponible avec sa photo ou son aperçu intérieur, son emplacement sur la carte, son prix, son nombre de pièces, la capacité du garage et l'évaluation du quartier. Incluez des options de filtrage et de tri afin que les utilisateurs puissent affiner les résultats par type de propriété, fourchette de prix et emplacement. Lorsqu'un agent sélectionne une propriété à afficher, le système doit afficher un itinéraire jusqu'à l'entrée de la propriété et informer l'acheteur de le suivre. À l'intérieur de la propriété, proposez un mode de visite interactive où l'agent peut mettre en évidence les caractéristiques à l'aide de marqueurs 3D ou d'annotations textuelles. Après la présentation, présentez à l'acheteur un panneau de confirmation d'achat ou de location qui montre la répartition financière complète, y compris le prix, les taxes, la commission de l'agent et les conditions hypothécaires, le cas échéant. L'interface devrait également inclure un onglet d'historique des transactions où les joueurs peuvent consulter leurs achats, ventes et paiements de location passés pour une transparence financière totale.

Personnalisation de la propriété et appréciation de la valeur

Laisser les propriétaires personnaliser leurs propriétés ajoute une touche personnelle et crée une économie de mise à niveau qui augmente la valeur des propriétés au fil du temps. Implémentez un système de placement de meubles qui permet aux joueurs d'acheter et de positionner des meubles à l'intérieur de leur propriété, de la même manière que GTA Online gère la personnalisation des appartements. Chaque meuble acheté augmente la valeur améliorée de la propriété, de sorte que lorsque le propriétaire vend finalement, il récupère une partie de son investissement dans l'ameublement. Au-delà du mobilier, proposez des améliorations structurelles telles que des rénovations de cuisine, des rénovations de salles de bains et des installations de systèmes de sécurité qui augmentent la valeur de base de la propriété d'un pourcentage. Suivez toutes les améliorations dans un tableau des modifications de propriété afin que la liste immobilière puisse montrer aux acheteurs potentiels exactement quelles améliorations ont été apportées. Les propriétés commerciales doivent prendre en charge des mises à niveau spécifiques à l'entreprise, telles que des vitrines pour un magasin, des équipements de cuisine pour un restaurant ou des racks de serveurs pour une entreprise technologique, liant ainsi le système immobilier à la boucle de jeu commerciale plus large. La combinaison de la dynamique du marché basée sur la localisation et des améliorations pilotées par les joueurs crée un riche écosystème immobilier dans lequel l'immobilier devient une stratégie légitime de création de richesse plutôt qu'un simple endroit pour stocker des objets et changer de vêtements.

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.