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
PerformHttpRequestest 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