@@ -267,6 +267,30 @@ function resolveOverlayRuntimeEventQueue(context = {}, gameplayState = null, syn
267267 return null ;
268268}
269269
270+ function hasOverlayRuntimeSyncPatch ( syncPatch ) {
271+ if ( ! syncPatch || typeof syncPatch !== 'object' ) {
272+ return false ;
273+ }
274+ if ( syncPatch . visible === true || syncPatch . visible === false ) {
275+ return true ;
276+ }
277+ if ( Number . isFinite ( Number ( syncPatch . interactionIndex ) ) ) {
278+ return true ;
279+ }
280+ return String ( syncPatch . activeOverlayId || '' ) . trim ( ) . length > 0 ;
281+ }
282+
283+ function computeOverlayRuntimeCompatSignature ( syncPatch , count ) {
284+ if ( ! hasOverlayRuntimeSyncPatch ( syncPatch ) ) {
285+ return '' ;
286+ }
287+ const visibleToken = syncPatch . visible === true ? '1' : ( syncPatch . visible === false ? '0' : 'x' ) ;
288+ const hasInteractionIndex = Number . isFinite ( Number ( syncPatch . interactionIndex ) ) ;
289+ const interactionToken = hasInteractionIndex ? String ( Math . trunc ( Number ( syncPatch . interactionIndex ) ) ) : 'x' ;
290+ const activeOverlayId = String ( syncPatch . activeOverlayId || '' ) . trim ( ) ;
291+ return `${ visibleToken } |${ interactionToken } |${ activeOverlayId } |${ Math . max ( 0 , Number ( count ) || 0 ) } ` ;
292+ }
293+
270294export function enqueueOverlayGameplayRuntimeSyncEvent ( target , event = { } ) {
271295 if ( ! target || typeof target !== 'object' ) {
272296 return false ;
@@ -303,6 +327,43 @@ function normalizeOverlayRuntimeSyncEvent(event) {
303327 } ;
304328}
305329
330+ function consumeOverlayRuntimeSyncEvents ( eventQueue = [ ] ) {
331+ if ( ! Array . isArray ( eventQueue ) || eventQueue . length === 0 ) {
332+ return {
333+ eventsProcessed : 0 ,
334+ patch : { } ,
335+ } ;
336+ }
337+
338+ const patch = { } ;
339+ let eventsProcessed = 0 ;
340+ for ( let i = 0 ; i < eventQueue . length ; i += 1 ) {
341+ const event = normalizeOverlayRuntimeSyncEvent ( eventQueue [ i ] ) ;
342+ if ( ! event ) {
343+ continue ;
344+ }
345+ if ( event . type !== 'overlay-runtime-sync' && event . type !== 'overlay-state-sync' ) {
346+ continue ;
347+ }
348+ eventsProcessed += 1 ;
349+ if ( event . visible === true || event . visible === false ) {
350+ patch . visible = event . visible ;
351+ }
352+ if ( Number . isFinite ( event . interactionIndex ) ) {
353+ patch . interactionIndex = event . interactionIndex ;
354+ }
355+ if ( event . activeOverlayId ) {
356+ patch . activeOverlayId = event . activeOverlayId ;
357+ }
358+ }
359+ eventQueue . length = 0 ;
360+
361+ return {
362+ eventsProcessed,
363+ patch,
364+ } ;
365+ }
366+
306367function applyOverlayRuntimeSyncPatch ( runtime , runtimeExtensions , patch = { } ) {
307368 const count = runtimeExtensions . length ;
308369 let desyncCorrected = false ;
@@ -372,27 +433,29 @@ export function synchronizeOverlayGameplayRuntimeState(runtime, context = {}) {
372433 const count = runtimeExtensions . length ;
373434 let desyncCorrected = false ;
374435 let eventsProcessed = 0 ;
436+ let syncMode = 'cached' ;
375437
376438 if ( Array . isArray ( eventQueue ) && eventQueue . length > 0 ) {
377- const pending = eventQueue . splice ( 0 , eventQueue . length ) ;
378- for ( let i = 0 ; i < pending . length ; i += 1 ) {
379- const event = normalizeOverlayRuntimeSyncEvent ( pending [ i ] ) ;
380- if ( ! event ) {
381- continue ;
382- }
383- if ( event . type !== 'overlay-runtime-sync' && event . type !== 'overlay-state-sync' ) {
384- continue ;
385- }
386- const corrected = applyOverlayRuntimeSyncPatch ( runtime , runtimeExtensions , event ) ;
439+ const consumedEvents = consumeOverlayRuntimeSyncEvents ( eventQueue ) ;
440+ eventsProcessed = consumedEvents . eventsProcessed ;
441+ if ( hasOverlayRuntimeSyncPatch ( consumedEvents . patch ) ) {
442+ const corrected = applyOverlayRuntimeSyncPatch ( runtime , runtimeExtensions , consumedEvents . patch ) ;
387443 desyncCorrected = desyncCorrected || corrected ;
388- eventsProcessed += 1 ;
444+ }
445+ if ( eventsProcessed > 0 ) {
446+ syncMode = 'events' ;
389447 }
390448 }
391449
392- // Compatibility fallback for pre-event producers.
450+ // Compatibility fallback for pre-event producers with change detection to avoid repeated polling overhead .
393451 if ( eventsProcessed === 0 && syncState ) {
394- const corrected = applyOverlayRuntimeSyncPatch ( runtime , runtimeExtensions , syncState ) ;
395- desyncCorrected = desyncCorrected || corrected ;
452+ const compatSignature = computeOverlayRuntimeCompatSignature ( syncState , count ) ;
453+ if ( compatSignature && runtime . interactionCompatSyncSignature !== compatSignature ) {
454+ const corrected = applyOverlayRuntimeSyncPatch ( runtime , runtimeExtensions , syncState ) ;
455+ desyncCorrected = desyncCorrected || corrected ;
456+ syncMode = 'compat' ;
457+ runtime . interactionCompatSyncSignature = compatSignature ;
458+ }
396459 }
397460
398461 const interactionIndex = normalizeInteractionIndex ( runtime ) ;
@@ -405,9 +468,10 @@ export function synchronizeOverlayGameplayRuntimeState(runtime, context = {}) {
405468 cycleKey : String ( runtime . interactionCycleKey || LEVEL17_OVERLAY_CYCLE_KEY ) ,
406469 desyncCorrected,
407470 eventsProcessed,
408- syncMode : eventsProcessed > 0 ? 'events' : 'compat' ,
471+ syncMode,
409472 } ;
410473 writeOverlayRuntimeSyncSnapshot ( syncState , snapshot ) ;
474+ runtime . interactionCompatSyncSignature = computeOverlayRuntimeCompatSignature ( syncState , count ) ;
411475 return snapshot ;
412476}
413477
0 commit comments