>
Tutoriel 2026-03-11

Synchronisation de la météo pour FiveM

TDYSKY

TDYSKY

Fondateur et développeur principal chez Agency Scripts

Le problème de la météo désynchronisée

Par défaut, GTA V gère la météo et l'heure indépendamment sur chaque client. Cela signifie qu'un joueur peut conduire sous un soleil radieux tandis qu'un autre joueur se tenant juste à côté de lui voit un orage. Pour les serveurs de jeu de rôle, cela rompt complètement l'immersion. Imaginez que tu essayies de coordonner une scène dans laquelle un personnage se plaint de la pluie tandis que l'écran d'un autre joueur affiche un ciel dégagé. Un système météo et horaire synchronisé garantit que chaque joueur sur le serveur vit le même environnement au même moment, ce qui est fondamental pour un jeu de rôle cohérent, des conditions de conduite réalistes et la coordination des événements. Dans ce didacticiel, nous allons créer un système complet de synchronisation météo avec une vitesse de temps configurable, des transitions saisonnières, un vote météo et des événements de panne d'électricité.

Gestion de l'état météorologique côté serveur

Le serveur est la seule source d’information sur la météo et l’heure. Il conserve le type de météo actuel, le type de météo suivant pour les transitions et l'horloge du jeu. Chaque client se synchronise sur cet état lors de son inscription et reçoit des mises à jour chaque fois que la météo change. GTA V prend en charge un ensemble spécifique de types de temps, notamment CLEAR, EXTRASUNNY, CLOUDS, OVERCAST, RAIN, THUNDER, CLEARING, FOGGY, SMOG, SNOW, SNOWLIGHT, BLIZZARD, et XMAS. Définissez des règles de transition logiques pour que le temps s'écoule naturellement plutôt que de passer du blizzard à un temps très ensoleillé sans état intermédiaire.

-- server.lua
local Config = {
    timeSpeed    = 2,        -- how many in-game minutes per real second
    syncInterval = 30000,    -- full sync broadcast every 30s
    autoChange   = true,     -- automatic weather cycling
    changeCooldown = 600,    -- seconds between automatic changes
}

local State = {
    weather     = 'CLEAR',
    nextWeather = 'CLOUDS',
    hour        = 12,
    minute      = 0,
    frozen      = false,
    blackout    = false,
    season      = 'summer',
}

local weatherTransitions = {
    EXTRASUNNY = {'CLEAR', 'CLOUDS'},
    CLEAR      = {'EXTRASUNNY', 'CLOUDS', 'OVERCAST'},
    CLOUDS     = {'CLEAR', 'OVERCAST', 'RAIN'},
    OVERCAST   = {'CLOUDS', 'RAIN', 'THUNDER', 'FOGGY'},
    RAIN       = {'OVERCAST', 'THUNDER', 'CLEARING'},
    THUNDER    = {'RAIN', 'OVERCAST'},
    CLEARING   = {'CLEAR', 'CLOUDS'},
    FOGGY      = {'CLEAR', 'CLOUDS', 'SMOG'},
    SMOG       = {'FOGGY', 'CLEAR'},
}

local seasonWeights = {
    summer = {EXTRASUNNY = 30, CLEAR = 30, CLOUDS = 20, RAIN = 10, THUNDER = 5, FOGGY = 5},
    winter = {CLOUDS = 20, OVERCAST = 20, SNOW = 25, SNOWLIGHT = 15, BLIZZARD = 10, FOGGY = 10},
    spring = {CLEAR = 25, CLOUDS = 25, RAIN = 20, CLEARING = 15, FOGGY = 10, OVERCAST = 5},
    autumn = {CLOUDS = 25, OVERCAST = 25, RAIN = 20, FOGGY = 15, CLEAR = 10, SMOG = 5},
}

Boucle de progression temporelle et de synchronisation

Le système de temps fonctionne comme une boucle côté serveur qui incrémente l'horloge du jeu en fonction de la vitesse configurée. UN timeSpeed de 2 signifie que chaque seconde du monde réel avance l'horloge du jeu de 2 minutes. Cela crée un cycle jour-nuit qui prend environ 12 minutes réelles pour 24 heures complètes de jeu. Le serveur diffuse l'heure actuelle et l'état météorologique à tous les clients à intervalles réguliers et envoie également des mises à jour immédiates lorsqu'un administrateur modifie la météo ou déclenche un événement de panne d'électricité. Les nouveaux joueurs rejoignant la mi-session reçoivent l'état actuel via un événement de synchronisation dédié afin qu'ils correspondent immédiatement à tous les autres.

-- Time progression loop (server.lua continued)
CreateThread(function()
    while true do
        Wait(1000)
        if not State.frozen then
            State.minute = State.minute + Config.timeSpeed
            if State.minute >= 60 then
                State.hour = State.hour + math.floor(State.minute / 60)
                State.minute = State.minute % 60
            end
            if State.hour >= 24 then
                State.hour = State.hour % 24
            end
        end
    end
end)

-- Periodic full sync
CreateThread(function()
    while true do
        Wait(Config.syncInterval)
        TriggerClientEvent('weather:fullSync', -1, State)
    end
end)

-- Sync new players on join
AddEventHandler('playerJoining', function()
    local src = source
    Wait(2000)
    TriggerClientEvent('weather:fullSync', src, State)
end)

Application météo côté client

Sur le client, tu dois remplacer les systèmes météorologiques et horaires natifs de GTA. Les principaux autochtones sont SetWeatherTypeNowPersist pour des changements de temps instantanés, SetWeatherTypeTransition pour un mélange en douceur entre deux états météorologiques, et NetworkOverrideClockTime pour régler l'horloge du jeu. Tu dois appeler NetworkOverrideClockTime chaque image pour empêcher le jeu de revenir à son propre calcul de temps. Pour les transitions météorologiques, GTA prend en charge un facteur de fusion compris entre 0,0 et 1,0 qui tu permet d'interpoler en douceur entre deux états météorologiques sur plusieurs secondes, créant ainsi des transitions réalistes où les nuages ​​arrivent progressivement avant que la pluie ne commence.

-- client.lua
local currentWeather = 'CLEAR'
local nextWeather    = 'CLEAR'
local weatherBlend   = 0.0
local hour, minute   = 12, 0
local blackout       = false

RegisterNetEvent('weather:fullSync', function(state)
    currentWeather = state.weather
    nextWeather    = state.nextWeather or state.weather
    hour           = state.hour
    minute         = state.minute
    blackout       = state.blackout or false
    applyWeather()
end)

function applyWeather()
    ClearOverrideWeather()
    ClearWeatherTypePersist()
    SetWeatherTypeNowPersist(currentWeather)
    if currentWeather ~= nextWeather then
        SetWeatherTypeTransition(
            GetHashKey(currentWeather),
            GetHashKey(nextWeather),
            weatherBlend
        )
    end
    if blackout then
        SetArtificialLightsState(true)
        SetArtificialLightsStateAffectsVehicles(false)
    else
        SetArtificialLightsState(false)
    end
end

-- Override clock every frame
CreateThread(function()
    while true do
        Wait(0)
        NetworkOverrideClockTime(hour, minute, 0)
    end
end)

Système de vote météo

Un système de vote météo permet à ta communauté de décider démocratiquement de la météo. Les joueurs peuvent voter pour leur type de météo préféré et, après une période de vote, l'option la plus populaire l'emporte. Cela fonctionne bien pour les événements où tu souhaites l'engagement des joueurs, comme décider si une compétition automobile se déroule sous un ciel clair ou sous des orages dramatiques. Limitez le vote à une fois par cycle pour éviter le spam et affichez le décompte actuel des votes via ton système de notification ou une simple superposition NUI. Le serveur collecte les votes, les compte à l'expiration de la période et applique la météo gagnante via une transition en douceur afin que tous les joueurs expérimentent le changement simultanément.

-- Weather voting (server.lua)
local votes = {}
local voteCooldown = {}
local votingOpen = false
local voteOptions = {'CLEAR', 'RAIN', 'THUNDER', 'FOGGY', 'SNOW'}

RegisterCommand('voteweather', function(source, args)
    if not votingOpen then
        TriggerClientEvent('chat:addMessage', source, {args = {'Weather', 'No vote is currently active.'}})
        return
    end
    if voteCooldown[source] then
        TriggerClientEvent('chat:addMessage', source, {args = {'Weather', 'You already voted this round.'}})
        return
    end
    local choice = string.upper(args[1] or '')
    local valid = false
    for _, opt in ipairs(voteOptions) do
        if opt == choice then valid = true break end
    end
    if not valid then
        TriggerClientEvent('chat:addMessage', source, {
            args = {'Weather', 'Options: ' .. table.concat(voteOptions, ', ')}
        })
        return
    end
    votes[choice] = (votes[choice] or 0) + 1
    voteCooldown[source] = true
    TriggerClientEvent('chat:addMessage', -1, {
        args = {'Weather', GetPlayerName(source) .. ' voted for ' .. choice}
    })
end, false)

function startWeatherVote()
    votes = {}
    voteCooldown = {}
    votingOpen = true
    TriggerClientEvent('chat:addMessage', -1, {
        args = {'Weather', 'Weather vote started! Use /voteweather [type]. Options: ' .. table.concat(voteOptions, ', ')}
    })
    SetTimeout(60000, function()
        votingOpen = false
        local winner, maxVotes = 'CLEAR', 0
        for weather, count in pairs(votes) do
            if count > maxVotes then winner = weather; maxVotes = count end
        end
        transitionWeather(winner)
        TriggerClientEvent('chat:addMessage', -1, {
            args = {'Weather', 'Vote ended! Weather changing to: ' .. winner}
        })
    end)
end

Système saisonnier et effets météorologiques sur le gameplay

Un système saisonnier ajoute une couche de profondeur que la plupart des serveurs négligent. En suivant le calendrier du jeu ou en mappant les mois du monde réel aux saisons, tu peux pondérer les probabilités météorologiques en conséquence. L'été favorise un temps clair et ensoleillé avec des orages occasionnels, tandis que l'hiver fait pencher la balance vers la neige, le ciel couvert et le brouillard. Les saisons peuvent également affecter les mécanismes de jeu au-delà des seuls visuels. Par temps de pluie, tu peux réduire la traction du véhicule en déclenchant un modificateur de tenue de route sur tous les véhicules actifs. Le brouillard pourrait réduire la distance de rendu pour les robots IA, rendant le jeu furtif plus viable. La neige pourrait ralentir légèrement la vitesse de déplacement des joueurs et nécessiter des vêtements d'hiver pour éviter une lente perte de santé. Ces petites touches créent un monde immersif qui semble vivant et réactif à son environnement.

Événements de panne et contrôles d'administration

Les événements Blackout sont un outil puissant pour les événements de serveur et les scénarios de jeu de rôle. Le SetArtificialLightsState native désactive toutes les sources de lumière artificielle du monde du jeu, plongeant la ville dans l’obscurité. Combiné avec un temps couvert ou brumeux, cela crée un environnement incroyablement atmosphérique, parfait pour les événements d'horreur, les scénarios de braquage ou les jeux de rôle de survie. Les commandes d'administration doivent fournir un contrôle total sur le système météo : temps de gel, définition d'heures spécifiques, forçage des types de météo, déclenchement de pannes d'électricité et démarrage des votes météorologiques. Protégez ces commandes avec des autorisations as afin que seul le personnel autorisé puisse modifier l'environnement. Un système météo bien conçu devient un outil de narration que les administrateurs utilisent pour créer l'ambiance de chaque événement et scénario sur ton serveur.

-- Admin commands (server.lua)
RegisterCommand('setweather', function(source, args)
    if source > 0 and not IsPlayerAceAllowed(source, 'command.setweather') then return end
    local weather = string.upper(args[1] or 'CLEAR')
    transitionWeather(weather)
    TriggerClientEvent('chat:addMessage', -1, {
        args = {'Admin', 'Weather changed to ' .. weather}
    })
end, true)

RegisterCommand('settime', function(source, args)
    if source > 0 and not IsPlayerAceAllowed(source, 'command.settime') then return end
    State.hour   = tonumber(args[1]) or 12
    State.minute = tonumber(args[2]) or 0
    TriggerClientEvent('weather:fullSync', -1, State)
end, true)

RegisterCommand('blackout', function(source, args)
    if source > 0 and not IsPlayerAceAllowed(source, 'command.blackout') then return end
    State.blackout = not State.blackout
    TriggerClientEvent('weather:fullSync', -1, State)
    TriggerClientEvent('chat:addMessage', -1, {
        args = {'Admin', 'Blackout ' .. (State.blackout and 'enabled' or 'disabled')}
    })
end, true)

RegisterCommand('freezetime', function(source, args)
    if source > 0 and not IsPlayerAceAllowed(source, 'command.freezetime') then return end
    State.frozen = not State.frozen
    TriggerClientEvent('chat:addMessage', -1, {
        args = {'Admin', 'Time ' .. (State.frozen and 'frozen' or 'unfrozen')}
    })
end, true)

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.