Skip to content

Developer API

Read exports, client exports, trusted write exports, events with payloads, and GlobalState.

Updated 2026-07-02

On this page

Everything here works across the escrow boundary - you integrate through exports, events, and GlobalState, never by editing the resource. Working starting points for a HUD, phone app, vehicle traction, fishing, interiors, and a store hook ship in the resource's examples/ folder.

The 60-second version

Three ways to read the sky - pick whichever fits:

-- 1. Ask for everything at once (server)
local snap = exports['dzr-environment']:GetEnvironmentSnapshot()
if snap.surface == 'slick' then --[[ wet roads ]] end

-- 2. React when it changes instead of asking (server or client)
AddEventHandler('dzr-environment:server:weatherChanged', function(data)
    -- data.current, data.surface, data.snapshot ...
end)

-- 3. Read the shared state directly - no export call at all (anywhere)
local env = GlobalState.environment   -- env.weather, env.hour, env.temperature ...

Conventions:

  • Read exports are safe to call from any server resource.
  • Write exports are for trusted server scripts. They return success, reason. Never wire one to a raw client event without your own permission check.
  • All weather type strings are GTA-native uppercase: EXTRASUNNY, CLEAR, CLOUDS, OVERCAST, RAIN, THUNDER, CLEARING, FOGGY, SMOG.

Server read exports

local E = exports['dzr-environment']

-- Time
E:GetHour()             --> number 0-23
E:GetMinute()           --> number 0-59
E:GetTime()             --> { hour, minute }
E:IsDay()               --> boolean
E:IsNight()             --> boolean

-- Weather
E:GetWeather()          --> string, e.g. 'RAIN'
E:IsRaining()           --> boolean (RAIN or THUNDER)
E:GetWeatherIntensity() --> number 0.3 | 0.6 | 0.9
E:GetWeatherTypes()     --> string[] (all valid types)

-- Derived conditions
E:GetTemperature()      --> { fahrenheit, celsius }
E:GetHeatLevel()        --> 'cool' | 'normal' | 'hot' | 'scorching'
E:GetVisibilityLevel()  --> 'clear' | 'reduced' | 'poor'
E:GetSurfaceCondition() --> 'dry' | 'damp' | 'slick'
E:GetWindLevel()        --> 'calm' | 'light' | 'moderate' | 'strong' | 'severe'
E:GetRainDuration()     --> number (minutes of accumulated rain this session)

-- Full snapshot (one call for everything)
E:GetEnvironmentSnapshot()

-- Forecast / transition
E:GetForecast()         --> { weather, eta = 'soon'|'later'|'distant', conditions }
E:GetTransitionInfo()   --> { nextWeather, possibleNext, remainingMinutes, ... }

-- Locks / blackout (read)
E:GetTimeLock()         --> boolean
E:GetWeatherLock()      --> boolean
E:GetBlackout()         --> boolean

GetEnvironmentSnapshot() shape:

{
  time        = { hour = 14, minute = 32 },
  weather     = 'RAIN',
  intensity   = 0.6,
  temperature = { fahrenheit = 62, celsius = 17 },
  heat        = 'normal',
  visibility  = 'reduced',
  surface     = 'damp',
  wind        = 'moderate',
}

Client exports

local E = exports['dzr-environment']

-- Sanitized, display-ready, respects Config.TemperatureUnit
E:GetWeatherInfo()  --> { time, weather, intensity,
                    --    temperature = { value, unit, fahrenheit, celsius },
                    --    heat, visibility, surface, wind }
                    --    (nil before the first sync)

-- Pause this resource's LOCAL application of time/weather (cinematics,
-- character select). Visual only - it does NOT stop server authority.
-- Resume must match the reason tag.
E:SuppressSync('charselect')
E:ResumeSync('charselect')

Local overrides (client-visual only)

Show THIS player a different sky or clock - interiors, apartments, character select, cutscenes - without changing the world for anyone else. These never trigger a server event or mutate server state. They are reason-tagged and reference-counted (latest active wins per axis); a 120-second timeout auto-clears an orphaned override.

local E = exports['dzr-environment']

E:SetLocalWeather('FOGGY', 'interior:apartment42')  --> boolean (false if invalid weather/reason)
E:SetLocalTime(2, 30, 'cutscene:intro')             --> boolean (hour 0-23, minute 0-59)
E:ClearLocalOverride('interior:apartment42')        -- restores the live server view smoothly
E:HasLocalOverride()                                --> boolean

-- Example: foggy 1am inside an apartment, restored on exit
AddEventHandler('myapartments:entered', function()
    E:SetLocalWeather('FOGGY', 'apt')
    E:SetLocalTime(1, 0, 'apt')
end)
AddEventHandler('myapartments:exited', function()
    E:ClearLocalOverride('apt')
end)

SetLocalWeather overrides only weather; SetLocalTime overrides only the clock - mix and match. They are independent of SuppressSync (if both are active, SuppressSync's full pause wins).

Server write exports (trusted only)

Each returns success: boolean, reason: string|nil.

local ok, reason = exports['dzr-environment']:SetWeather('RAIN')
if not ok then print('failed:', reason) end

E:SetTime(hour)                      -- 0-23, instant snap
E:SetTime(hour, true)                -- cinematic: the clock RUNS to the hour over ~8-30s
E:SetTime(hour, true, true)          -- cinematic, rewinding backward instead
E:SetWeather(type)                   -- a valid weather string
E:SetWeatherIntensity('light')       -- 'light' | 'normal' | 'heavy' (or 0.3 / 0.6 / 0.9)
E:RandomizeEnvironment('all')        -- 'all' | 'time' | 'weather'
E:SetTimeRatio(ratio)                -- 0.1-100 game minutes per real minute
E:SetTimeLock(bool)
E:SetWeatherLock(bool)
E:SetBlackout(bool)

Failure reasons you can get back: control disabled (Config.ControlTime / ControlWeather is false), invalid input, or no change. The lock and blackout setters return false, 'no change' when the state is already what you asked for - the desired end state is still in effect.

Warning

Write exports are meant for trusted server-side scripts. Never expose one straight to a client event without your own permission check.

Events

Fired when Config.BroadcastEvents is true. Each event has a server-local variant (dzr-environment:server:*, handle with AddEventHandler) and a client broadcast variant (dzr-environment:client:*, handle with RegisterNetEvent). Payloads are self-sufficient - you never need to call an export in response.

weatherChanged

{
  previous    = 'CLEAR',
  current     = 'RAIN',
  intensity   = 0.6,
  temperature = { fahrenheit = 62, celsius = 17 },
  visibility  = 'reduced',
  surface     = 'damp',
  wind        = 'moderate',
  snapshot    = { ... },   -- full GetEnvironmentSnapshot shape
}

hourChanged

Fires on a natural hour rollover.

{ previousHour = 13, currentHour = 14, minute = 0,
  isDay = true, isNight = false, snapshot = { ... } }

blackoutChanged

{ enabled = true, previous = false }

The automation events (overrideStarted, overrideEnded, presetApplied, notice, audit) are documented on the automation page.

GlobalState (read-only)

GlobalState.environment is published to every client and readable on both sides without an export call:

local env = GlobalState.environment
-- env.hour, env.minute, env.weather, env.intensity, env.rainDuration
-- env.baseTimeRatio       -- admin-chosen day-length ratio
-- env.realTimeRatio       -- live ratio, temporarily higher during smooth transitions
-- env.lockTime, env.lockWeather
-- env.blackout
-- env.temperature = { fahrenheit, celsius }
-- env.wind
-- env.transition = { nextWeather, weatherRemaining }

Contract: authoritative inputs plus stable convenience-derived values. For exact local behavior you may recompute derived values yourself, but the published ones are kept in sync.

Need help with this? Open a ticket on Discord or read the support guide.