@@ -331,6 +331,11 @@ const spawnLocations2 = PluginManager.loadServerData(
331331 "2016/sampleData/equipmentModelTexturesMapping.json"
332332 ) ;
333333
334+ // A player resource is only re-sent to clients when it crosses one of these fractions of its MAX_VALUE
335+ // (permille), so continuously-decaying resources no longer emit a ResourceEvent every tick for a change
336+ // the client cannot render. TODO: RE-confirm the client's actual resource-bar granularity.
337+ const RESOURCE_SEND_STEPS = 1000 ;
338+
334339export class ZoneServer2016 extends EventEmitter {
335340 /** Networking layer - allows sending game data to the game client,
336341 * lays on top of the H1Z1 protocol (on top of the actual H1Z1 packets)
@@ -4024,13 +4029,47 @@ export class ZoneServer2016 extends EventEmitter {
40244029 }
40254030 }
40264031
4032+ /**
4033+ * Change-gate for ResourceEvent sends. Returns true (and updates the character's last-sent cache) only
4034+ * when the value differs from the last value sent by enough to change what the client displays - killing
4035+ * per-tick spam for continuously-decaying resources whose raw value ticks by a client-imperceptible
4036+ * amount. The client renders resources as a fraction of MAX_VALUE, so the value is quantized to
4037+ * RESOURCE_SEND_STEPS. Non-player entities (vehicles/doors/etc., not in _characters) are never gated, so
4038+ * their existing behavior is unchanged.
4039+ */
4040+ private _shouldSendResourceEvent (
4041+ entityId : string ,
4042+ resourceId : number ,
4043+ value : number
4044+ ) : boolean {
4045+ const character = this . _characters [ entityId ] ;
4046+ if ( ! character ) return true ; // non-player resource - preserve existing behavior
4047+ const v = value >= 0 ? value : 0 ;
4048+ const last = character . _lastSentResources [ resourceId ] ;
4049+ const max = this . getResourceMaxValue ( resourceId as ResourceIds ) ;
4050+ let changed : boolean ;
4051+ if ( last === undefined || max <= 0 ) {
4052+ changed = last !== v ;
4053+ } else {
4054+ const quantum = ( n : number ) =>
4055+ Math . round ( ( n / max ) * RESOURCE_SEND_STEPS ) ;
4056+ changed =
4057+ quantum ( v ) !== quantum ( last ) ||
4058+ // always emit exact empty/full transitions (drive starving/dehydrated/full states)
4059+ ( ( v <= 0 || v >= max ) && v !== last ) ;
4060+ }
4061+ if ( changed ) character . _lastSentResources [ resourceId ] = v ;
4062+ return changed ;
4063+ }
4064+
40274065 updateResource (
40284066 client : Client ,
40294067 entityId : string ,
40304068 value : number ,
40314069 resourceId : ResourceIds ,
40324070 resourceType ?: number // most resources have the same id and type
40334071 ) {
4072+ if ( ! this . _shouldSendResourceEvent ( entityId , resourceId , value ) ) return ;
40344073 this . sendData < ResourceEvent > ( client , "ResourceEvent" , {
40354074 eventData : {
40364075 type : 3 ,
@@ -4051,6 +4090,7 @@ export class ZoneServer2016 extends EventEmitter {
40514090 resourceType : number ,
40524091 dictionary : EntityDictionary < BaseEntity >
40534092 ) {
4093+ if ( ! this . _shouldSendResourceEvent ( entityId , resourceId , value ) ) return ;
40544094 this . sendDataToAllWithSpawnedEntity < ResourceEvent > (
40554095 dictionary ,
40564096 entityId ,
0 commit comments