From dc92d07879bc0c80553c0bfea72fe54704dc1c1a Mon Sep 17 00:00:00 2001 From: Kiki <70842600+terega@users.noreply.github.com> Date: Fri, 16 Oct 2026 11:14:44 +0200 Subject: [PATCH 1/2] Synchronize MR ground wetness and persist accumulated dampness Make the server authoritative for MR moisture values while retaining the existing temperature- and sunlight-based formulas. Send updated moisture values to clients when they change, and send the current state to newly joining clients. Integrate daytime drying using elapsed time and the effective time scale. Persist accumulated dampness through the existing weather XML save/load hooks using the MRdampness attribute. Replace the previous RealisticMain weather update loop with a Weather.update hook and clear pending state when leaving the map. Use the neutral identifiers MRweather, MRwetnessEvent and MRwetness. No forecasts, rainfall, cloud settings or traction coefficients are changed. --- MoreRealistic/scripts/MR_Weather.lua | 301 ++++++++++++++------------- 1 file changed, 155 insertions(+), 146 deletions(-) diff --git a/MoreRealistic/scripts/MR_Weather.lua b/MoreRealistic/scripts/MR_Weather.lua index 39299cd..10e2942 100644 --- a/MoreRealistic/scripts/MR_Weather.lua +++ b/MoreRealistic/scripts/MR_Weather.lua @@ -1,152 +1,161 @@ ---20261110 - doesn't take into account current month anymore. Because it highly depends on the map location IRL ---only take into account current 'minTemprature', 'maxTemperature', and 'currentTemperature' g_currentMission.environment.weather.temperatureUpdater.currentMax VehicleDebug.state // weather:getCurrentTemperature() ---base game settings seem a little bit "OFF". ---there are no settings by month. ---settings are by season. --- season 1 = spring (march april may) --- season 2 = summer (june july august) --- season 3 = autumn (september october november) --- season 4 = winter (december january february) ---it means player can experience the same weather/temperatures in march and may, or in june and august, or in september and november, or in december and february. ---it doesn't fit my area at all for example (north of france). The temperatures in november are just "crazy" compared to IRL here. ---and it means we can experience a "gap" when changing season (great difference between may and june for example) ---another "flaw" = each base game map has the exact same config (USA, European and Asian map => not realistic at all) - -Weather.mrCurrentWetness = 0 --init -Weather.mrCurrentWetnessMin = 0 -Weather.mrCurrentWetnessDamp = 0 -Weather.mrUpdateTimer = 0 - - -Weather.mrGetGroundWetness = function(self, superFunc, updateMR, dt) - - if updateMR then - - local wetness = superFunc(self) - - local minWetness = 0 - if g_currentMission.environment.weather.temperatureUpdater.currentMax<10 then - minWetness = 0.25 - elseif g_currentMission.environment.weather.temperatureUpdater.currentMax<30 then - minWetness = (30 - g_currentMission.environment.weather.temperatureUpdater.currentMax)/80 --10°C = 25% / 30°C = 0% - end - - local dampWetness = 0 - if g_currentMission.environment.isSunOn then - if Weather.mrCurrentWetnessDamp>0 then - local timeScale = g_currentMission:getEffectiveTimeScale() - local fxTemp = math.max(0.1, g_currentMission.environment.weather:getCurrentTemperature()/25) - fxTemp = math.pow(fxTemp, 0.7) - dampWetness = math.max(0, Weather.mrCurrentWetnessDamp - 0.1*fxTemp*timeScale*dt/3600000) --decrease dampness when the sun is present. 1 hour to go from 10% to 0% dampness when the outdoor temperature is 25°C - end - else - local currentTemperature = g_currentMission.environment.weather:getCurrentTemperature() - if currentTemperature<30 then - if currentTemperature>25 then - dampWetness = 0.06-0.012*(currentTemperature-25) - elseif currentTemperature>20 then - dampWetness = 0.115-0.011*(currentTemperature-20) - elseif currentTemperature>15 then - dampWetness = 0.165-0.01*(currentTemperature-15) - elseif currentTemperature>10 then - dampWetness = 0.205-0.008*(currentTemperature-10) - elseif currentTemperature>5 then - dampWetness = 0.235-0.006*(currentTemperature-5) - elseif currentTemperature>0 then - dampWetness = 0.25-0.003*currentTemperature - else - dampWetness = 0.25 - end - end - end - - wetness = math.max(minWetness, dampWetness, wetness) - - --telemetry - Weather.mrCurrentWetnessMin = minWetness - Weather.mrCurrentWetnessDamp = dampWetness - Weather.mrCurrentWetness = wetness - +-- Temperature/sun model, with server-owned persistent dampness. +-- No forecast, rainfall, time scale or cloud settings are changed here. +MRweather = {INTERVAL_MS=2000} +local M = MRweather +local function finite(v) return type(v)=="number" and v==v and math.abs(v)=30 then return 0 + elseif t>25 then return 0.06-0.012*(t-25) + elseif t>20 then return 0.115-0.011*(t-20) + elseif t>15 then return 0.165-0.01*(t-15) + elseif t>10 then return 0.205-0.008*(t-10) + elseif t>5 then return 0.235-0.006*(t-5) + elseif t>0 then return 0.25-0.003*t + end + return 0.25 +end +function M.telemetry(s) + Weather.mrCurrentWetness=s.wetness + Weather.mrCurrentWetnessMin=s.minimum + Weather.mrCurrentWetnessDamp=s.damp +end +local nativeGroundWetness=Weather.getGroundWetness +function M.refresh(w) + local s=state(w) + local m=g_currentMission + if g_server==nil or not m or not m.environment then return false end + local maxTemp=w.temperatureUpdater and w.temperatureUpdater.currentMax + local temperature=w:getCurrentTemperature() + if not finite(maxTemp) or not finite(temperature) then return false end + local minimum=math.max(0,math.min(0.25,(30-maxTemp)/80)) + if m.environment.isSunOn then + s.damp=math.max(0,s.damp-s.drying) + else + s.damp=M.nightDamp(temperature) end - - return Weather.mrCurrentWetness + s.drying=0 + s.minimum=minimum + s.wetness=math.max(minimum,s.damp,unit(nativeGroundWetness(w))) + s.ready=true + M.telemetry(s) + return true end - - ---[[ old function -Weather.mrGetGroundWetness = function(self, superFunc) - - local wetness = superFunc(self) - - --minimum groundwetness function of month and time - -- wetness during night => +0.1 - -- wetness during winter - -- period = month - -- season 1 = spring (march april may) - -- season 2 = summer (june july august) - -- season 3 = autumn (september october november) - -- season 4 = winter (december january february) - local month = g_currentMission.environment.currentPeriod - local minWetness = 0 - local dampStart=99 - local dampStop=0 - if month==11 then --january - minWetness = 0.25 - elseif month==12 then --february - minWetness = 0.20 - elseif month==1 then --march - minWetness = 0.15 - dampStart = 19 - dampStop = 11 - elseif month==2 then --april - minWetness = 0.1 - dampStart = 19 - dampStop = 11 - elseif month==3 then --may - minWetness = 0.05 - dampStart = 19 - dampStop = 10 - elseif month==4 then --june - minWetness = 0 - dampStart = 20 - dampStop = 10 - elseif month==5 then --july - minWetness = 0 - dampStart = 21 - dampStop = 9 - elseif month==6 then --august - minWetness = 0 - dampStart = 22 - dampStop = 9 - elseif month==7 then --september - minWetness = 0.1 - dampStart = 20 - dampStop = 10 - elseif month==8 then --october - minWetness = 0.15 - dampStart = 19 - dampStop = 11 - elseif month==9 then --november - minWetness = 0.2 - elseif month==10 then --december - minWetness = 0.25 +function M.apply(w,wetness,minimum,damp) + if not finite(wetness) or not finite(minimum) or not finite(damp) + or wetness<0 or wetness>1 or minimum<0 or minimum>0.25 + or damp<0 or damp>0.25 then return end + local s=state(w) + s.wetness,s.minimum,s.damp=wetness,minimum,damp + s.ready=true + M.telemetry(s) +end +MRwetnessEvent={} +local eventMt=Class(MRwetnessEvent,Event) +InitEventClass(MRwetnessEvent,"MRwetnessEvent") +function MRwetnessEvent.emptyNew() return Event.new(eventMt) end +function MRwetnessEvent.new(s) + local e=MRwetnessEvent.emptyNew() + e.wetness,e.minimum,e.damp=s.wetness,s.minimum,s.damp + return e +end +function MRwetnessEvent:writeStream(streamId,connection) + streamWriteFloat32(streamId,self.wetness) + streamWriteFloat32(streamId,self.minimum) + streamWriteFloat32(streamId,self.damp) +end +function MRwetnessEvent:readStream(streamId,connection) + self.wetness=streamReadFloat32(streamId) + self.minimum=streamReadFloat32(streamId) + self.damp=streamReadFloat32(streamId) + self:run(connection) +end +function MRwetnessEvent:run(connection) + -- Clients cannot alter the server's moisture state. + if g_server~=nil or not connection or not connection:getIsServer() then return end + local w=currentWeather() + if w then M.apply(w,self.wetness,self.minimum,self.damp) + else M.pending={self.wetness,self.minimum,self.damp} end +end +function M.initialize(w) + if not w then return end + if g_server~=nil then M.refresh(w) + elseif M.pending then + M.apply(w,unpack(M.pending));M.pending=nil end - - --damp = can't increase minWetness above 0.2 - if minWetness<0.2 then - local damp = 0 - local hour = g_currentMission.environment.currentHour + g_currentMission.environment.currentMinute / 60 - --2hours for damp to fully increase or decrease - if hour>dampStart then - damp = math.min(0.1, hour-dampStart * 0.05) - elseif hour=0 then + if m.environment.isSunOn then + -- Integrate each frame: speed changes and pauses do not retroactively + -- apply the latest time scale to the entire two-second interval. + local fx=math.max(0.1,temperature/25)^0.7 + s.drying=s.drying+0.1*fx*scale*dt/3600000 + else + -- Keep the authoritative night state current through sunrise. + s.damp=M.nightDamp(temperature) + s.drying=0 end - minWetness = math.min(0.2, minWetness + damp) end - - wetness = math.max(minWetness, wetness) - - return wetness + s.elapsed=s.elapsed+dt + if not s.ready or s.elapsed>=M.INTERVAL_MS then + if M.refresh(w) then + s.elapsed=0 + if s.sentWetness~=s.wetness or s.sentMinimum~=s.minimum or s.sentDamp~=s.damp then + g_server:broadcastEvent(MRwetnessEvent.new(s),false) + s.sentWetness,s.sentMinimum,s.sentDamp=s.wetness,s.minimum,s.damp + end + end + end +end) +Weather.sendInitialState=Utils.appendedFunction(Weather.sendInitialState,function(w,connection) + if g_server==nil or not connection then return end + M.initialize(w) + local s=state(w) + if s.ready then connection:sendEvent(MRwetnessEvent.new(s)) end +end) +-- Use the existing weather XML handle; never create/save/delete the game file. +-- Support native handles and XMLFile objects without owning their lifecycle. +local function readFloat(handle,key) + if type(handle)=="table" and handle.getFloat then return handle:getFloat(key) end + return getXMLFloat(handle,key) +end +local function writeFloat(handle,key,value) + if type(handle)=="table" and handle.setFloat then handle:setFloat(key,value) + else setXMLFloat(handle,key,value) end end ---]] +Weather.loadFromXMLFile=Utils.appendedFunction(Weather.loadFromXMLFile,function(w,handle,key) + local s=state(w) + local damp=readFloat(handle,key.."#MRdampness") + s.damp=math.min(0.25,unit(damp)) + s.ready,s.elapsed,s.drying=false,0,0 +end) +Weather.saveToXMLFile=Utils.appendedFunction(Weather.saveToXMLFile,function(w,handle,key) + if g_server==nil then return end + local s=state(w) + -- Include drying accumulated since the last published snapshot. + writeFloat(handle,key.."#MRdampness",math.max(0,s.damp-s.drying)) +end) From 3a5ae82575dbdb90a98690b390b702fa3e92feaf Mon Sep 17 00:00:00 2001 From: Kiki <70842600+terega@users.noreply.github.com> Date: Fri, 16 Oct 2026 11:18:04 +0200 Subject: [PATCH 2/2] Synchronize MR ground wetness and persist accumulated dampness Make the server authoritative for MR moisture values while retaining the existing temperature- and sunlight-based formulas. Send updated moisture values to clients when they change, and send the current state to newly joining clients. Integrate daytime drying using elapsed time and the effective time scale. Persist accumulated dampness through the existing weather XML save/load hooks using the MRdampness attribute. Replace the previous RealisticMain weather update loop with a Weather.update hook and clear pending state when leaving the map. Use the neutral identifiers MRweather, MRwetnessEvent and MRwetness. No forecasts, rainfall, cloud settings or traction coefficients are changed. --- MoreRealistic/scripts/RealisticMain.lua | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/MoreRealistic/scripts/RealisticMain.lua b/MoreRealistic/scripts/RealisticMain.lua index 52c0311..48251a8 100644 --- a/MoreRealistic/scripts/RealisticMain.lua +++ b/MoreRealistic/scripts/RealisticMain.lua @@ -100,16 +100,14 @@ print("**********************************************************************") function RealisticMain:loadMap(name) RealisticUtils.loadTerrainIdToName() - g_currentMission.environment.weather.getGroundWetness = Utils.overwrittenFunction(g_currentMission.environment.weather.getGroundWetness, Weather.mrGetGroundWetness) + MRweather.initialize(g_currentMission.environment.weather) end -function RealisticMain:update(dt) - --update weather data once every 2s - Weather.mrUpdateTimer = Weather.mrUpdateTimer - dt - if Weather.mrUpdateTimer<=0 then - g_currentMission.environment.weather:getGroundWetness(true, 2000) --true means we want to update MR values for damp and minWetness - Weather.mrUpdateTimer = 2000 - end +function RealisticMain:deleteMap() + MRweather.pending = nil + Weather.mrCurrentWetness = nil + Weather.mrCurrentWetnessMin = nil + Weather.mrCurrentWetnessDamp = nil end --allow to use the following functions :