API Reference

Developer reference for oxide-weather — the o-link weather namespace, server and client exports, write permissions, return shapes, events, state bags and integration examples.

This page is for developers writing code against oxide-weather. Server owners don't need anything on this page.

Two Ways In

local olink = exports['o-link']:olink()
local weather = olink.weather.GetWeather()

Write against the weather namespace and your script keeps working if the server owner ever swaps weather resources. o-link ships adapters for qb-weathersync, cd_easytime, Renewed-Weathersync, night_natural_disasters, and a native fallback, so the read functions those providers can answer will still answer.

oxide-weather is the only provider that implements the full namespace. Gate anything beyond the basics:

if olink.weather.GetResourceName() == 'oxide-weather' then
    local conditions = olink.weather.GetConditionsAt(coords)
end

Minimum o-link version: 1.8.0. The server-side weather namespace was added in 1.7.0, but this resource also loads the shared map files that o-link first ships in 1.8.0, so it does not start on anything older.

Direct exports

local weather = exports['oxide-weather']:GetWeather()

Faster to write, but ties your resource to oxide-weather specifically.

Permissions for Writes

Every read export is open to any resource. Every write export is gated twice:

  1. Resource allowlist. The calling resource must be listed in Config.SetterResources on the server. Calls from anywhere else return false, 'Calling resource is not allowed to change weather' and are logged. See Configuration → Trusted resources.
  2. Admin check, when a player is involved. Every write export takes an optional trailing actor parameter, the player's server id. When you pass one, that player must pass olink.framework.IsAdmin. Pass nil for a system-driven change with no player behind it.

The calling resource's identity is taken from the engine (GetInvokingResource). It cannot be supplied or spoofed by the caller, whether you call directly or through o-link.

-- server, from a resource listed in Config.SetterResources
local ok, reason = exports['oxide-weather']:SetWeather('THUNDER', nil)
if not ok then print(reason) end

Writes are also refused, with a reason, while a holiday preset is active or while a global scene lock is held. While the real-world weather mirror is driving the weather, SetWeather, SetDynamicWeather, SetWeatherInterval, SetForecast, their zone variants and SpawnFront are refused too, and fronts are paused.

Common Return Shapes

Conditions

Returned by GetConditionsAt on both sides.

{
    weather     = 'RAIN',                        -- the dominant weather at that point
    weights     = { RAIN = 0.7, CLOUDS = 0.3 },  -- blend of every contributing weather, sums to 1
    zone        = 'city',                        -- region id, or 'global'
    rain        = 0.7,                           -- 0-1, share of RAIN + THUNDER
    snow        = 0.0,                           -- 0-1, share of snow weathers
    raining     = true,                          -- rain >= 0.1
    wind        = { speed = 3.2, direction = 218.4 },  -- m/s, degrees
    temperature = 14.2,                          -- Celsius. nil when seasons are disabled
    snowLevel   = 0.0,                           -- 0-1 ground cover
    season      = 'autumn',                      -- nil when seasons are disabled
    blackout    = false,
}

WeatherData

Returned by GetWeatherData and GetZoneWeather-adjacent calls.

{
    current = {
        weather     = 'OVERCAST',
        time        = { hour = 14, minute = 37 },
        zone        = 'city',
        label       = 'Los Santos',
        temperature = 12.4,        -- present when seasons are enabled
        snowLevel   = 0.0,
        season      = 'autumn',
        baseWeather = 'CLOUDS',    -- client GetWeatherData only: present when a front is overriding the region
        front       = { id, weather, strength, windStrength, phase, coords },  -- client GetWeatherData only. phase is 'weather' or 'approaching'
    },
    forecast = {
        {
            weather     = 'RAIN',
            startsIn    = 8.4,                        -- real minutes from now
            gameTime    = { hour = 18, minute = 12 }, -- in-game time it lands
            temperature = 9.8,
            season      = 'autumn',
            baseWeather = 'RAIN',   -- client GetWeatherData only
        },
        -- ...up to Config.ForecastLength entries
    },
    settings = {
        dynamicWeather   = true,
        weatherInterval  = 15,      -- real minutes
        timeScale        = 30,
        timeFrozen       = false,
        blackout         = false,
        clock            = { mode = 'game', nightScale = 30, nightStart = 21, nightEnd = 6, utcOffset = 0 },  -- global data only
        forecastRevision = 12,
        temperatureUnit  = 'F',     -- display only. Exports always return Celsius
        season           = 'autumn',
        seasonMode       = 'game',  -- 'game' | 'calendar' | 'manual' | 'holiday'
        holiday          = nil,
        clockOffset      = nil,     -- per-region clock offset in game minutes, region data only
        broadcast        = { ... }, -- the live public weather settings, global data only
        realWorld        = nil,     -- { enabled, active, stale, station, attribution } while the real-world mirror is on, global data only
    },
    wind = { speed = 3.2, direction = 218.4 },
}

forecast is empty when automatic weather is off for that region.

Front

{
    id         = 'front-3',
    weather    = 'THUNDER',
    coords     = { x = -5000.0, y = 2800.0, z = 45.0 },  -- where it spawned
    position   = { x = -1420.5, y = 2800.0, z = 45.0 },  -- where it is now
    heading    = 90.0,      -- compass bearing
    speed      = 12.0,      -- m/s
    radius     = 900.0,
    blendWidth = 350.0,
    windLead   = 500.0,
    windSpeed  = 6.0,
    duration   = 1200.0,    -- seconds
    fadeSeconds = 20.0,
    priority   = 0,
    age        = 184000,    -- milliseconds since spawn
}

Alert

{
    id       = 'weather-1757000000-0-4',
    zone     = 'paleto',
    region   = 'Paleto Bay',
    weather  = 'THUNDER',
    severity = 3,                    -- 1 advisory, 2 watch, 3 warning
    status   = 'active',             -- 'upcoming' | 'active' | 'ended'
    label    = 'Thunderstorm',
    title    = 'Thunderstorm warning',
    advice   = 'Lightning and heavy rain are expected...',
    message  = 'Paleto Bay · until 04:15 tomorrow. Lightning and heavy rain...',
    window   = 'until 04:15 tomorrow',
    startsIn = 0,                    -- real seconds
    endsIn   = 620,                  -- real seconds. nil means "until conditions change"
    startTime = { hour = 3, minute = 5, dayOffset = 0 },   -- in-game
    endTime   = { hour = 4, minute = 15, dayOffset = 1 },
    source   = 'front',              -- 'region' | 'forecast' | 'front' | 'scene'
    revision = 2,                    -- bumps when the alert meaningfully changes
    issuedAt = 1757000000,           -- unix seconds
    updatedAt = 1757000420,
}

Server Exports

Status

GetApiVersion

local version = exports['oxide-weather']:GetApiVersion()   -- 2

Returns: number — the API contract version. Currently 2. Check it before relying on a shape.


IsReady

if exports['oxide-weather']:IsReady() then ... end

Returns: booleantrue once the simulation has finished loading and restoring. Before this is true, read exports return an empty or default value: nil for single values, {} for lists and false for flags.


Point queries

All coordinate parameters accept a vector3 or a plain { x = , y = , z = } table. z defaults to 0 when omitted. Coordinates outside -20000..20000 on x/y return nil.

GetConditionsAt

local data = exports['oxide-weather']:GetConditionsAt(vector3(215.0, -810.0, 31.0))

Parameters:

NameTypeDescription
coordsvector3 or tableThe point to sample

Returns: table — a Conditions table, or nil if not ready or the coordinates are invalid.


GetWeatherAt

local weather = exports['oxide-weather']:GetWeatherAt(coords)   -- 'THUNDER'

Returns: string — the dominant weather at that point, including any front overriding it. nil if not ready.


GetWindAt

local wind = exports['oxide-weather']:GetWindAt(coords)
-- { speed = 3.2, direction = 218.4 }

Returns: table with speed (m/s) and direction (degrees), or nil.


IsRainingAt

if exports['oxide-weather']:IsRainingAt(coords) then ... end

Returns: booleantrue when the rain share at that point is at least 0.1. nil if not ready.


IsSnowOnGround

local snowy = exports['oxide-weather']:IsSnowOnGround(coords)   -- coords optional

Parameters:

NameTypeDescription
coordsvector3 or tableOptional. Without it, the global snow level is used

Returns: booleantrue when snow cover exceeds Config.Climate.Snow.GroundThreshold. Without seasons, the level is 1 under snowy weather and 0 otherwise. nil when nothing is ready, or when called without coordinates while seasons are disabled.


GetTemperatureAt

local celsius = exports['oxide-weather']:GetTemperatureAt(coords)   -- 12.4

Returns: number — degrees Celsius, always, regardless of the display unit setting. nil when seasons are disabled.


GetSnowLevelAt

local level = exports['oxide-weather']:GetSnowLevelAt(coords)   -- 0.0 to 1.0

Returns: number — ground cover from 0 to 1. nil when seasons are disabled.


GetZoneAt

local zone = exports['oxide-weather']:GetZoneAt(coords)   -- 'city'

Returns: string — the region id, or 'global' when regions are disabled or the point is in no region. nil when the coordinates are invalid.


GetRoadConditionsAt

local road = exports['oxide-weather']:GetRoadConditionsAt(coords)
-- { wetness = 0.42, ice = 0.0, cellSize = 500 }

Returns: table with wetness (0-1), ice (0-1) and cellSize (metres), or nil when the point is outside the simulated bounds.


Global state

GetWeather

local weather = exports['oxide-weather']:GetWeather()   -- 'CLEAR'

Returns: string — the global weather. Regions and fronts may differ locally. Use GetWeatherAt for a specific point.


GetTime

local time = exports['oxide-weather']:GetTime()   -- { hour = 14, minute = 37 }

Returns: table with hour (0-23) and minute (0-59).


GetTimeScale

local scale = exports['oxide-weather']:GetTimeScale()   -- 30

Returns: number — game minutes per real minute during the day.


IsTimeFrozen

local frozen = exports['oxide-weather']:IsTimeFrozen()

Returns: boolean.


IsBlackout

local dark = exports['oxide-weather']:IsBlackout()

Returns: boolean — the global blackout state. Lightning outages are always regional, so use IsZoneBlackout or GetBlackouts to see those.


IsDynamicWeather

local auto = exports['oxide-weather']:IsDynamicWeather()

Returns: boolean.


GetWeatherInterval

local minutes = exports['oxide-weather']:GetWeatherInterval()   -- 15

Returns: number — real minutes between global weather changes.


GetForecast

local forecast = exports['oxide-weather']:GetForecast()

Returns: table — the global forecast, a list of entries as described in WeatherData. Empty when automatic weather is off.


GetWeatherData

local data = exports['oxide-weather']:GetWeatherData()          -- global
local paleto = exports['oxide-weather']:GetWeatherData('paleto') -- one region

Parameters:

NameTypeDescription
zonestringOptional. A region id, or 'global'. Defaults to global

Returns: WeatherData, or nil when the region does not exist or the resource is not ready.


Regions

GetZones

local zones = exports['oxide-weather']:GetZones()

Returns: table — a list of region definitions, sorted by id:

{
    { id = 'city', label = 'Los Santos', climate = 'temperate', color = '#57c6c5',
      enabled = true, priority = 0, blendWidth = 200.0,
      dynamicWeather = true, weatherInterval = 15,
      wind = { speed = 1.5, direction = 225.0 },
      polygon = { points = { { x = -3200, y = -3800 }, ... } } },
    -- ...
}

The configured regions are returned even when regional weather is switched off. Check GetSetting('Regional').Enabled or the enabled field of GlobalState['oxide:weatherZones'] before treating them as active.


GetZoneWeather

local weather = exports['oxide-weather']:GetZoneWeather('sandy')   -- 'EXTRASUNNY'

Returns: string, or nil when the region does not exist.


GetZoneForecast

local forecast = exports['oxide-weather']:GetZoneForecast('sandy')

Returns: table — that region's forecast list. Empty when the region has automatic weather off.


Season and climate

GetSeason

local season = exports['oxide-weather']:GetSeason()   -- 'winter'

Returns: string'spring', 'summer', 'autumn' or 'winter'. nil when seasons are disabled.


GetClimateData

local climate = exports['oxide-weather']:GetClimateData()
-- { season = 'winter', mode = 'game', day = 3, daysPerSeason = 7,
--   holiday = nil, temperatureUnit = 'F' }

Returns: table, or nil when seasons are disabled. mode is 'game', 'calendar', 'manual' or 'holiday'. day and daysPerSeason are only present in game mode with no holiday running.


GetTemperature

local celsius = exports['oxide-weather']:GetTemperature()           -- global
local paleto  = exports['oxide-weather']:GetTemperature('paleto')   -- one region

Returns: number — degrees Celsius at that region's reference point. nil when seasons are disabled.


GetSnowLevel

local level = exports['oxide-weather']:GetSnowLevel()           -- global
local alps  = exports['oxide-weather']:GetSnowLevel('chiliad')  -- one region

Returns: number — 0 to 1. nil when seasons are disabled.


Fronts and blackouts

GetFronts

local fronts = exports['oxide-weather']:GetFronts()

Returns: table — a list of Front tables, sorted by priority then id. Empty when fronts are disabled.


IsZoneBlackout

local dark = exports['oxide-weather']:IsZoneBlackout('paleto')

Parameters:

NameTypeDescription
zonestringA region id, or 'global'

Returns: boolean, or nil when the region does not exist.


GetBlackouts

local blackouts = exports['oxide-weather']:GetBlackouts()
-- {
--   overrides = { paleto = true },
--   outages   = { { id = 'blackout-2', zone = 'city', source = 'scheduled',
--                   startsIn = 480000, endsIn = 2280000 } },   -- milliseconds
-- }

Returns: table with overrides (region id to boolean) and outages (times in milliseconds). source is 'scheduled' for ScheduleBlackout, 'manual' for a timed SetZoneBlackout and 'storm' for a lightning strike.


GetRealWorldWeather

local mirror = exports['oxide-weather']:GetRealWorldWeather()
-- {
--   revision = 8, enabled = true, active = true, stale = false, exhausted = false,
--   location = 'Paris, FR', pollMinutes = 15, syncClock = false,
--   station = { name = 'Paris, Ile-de-France, France', latitude = 48.85, longitude = 2.35,
--               timezone = 'Europe/Paris', utcOffset = 7200 },
--   weather = 'RAIN', code = 63, temperature = 14.2, humidity = 81, isDay = true,
--   wind = { speed = 4.1, direction = 230 },
--   observedAt = 1789200900, fetchedAt = 1789201013, nextPollIn = 612,
--   lastError = nil, attribution = 'Weather data by Open-Meteo.com',
-- }

Returns: table describing the real-world weather mirror, or nil before the simulation is ready.

  • active is true while the mirror owns the weather; enabled alone means it is switched on but has nothing to show yet.
  • revision goes up whenever any of this changes.
  • code is the raw WMO weather code and weather its mapped type.
  • Times are UTC epoch seconds and nextPollIn is seconds.
  • utcOffset is the station's offset in seconds, which is what the clock mirror uses.
  • Show attribution next to the data if you display it.

Alerts and reports

GetWeatherAlerts

local alerts = exports['oxide-weather']:GetWeatherAlerts()          -- every region
local local_ = exports['oxide-weather']:GetWeatherAlerts('paleto')  -- one region

Parameters:

NameTypeDescription
zonestringOptional. A region id, or 'global'

Returns: table — a list of active Alert tables, most severe first. On a bad region id, returns nil, reason.


GenerateWeazelReport

local report, reason = exports['oxide-weather']:GenerateWeazelReport(nil, { unit = 'C' })
if report then print(report.text) end

Parameters:

NameTypeDescription
zonestringOptional. A region id to report on. Omit for every region
optionstableOptional. { unit = 'C' or 'F' }. Defaults to the server's display unit

Returns:

{
    headline   = 'Weazel Weather · 14:37',
    text       = 'Weazel Weather · 14:37\nLos Santos: OVERCAST 12.4°C, wind 3.2 m/s at 218\n...',
    generatedAt = 1757000000,          -- unix seconds
    gameTime   = { hour = 14, minute = 37 },
    season     = 'autumn',
    unit       = 'C',
    zones      = { 'city', 'paleto', ... },
    alerts     = { <Alert>, ... },     -- active alerts included in the text
}

Returns nil, reason when the public weather service is turned off or the region is unknown.

text is a ready-to-print bulletin with a headline, one line per region with conditions, temperature and wind, an outlook line, and every active alert. Use text for a quick print, or build your own layout from the structured fields.


Scene locks

GetSceneLocks

local locks = exports['oxide-weather']:GetSceneLocks()
-- { { target = 'global', owner = 'admin', weather = 'THUNDER',
--     time = { hour = 3, minute = 5 }, remaining = 412.0 } }   -- seconds

Returns: table — a list of active locks, sorted by target. target is 'global' or a player server id as a string. owner is the resource that requested it, or 'admin' for a command.


Exposure

GetExposureAt

local cover = exports['oxide-weather']:GetExposureAt(coords, 0)
-- { known = true, covered = false, bucket = 0, age = 4.2 }

Parameters:

NameTypeDescription
coordsvector3 or tableThe point to check
bucketnumberOptional routing bucket. Defaults to 0

Returns: table:

FieldTypeMeaning
knownbooleanWhether an answer is available yet
coveredbooleanPresent when known is true
bucketnumberThe bucket the answer applies to
agenumberSeconds since the answer was measured
swimmingbooleanWhether the player is swimming. Always false for a plain coordinate

nil when the coordinates or bucket are invalid, or before the simulation is ready. Points inside one of your configured covered areas answer { known = true, covered = true, bucket } at once, with no age.

The first call for a new point almost always returns { known = false }. The point is queued and a nearby player's game is asked to check it. Poll again a second or two later. Results stay fresh for Config.Exposure.FreshSeconds (30 by default). If no player is within Config.Exposure.Radius of the point, it never resolves. Do not treat known = false as "not covered".


GetPlayerExposure

local exposure = exports['oxide-weather']:GetPlayerExposure(source)

Parameters:

NameTypeDescription
sourcenumberPlayer server id

Returns: table:

{
    known       = true,
    covered     = false,      -- interiors and enclosed vehicles count as covered
    wetness     = 0.63,       -- 0-1. Rises in rain, falls when dry, 1 while swimming
    insulation  = 0.0,        -- 0-1, from Config.Exposure.Clothing
    temperature = 4.1,        -- Celsius at the player's position
    conditions  = <Conditions>,
}

nil when the player is offline or has no ped.


Settings

GetSetting

local snow = exports['oxide-weather']:GetSetting('Snow')

Parameters:

NameTypeDescription
keystringA top-level config key, for example 'Snow', 'Climate', 'Broadcast'

Returns: a deep copy of the live value, or nil for an unknown key. Open to any resource.


Write exports

All of these require the calling resource to be listed in Config.SetterResources. All return success, reason.

The trailing actor parameter is optional in every case. Pass a player's server id to have the change attributed to and permission-checked against that player, or nil for a system change. See Permissions for writes.

ExportSignatureNotes
SetWeather(weather, actor?)Sets the global weather and every region to match
SetTime(hour, minute, actor?)Hour 0-23, minute 0-59. Clears every per-region clock offset and switches the clock to game mode
SetTimeScale(scale, actor?)Game minutes per real minute during the day, 0.01-1440
FreezeTime(frozen, hour?, actor?)frozen is a boolean. Optional hour to freeze at
SetClock(clock, actor?)A full clock table: mode, nightScale, nightStart, nightEnd, utcOffset. utcOffset is in minutes, -720 to 840
SetBlackout(enabled, actor?)Global blackout. Clears every regional override and schedule
SetDynamicWeather(enabled, actor?)Automatic weather, globally and in every region
SetWeatherInterval(minutes, actor?)Real minutes between changes, 0.017-10080
SetZoneWeather(zone, weather, actor?)One region
SetZoneDynamicWeather(zone, enabled, actor?)One region
SetZoneWeatherInterval(zone, minutes, actor?)One region
SetZoneTime(zone, hour, minute?, actor?)Gives a region its own clock offset. Pass hour = false to clear it. 'global' is rejected, use SetTime
SetForecast(zone, entries, revision, actor?)Pins specific weather into a region's queue. entries is a list of exactly Config.ForecastLength weather names, revision must match the region's current forecastRevision, and the region's automatic weather must be on
SetSeason(season, actor?)'spring', 'summer', 'autumn', 'winter', 'auto' or 'calendar'
SetHoliday(name, enabled, actor?)'christmas' or 'halloween'
SpawnFront(options, actor?)See below. Returns true, frontId
RemoveFront(id, actor?)
SetZoneBlackout(zone, enabled, duration?, actor?)duration in seconds. Omit for indefinite
ScheduleBlackout(zone, delay, duration, actor?)Both in seconds. Returns true, outageId
CancelBlackout(id, actor?)
SetSceneLock(target, seconds, actor?)target is 'global' or a player server id. seconds 1-7200
ClearSceneLock(target, actor?)Only the resource that took the lock may release it
SetSetting(key, value, actor?)Writes a setting to the database, with the same validation as the settings menu

SetSetting and restart-only keys

SetSetting has a third return value. For a key that needs a restart of the resource before it takes effect, it returns true, nil, 'restart':

local ok, reason, pending = exports['oxide-weather']:SetSetting('WeatherTransitionTime', 0, nil)
if ok and pending == 'restart' then
    print('saved, but oxide-weather must be restarted for it to apply')
end

Read the current value back with GetSetting(key). Keys are the top-level names from the config files, for example 'Snow', 'Climate', 'Fronts', 'Broadcast', 'Roads', 'Exposure'. See Configuration for the full list and which ones need a restart.

SpawnFront options

local ok, frontId = exports['oxide-weather']:SpawnFront({
    coords     = vector3(-1200.0, 2400.0, 30.0),  -- required
    weather    = 'THUNDER',   -- defaults to Config.Fronts.Defaults.weather
    heading    = 90.0,        -- 0-360, compass bearing
    speed      = 12.0,        -- 0-100 m/s
    radius     = 900.0,       -- 100-5000 m
    blendWidth = 350.0,       -- 1-5000 m, cannot exceed radius
    windLead   = 500.0,       -- 0-5000 m
    windSpeed  = 6.0,         -- 0-12 m/s
    duration   = 1200.0,      -- 10-7200 seconds
    fadeSeconds = 20.0,       -- 0-60, cannot exceed half the duration
    priority   = 0,           -- -1000 to 1000
}, nil)

Anything you leave out falls back to Config.Fronts.Defaults.


Bridge entry points

WeatherBridge(method, origin, ...) and WeatherReadBridge(method, origin, ...) exist so o-link can forward calls while preserving the original caller's identity. Both reject any invoker other than o-link, and both refuse nested calls.

Do not call these directly. Use the named exports above, or the o-link namespace.

Client Exports

local weather = exports['oxide-weather']:GetWeather()

Client exports read from the state the server has already pushed, so they are cheap. They reflect what the local player actually sees, including region blending, fronts and any scene lock applied to them.

ExportSignatureReturns
GetApiVersion()number2
IsReady()boolean — settings received and the server is ready
GetWeather()string — the weather where this player is
GetTime()table{ hour, minute }, including any regional offset
IsTimeFrozen()boolean
IsBlackout()boolean — for this player's region
GetForecast()table — the forecast for this player's region, with live countdowns
GetCurrentZone()string — region id, or 'global'
GetWind()table{ speed, direction }
GetSeason()string or nil
GetTemperature()number — Celsius at the player, or nil
GetTemperatureAt(coords)number — Celsius, or nil
GetSnowLevel()number — 0-1 at the player, or nil
GetSnowLevelAt(coords)number — 0-1, or nil
GetConditionsAt(coords)Conditions, or nil. Defaults to the player's position
GetWindAt(coords)table{ speed, direction }, or nil
IsRainingAt(coords)boolean or nil
IsSnowOnGround(coords)boolean or nil
GetWeatherData()WeatherData for this player's location. Blocking, asks the server
GetFronts()table — a list of Front tables
GetCurrentFront()table{ id, weather, strength, windStrength, phase, coords } for the front the player is inside (phase = 'weather') or the one whose wind has reached them (phase = 'approaching', strength = 0), or nil
GetRoadConditionsAt(coords)table{ wetness, ice, cellSize }, or nil. Cached 5 seconds per grid cell. The server answers at most one request every 500 ms per player, so a faster cache miss returns nil
IsCovered(coords)boolean when it can tell, nil when it cannot. Defaults to the player's position
GetExposureAt(coords)table{ known, covered }
RegisterExposureEntity(entity)boolean — see below
UnregisterExposureEntity(entity)boolean
ToggleSync(enabled)boolean — see below
IsSyncEnabled()boolean

IsCovered

local covered = exports['oxide-weather']:IsCovered()   -- current position

Fires a probe straight up from the point and reports whether it hits anything. Bounded to loaded collision within 150 metres of the player, and yields while the probe runs.

Returns nil when it cannot tell: too far away, collision not streamed, or the probe timed out. Treat nil as unknown, not as "not covered".


RegisterExposureEntity / UnregisterExposureEntity

-- Ignore a prop you spawned, so it does not read as shelter
exports['oxide-weather']:RegisterExposureEntity(umbrellaObject)
exports['oxide-weather']:UnregisterExposureEntity(umbrellaObject)

Registers an object so cover probes near it ignore it. Useful for props your own resource spawns that should not count as a roof.

Only objects (entity type 3) are accepted. Only the resource that registered an entity may unregister it, and everything a resource registered is dropped automatically when that resource stops. Registrations are also dropped if the entity stops existing or its model changes.

Returns: boolean — whether the call was accepted.


ToggleSync

exports['oxide-weather']:ToggleSync(false)   -- suspend weather sync for this client
exports['oxide-weather']:ToggleSync(true)    -- resume

Suspends oxide-weather's control of the sky and clock on this client so your resource can take over, for a cutscene or a minigame.

Ownership is tracked per resource and is idempotent. Only the resource that suspended sync can resume it. Suspensions are released automatically when the suspending resource stops. Sync resumes only when every holder has released it.

While suspended, IsSyncEnabled() returns false and the resource stops applying weather, time, blackout and snow. Whatever you set stays until you release.

Returns: booleanfalse if the arguments were rejected.

local olink = exports['o-link']:olink()

Server

olink.weather.GetResourceName() returns 'oxide-weather' when it is running, otherwise 'none'.

Read functions: GetApiVersion, IsReady, GetSetting, GetWeather, GetTime, GetTimeScale, IsTimeFrozen, IsBlackout, IsDynamicWeather, GetWeatherInterval, GetForecast, GetWeatherData, GetZones, GetZoneWeather, GetZoneForecast, GetZoneAt, GetSeason, GetClimateData, GetTemperature, GetTemperatureAt, GetSnowLevel, GetSnowLevelAt, GetFronts, GetWeatherAt, IsZoneBlackout, GetBlackouts, GetSceneLocks, GetWeatherAlerts, GenerateWeazelReport, GetConditionsAt, IsRainingAt, GetWindAt, IsSnowOnGround, GetExposureAt, GetPlayerExposure, GetRoadConditionsAt, GetRealWorldWeather

Write functions: SetWeather, SetSetting, SetTime, SetTimeScale, FreezeTime, SetBlackout, SetDynamicWeather, SetWeatherInterval, SetZoneWeather, SetZoneDynamicWeather, SetZoneWeatherInterval, SetSeason, SetHoliday, SpawnFront, RemoveFront, SetZoneBlackout, ScheduleBlackout, CancelBlackout, SetSceneLock, ClearSceneLock, SetForecast, SetClock, SetZoneTime

Signatures match the direct exports. Writes normalise their first return to a strict boolean, so check ok == true. Only the first two returns pass through, so olink.weather.SetSetting cannot report the 'restart' marker; call the direct export when you need it.

Client

GetResourceName, GetApiVersion, IsReady, ToggleSync, GetWeather, GetTime, IsTimeFrozen, IsBlackout, GetForecast, GetCurrentZone, GetWind, GetSeason, GetTemperature, GetTemperatureAt, GetSnowLevel, GetSnowLevelAt, GetFronts, GetCurrentFront, IsSyncEnabled, GetConditionsAt, GetWindAt, IsRainingAt, IsSnowOnGround, IsCovered, GetExposureAt, RegisterExposureEntity, UnregisterExposureEntity, GetWeatherData, GetRoadConditionsAt

ToggleSync, RegisterExposureEntity and UnregisterExposureEntity fill in your resource name from the engine, so you do not pass it.

Bridge events

o-link re-emits a subset of the events below under its own names, so you can listen without depending on oxide-weather directly:

Server: olink:server:weather:ready, olink:server:weather:stopped, olink:server:weather:timeChanged, olink:server:weather:clockProgress, olink:server:weather:weatherChanged, olink:server:weather:zoneWeatherChanged, olink:server:weather:seasonChanged, olink:server:weather:blackoutChanged, olink:server:weather:alertsChanged

Client: olink:client:weather:ready, olink:client:weather:stopped

Payloads are identical to the oxide:weather:* events they mirror.

Server Events

Listen with AddEventHandler. These are server-local, not network events.

oxide:weather:weatherChanged

AddEventHandler('oxide:weather:weatherChanged', function(data)
    -- data.weather  : string, the new global weather
    -- data.previous : string or nil
end)

oxide:weather:timeChanged

Fires once per in-game minute.

AddEventHandler('oxide:weather:timeChanged', function(time)
    -- time.hour, time.minute
end)

oxide:weather:clockProgress

Fires on every simulation step: at least once a second, and again whenever a read export forces the simulation to advance. Do not assume a fixed cadence.

AddEventHandler('oxide:weather:clockProgress', function(data)
    -- data.minutes : number, total in-game minutes advanced since the resource started
    -- data.frozen  : boolean, true while the clock is frozen or a global scene lock is held
end)

oxide:weather:freezeChanged

AddEventHandler('oxide:weather:freezeChanged', function(data)
    -- data.frozen : boolean
end)

oxide:weather:blackoutChanged

AddEventHandler('oxide:weather:blackoutChanged', function(data)
    -- data.enabled : boolean, the global blackout state
end)

oxide:weather:zoneWeatherChanged

AddEventHandler('oxide:weather:zoneWeatherChanged', function(data)
    -- data.zone     : string, region id
    -- data.weather  : string
    -- data.previous : string or nil
end)

oxide:weather:zoneBlackoutChanged

AddEventHandler('oxide:weather:zoneBlackoutChanged', function(data)
    -- data.zone    : string, region id
    -- data.enabled : boolean
end)

oxide:weather:seasonChanged

AddEventHandler('oxide:weather:seasonChanged', function(data)
    -- data.season   : string or nil
    -- data.previous : string or nil
end)

oxide:weather:frontSpawned / oxide:weather:frontRemoved

AddEventHandler('oxide:weather:frontSpawned', function(front)
    -- front : the front as spawned, with age = 0 and no position field. Use GetFronts for the live shape
end)

AddEventHandler('oxide:weather:frontRemoved', function(data)
    -- data.id     : string
    -- data.reason : string, for example 'admin' or 'expired'
end)

oxide:weather:lightningStrike

Also sent to every client as a network event.

AddEventHandler('oxide:weather:lightningStrike', function(data)
    -- data.id                  : string, unique strike id
    -- data.front               : string, the front id that produced it
    -- data.zone                : string, region id
    -- data.coords              : { x, y, z }
    -- data.radius              : number, metres within which players see the flash
    -- data.flicker             : boolean
    -- data.flickerMilliseconds : number
end)

oxide:weather:alertsChanged

Fires whenever the active alert set meaningfully changes.

AddEventHandler('oxide:weather:alertsChanged', function(alerts)
    -- alerts : a list of Alert tables, most severe first
end)

oxide:weather:server:configUpdated

Fires after a live setting is saved, except for the control keys that apply through the simulation directly (DynamicWeather, WeatherInterval, TimeScale, FreezeTime, Clock, Broadcast) and keys that wait for a restart. Re-read anything you cached from GetSetting.

Client Events

oxide:weather:forecastChanged

AddEventHandler('oxide:weather:forecastChanged', function(forecast)
    -- forecast : the forecast list for this player's region
end)

Fires on first sync, when the global forecast is republished, and when the player crosses into another region.


oxide:weather:zoneChanged

AddEventHandler('oxide:weather:zoneChanged', function(data)
    -- data.zone     : string, the region entered
    -- data.previous : string or nil
end)

oxide:weather:lightningStrike (client)

The same payload as the server event. Delivered to every client; filter by distance yourself if you need to.


oxide:weather:bulletinChanged

RegisterNetEvent('oxide:weather:bulletinChanged', function(packet)
    -- packet.session  : string, changes when the resource restarts
    -- packet.sequence : number, increments on every change
    -- packet.alerts   : a list of Alert tables
end)

oxide:weather:client:configUpdated

Fires after settings arrive or change on this client.

State Bags

Everything below is on GlobalState, readable from server and client without a call. This is the cheapest way to follow the weather in a loop.

KeyTypeContents
oxide:weatherReadybooleantrue once the simulation is running
oxide:weatherstringGlobal weather
oxide:timetable{ hour, minute, minutes, scale, frozen, clock }. minutes is the fractional game minute of the day, scale the current timescale, frozen whether the clock is held and clock the clock settings. Re-published on each minute rollover or when the clock changes, so advance it yourself from minutes and scale between updates
oxide:blackoutbooleanGlobal blackout
oxide:freezeTimebooleanClock frozen
oxide:dynamicWeatherbooleanAutomatic weather on
oxide:timeScalenumberGame minutes per real minute
oxide:weatherIntervalnumberReal minutes between changes
oxide:forecasttableThe global forecast list
oxide:seasonstringCurrent season, or absent when seasons are off
oxide:snowtableRegion id to snow level, plus global
oxide:climatetableThe same shape as GetClimateData()
oxide:realWorldtableThe same shape as GetRealWorldWeather() without lastError, published whenever it changes
oxide:zoneTimestableRegion id to { hour, minute, offset }, only for regions with a clock offset
oxide:regionalWeathertable{ revision, zones = { [id] = WeatherData }, climate }
oxide:weatherZonestable{ enabled, revision, zones = { <definition>, ... } }
oxide:frontstable{ version, epoch, stamp, sequence, paused, fronts, outages, overrides }
oxide:weatherHoldstableActive scene locks by target, each with remaining in milliseconds
oxide:weatherBroadcastConfigtableThe live public weather settings
oxide-weather:configtableThe list of setting keys published to clients
oxide-weather:config:<Key>anyThe live value of one setting. SetterResources, Persistence, Exposure and RealWorld are never published; read those with GetSetting on the server

oxide:regionalWeather and oxide:weatherZones carry a revision. Only trust them together when the two revisions match, since they are published separately.

-- client
local weather = GlobalState['oxide:weather']
local time    = GlobalState['oxide:time']

AddStateBagChangeHandler('oxide:weather', 'global', function(_, _, value)
    print('weather is now', value)
end)

Integration Examples

Cold damage while exposed

-- server, in any resource
local olink = exports['o-link']:olink()

CreateThread(function()
    while true do
        Wait(30000)
        for _, id in ipairs(GetPlayers()) do
            local src = tonumber(id)
            local exposure = olink.weather.GetPlayerExposure(src)
            if exposure and exposure.known and not exposure.covered
                and exposure.temperature and exposure.temperature <= 0 then
                local protection = exposure.insulation - exposure.wetness * 0.5
                if protection < 0.5 then
                    local ped = GetPlayerPed(src)
                    SetEntityHealth(ped, math.max(101, GetEntityHealth(ped) - 5))
                end
            end
        end
    end
end)

Slow crop growth in the cold

-- server
local olink = exports['o-link']:olink()

local function GrowthMultiplier(coords)
    local conditions = olink.weather.GetConditionsAt(coords)
    if not conditions then return 1.0 end          -- weather unavailable, do not punish the player
    local multiplier = 1.0
    if conditions.raining then multiplier = multiplier * 1.15 end
    if conditions.temperature then
        if conditions.temperature <= 0 then multiplier = multiplier * 0.7
        elseif conditions.temperature >= 35 then multiplier = multiplier * 0.8 end
    end
    return multiplier
end

Reduced grip on wet and icy roads

-- client
local olink = exports['o-link']:olink()

CreateThread(function()
    while true do
        Wait(2000)
        local vehicle = GetVehiclePedIsIn(PlayerPedId(), false)
        if vehicle ~= 0 and GetPedInVehicleSeat(vehicle, -1) == PlayerPedId() then
            local road = olink.weather.GetRoadConditionsAt(GetEntityCoords(PlayerPedId()))
            if road then
                -- slippery once the road is properly wet, or once ice has formed
                SetVehicleReduceGrip(vehicle, road.ice > 0.3 or road.wetness > 0.6)
            end
        end
    end
end)

A Weazel News weather segment

-- server, in a news or radio resource
local olink = exports['o-link']:olink()

RegisterCommand('weatherreport', function(source)
    local report = olink.weather.GenerateWeazelReport(nil, { unit = 'C' })
    if not report then
        olink.notify.Send(source, 'The weather desk is offline.', 'error')
        return
    end
    TriggerClientEvent('chat:addMessage', -1, {
        color = { 200, 200, 255 },
        multiline = true,
        args = { 'Weazel News', report.text },
    })
end, false)

Suspending sync for a cutscene

-- client
local weatherResource = exports['o-link']:olink().weather

local function BeginCutscene()
    weatherResource.ToggleSync(false)     -- oxide-weather stops touching the sky
    SetWeatherTypeNowPersist('EXTRASUNNY')
    NetworkOverrideClockTime(6, 0, 0)
end

local function EndCutscene()
    weatherResource.ToggleSync(true)      -- normal weather resumes immediately
end

Prefer a scene lock over ToggleSync when you want the real weather held still rather than replaced. A lock keeps everyone consistent and expires on its own:

-- server, from a resource listed in Config.SetterResources
exports['oxide-weather']:SetSceneLock('global', 300, nil)
-- ...run the scene...
exports['oxide-weather']:ClearSceneLock('global', nil)

Reacting to a storm rolling in

-- server
AddEventHandler('olink:server:weather:alertsChanged', function(alerts)
    for _, alert in ipairs(alerts) do
        if alert.severity == 3 and alert.status == 'active' then
            print(('Severe weather in %s: %s'):format(alert.region, alert.title))
        end
    end
end)

On this page

Two Ways InThrough o-link (recommended)Direct exportsPermissions for WritesCommon Return ShapesConditionsWeatherDataFrontAlertServer ExportsStatusGetApiVersionIsReadyPoint queriesGetConditionsAtGetWeatherAtGetWindAtIsRainingAtIsSnowOnGroundGetTemperatureAtGetSnowLevelAtGetZoneAtGetRoadConditionsAtGlobal stateGetWeatherGetTimeGetTimeScaleIsTimeFrozenIsBlackoutIsDynamicWeatherGetWeatherIntervalGetForecastGetWeatherDataRegionsGetZonesGetZoneWeatherGetZoneForecastSeason and climateGetSeasonGetClimateDataGetTemperatureGetSnowLevelFronts and blackoutsGetFrontsIsZoneBlackoutGetBlackoutsGetRealWorldWeatherAlerts and reportsGetWeatherAlertsGenerateWeazelReportScene locksGetSceneLocksExposureGetExposureAtGetPlayerExposureSettingsGetSettingWrite exportsSetSetting and restart-only keysSpawnFront optionsBridge entry pointsClient ExportsIsCoveredRegisterExposureEntity / UnregisterExposureEntityToggleSyncThe o-link Weather NamespaceServerClientBridge eventsServer Eventsoxide:weather:weatherChangedoxide:weather:timeChangedoxide:weather:clockProgressoxide:weather:freezeChangedoxide:weather:blackoutChangedoxide:weather:zoneWeatherChangedoxide:weather:zoneBlackoutChangedoxide:weather:seasonChangedoxide:weather:frontSpawned / oxide:weather:frontRemovedoxide:weather:lightningStrikeoxide:weather:alertsChangedoxide:weather:server:configUpdatedClient Eventsoxide:weather:forecastChangedoxide:weather:zoneChangedoxide:weather:lightningStrike (client)oxide:weather:bulletinChangedoxide:weather:client:configUpdatedState BagsIntegration ExamplesCold damage while exposedSlow crop growth in the coldReduced grip on wet and icy roadsA Weazel News weather segmentSuspending sync for a cutsceneReacting to a storm rolling in