-- Loaded after content/interface; schema and the persistence instance come from init. local C, M, P = core, hearth.model, hearth.persistence local singleplayer = C.is_singleplayer() local deployment = C.settings:get("hearth_environment") if deployment == nil then deployment = singleplayer and "local" or "production" end if deployment ~= "local" and deployment ~= "staging" and deployment ~= "production" then error("Last Hearth: hearth_environment must be local, staging, or production.", 0) end if not singleplayer and deployment == "local" then error("Last Hearth: dedicated servers cannot use the local environment.", 0) end local date_setting = C.settings:get("hearth_preview_date") local weather_setting = C.settings:get("hearth_preview_weather") or "auto" local function nonempty(value) return value ~= nil and value ~= "" end if deployment == "production" and (nonempty(date_setting) or weather_setting ~= "auto" or C.settings:get("hearth_launcher_mode") ~= nil or C.settings:get("hearth_smoke_token") ~= nil or C.settings:get("hearth_m1_course") ~= nil or C.settings:get("hearth_m1_course_nonce") ~= nil or C.settings:get("hearth_m1_course_snag") ~= nil or C.settings:get("hearth_course_token") ~= nil) then error("Last Hearth: production rejects fixture and launcher test settings.", 0) end local preview_date if nonempty(date_setting) then preview_date = tonumber(date_setting) if not preview_date or preview_date ~= preview_date or preview_date < 946684800 or preview_date > 4102444800 or preview_date % 1 ~= 0 then error("Last Hearth: hearth_preview_date must be an integer UTC timestamp from 2000 through 2100.", 0) end end if weather_setting ~= "auto" and weather_setting ~= "clear" and weather_setting ~= "rain" then error("Last Hearth: hearth_preview_weather must be auto, clear, or rain.", 0) end local configured = M.location(C.settings:get("hearth_latitude"), C.settings:get("hearth_longitude")) if (deployment == "production" or not singleplayer) and not configured then error("Last Hearth: server/production requires explicit valid hearth_latitude and hearth_longitude settings.", 0) end -- Always validate stored data, even when settings take precedence. Corrupt saves -- must fail startup, not disappear behind a configured location or neutral preview. local stored = P.load_world("location") local selected = configured or (singleplayer and stored) hearth.has_location = selected ~= nil and selected ~= false -- The existing location form and HUD disclose this unchosen neutral preview. local location = selected or {latitude = 0, longitude = 0} function hearth.climate_at(timestamp) if type(timestamp) ~= "number" or timestamp ~= timestamp or timestamp < 0 then return nil end return M.solar(math.floor(timestamp), location.latitude, location.longitude) end function hearth.set_location(lat, lon) assert(singleplayer, "Only local worlds can select location in game") local value = assert(M.location(lat, lon), "Invalid reference coordinates") -- Publish only after a successful save; never silently replace a failed record. P.save_world("location", value) location, hearth.has_location = value, true end function hearth.clock_update() local now = preview_date or os.time() hearth.clock = M.solar(now, location.latitude, location.longitude) hearth.weather = M.weather(now, location.latitude, location.longitude) if weather_setting ~= "auto" then hearth.weather = weather_setting end C.set_timeofday(hearth.clock.timeofday) end C.after(0, hearth.clock_update)