Explorer
KNOW-PAT-096

FiveM - Weather/Time Sync System : OpenWeatherMap API, smooth transition, pause sync

Domaine
fivem
Type
pattern
Priorité
P2

Parent : [[INDEX-FIVEM-GAMEPLAY]]

FiveM - Weather/Time Sync System : OpenWeatherMap API, smooth transition, pause sync

Problème

La météo et l'heure doivent être synchronisées entre tous les joueurs. Une transition brutale (jour → nuit instantané) est immersion-breaking. Certains events (photo mode, intérieur) nécessitent de figer temporairement la météo.

Solution

Serveur fetch météo réelle via OpenWeatherMap, client smooth transition avec interpolation, pause sync pour les events spéciaux, blackout et tsunami countdown support.

-- server.lua
function GetRealWorldData(city)
    local data = {}
    PerformHttpRequest(
        'https://api.openweathermap.org/data/2.5/weather?q='..city..'&appid='..Config.APIKey..'&units=metric',
        function(error, result, header)
            if error == 200 then
                local result = json.decode(result)
                for c, d in pairs(Config.Weather.RealWeather.weather_types) do
                    for cd = 1, #d do
                        if d[cd] == result.weather[1].id then
                            data.weather = c
                            data.info = {
                                weather = result.weather[1].main,
                                weather_description = result.weather[1].description,
                                country = result.sys.country,
                                city = result.name,
                            }
                            break
                        end
                    end
                end
                RealTimezone = result.timezone
                data.hours, data.mins = GetRealTime()
            else
                print('^1Error: Invalid City Name or API Key.^0')
            end
        end,
        'GET', '', {["Content-Type"] = 'application/json'}
    )
    
    -- Attente non-bloquante avec timeout
    local timeout = 0
    while not data.weather and timeout <= 100 do
        Wait(0)
        timeout = timeout + 1
    end
    return data
end

-- client.lua : Smooth transition
RegisterNetEvent('cd_easytime:ForceUpdate')
AddEventHandler('cd_easytime:ForceUpdate', function(data, source)
    if not PauseSync.state then
        if data.weather ~= nil and data.weather ~= self.weather then
            CheckSnowSync(data.weather)
            ChangeWeather(data.weather, data.instantweather)
            self.weather = data.weather
        end
        
        if (data.hours ~= nil and data.hours ~= self.hours) or
           (data.mins ~= nil and data.mins ~= self.mins) then
            if not data.instanttime then
                SmoothChangeTime(data, 1)
                self.hours = data.hours
                self.mins = data.mins
            else
                forcedInstantTimeUpdate = true
                self.hours = data.hours
                self.mins = data.mins
                NetworkOverrideClockTime(self.hours, self.mins, 0)
                Wait(1000)
                forcedInstantTimeUpdate = false
            end
        end
    end
end)

-- Pause sync (ex: photo mode)
RegisterNetEvent('cd_easytime:PauseSync')
AddEventHandler('cd_easytime:PauseSync', function(boolean, hours)
    if boolean == PauseSync.state then return end
    if boolean then
        PauseSync.state = true
        ChangeWeather('EXTRASUNNY', true)
        while PauseSync.state do
            Wait(0)
            NetworkOverrideClockTime(hours or 20, 0, 0)
        end
    else
        PauseSync.state = false
        Wait(300)
        TriggerServerEvent('cd_easytime:SyncMe_basics', { weather = true, time = true })
    end
end)

Pourquoi

  • PerformHttpRequest est asynchrone → timeout pour éviter le blocage
  • SmoothChangeTime évite les sauts visuels brutaux
  • PauseSync permet aux scripts (photo, cinéma) de contrôler l'environnement

Quand l'utiliser

  • Serveurs RP souhaitant météo réaliste synchronisée
  • Events narratifs nécessitant des conditions météo spécifiques

Quand NE PAS l'utiliser

  • Serveurs deathmatch où la météo n'a pas d'importance

Exemples

Correct

-- SmoothChangeTime avec interpolation
default_time_scale = 1.0
function SmoothChangeTime(targetData, speed)
    local currentHours = GetClockHours()
    local currentMins = GetClockMinutes()
    local targetHours = targetData.hours
    local targetMins = targetData.mins
    
    while currentHours ~= targetHours or currentMins ~= targetMins do
        Wait(10 / speed)
        currentMins = currentMins + 1
        if currentMins >= 60 then
            currentMins = 0
            currentHours = (currentHours + 1) % 24
        end
        NetworkOverrideClockTime(currentHours, currentMins, 0)
    end
end

Incorrect

-- Transition instantanée → immersion breaking
RegisterNetEvent('weather:set')
AddEventHandler('weather:set', function(weather, hour)
    SetWeatherTypeNow(weather)
    NetworkOverrideClockTime(hour, 0, 0)
end)

Références

  • cd_easytime/server/server.lua L40-67
  • cd_easytime/client/client.lua L36-60