@@ -79,6 +79,37 @@ function recalcObjectiveSummary(objectivesById) {
7979 } ;
8080}
8181
82+ function ensureNormalizedWorldState ( snapshot ) {
83+ if ( ! isPlainObject ( snapshot . worldState ) ) {
84+ snapshot . worldState = { } ;
85+ }
86+
87+ const worldState = snapshot . worldState ;
88+
89+ if ( ! isPlainObject ( worldState . scores ) ) {
90+ worldState . scores = { } ;
91+ }
92+ worldState . scores . total = toFiniteNumber ( worldState . scores . total , 0 ) ;
93+ worldState . scores . rewardPoints = toFiniteNumber ( worldState . scores . rewardPoints , 0 ) ;
94+ if ( worldState . scores . rank === undefined ) {
95+ worldState . scores . rank = null ;
96+ }
97+
98+ if ( ! isPlainObject ( worldState . objectives ) ) {
99+ worldState . objectives = { } ;
100+ }
101+ if ( ! isPlainObject ( worldState . objectives . byId ) ) {
102+ worldState . objectives . byId = { } ;
103+ }
104+ worldState . objectives . summary = recalcObjectiveSummary ( worldState . objectives . byId ) ;
105+
106+ if ( ! isPlainObject ( worldState . flags ) ) {
107+ worldState . flags = { } ;
108+ }
109+
110+ return worldState ;
111+ }
112+
82113function validateTransitionGameMode ( payload ) {
83114 const nextMode = payload && payload . nextMode ;
84115 if ( typeof nextMode !== 'string' || ! nextMode . trim ( ) ) {
@@ -114,25 +145,18 @@ function validateApplyScoreDelta(payload) {
114145 if ( ! Number . isFinite ( normalizedDelta ) ) {
115146 return { ok : false , reason : 'applyScoreDelta requires finite numeric payload.delta.' } ;
116147 }
148+ if ( payload . rewardDelta !== undefined && ! Number . isFinite ( Number ( payload . rewardDelta ) ) ) {
149+ return { ok : false , reason : 'applyScoreDelta payload.rewardDelta must be finite numeric when provided.' } ;
150+ }
117151 return { ok : true } ;
118152}
119153
120154function applyAuthoritativeScoreDelta ( snapshot , payload , context = { } ) {
121155 if ( isPassiveModeContext ( context ) ) {
122156 return { changes : [ ] } ;
123157 }
124- if ( ! snapshot . worldState || ! isPlainObject ( snapshot . worldState ) ) {
125- snapshot . worldState = { } ;
126- }
127- if ( ! isPlainObject ( snapshot . worldState . scores ) ) {
128- snapshot . worldState . scores = {
129- total : 0 ,
130- rewardPoints : 0 ,
131- rank : null
132- } ;
133- }
134-
135- const scores = snapshot . worldState . scores ;
158+ const worldState = ensureNormalizedWorldState ( snapshot ) ;
159+ const scores = worldState . scores ;
136160 const currentTotal = toFiniteNumber ( scores . total , 0 ) ;
137161 const currentRewardPoints = toFiniteNumber ( scores . rewardPoints , 0 ) ;
138162 const delta = toFiniteNumber ( payload . delta , 0 ) ;
@@ -177,17 +201,8 @@ function applyAuthoritativeObjectiveProgress(snapshot, payload, context = {}) {
177201 }
178202 const now = typeof context . now === 'function' ? context . now : ( ) => Date . now ( ) ;
179203 const objectiveId = String ( payload . objectiveId || '' ) . trim ( ) ;
180- const objectives = snapshot . worldState && snapshot . worldState . objectives
181- ? snapshot . worldState . objectives
182- : { summary : { total : 0 , completed : 0 , active : 0 } , byId : { } } ;
183- if ( ! snapshot . worldState . objectives ) {
184- snapshot . worldState . objectives = objectives ;
185- }
186-
187- if ( ! isPlainObject ( objectives . byId ) ) objectives . byId = { } ;
188- if ( ! isPlainObject ( objectives . summary ) ) {
189- objectives . summary = { total : 0 , completed : 0 , active : 0 } ;
190- }
204+ const worldState = ensureNormalizedWorldState ( snapshot ) ;
205+ const objectives = worldState . objectives ;
191206
192207 const existing = isPlainObject ( objectives . byId [ objectiveId ] ) ? objectives . byId [ objectiveId ] : { } ;
193208 objectives . byId [ objectiveId ] = {
0 commit comments