1212 */
1313class FrameBuffer {
1414 static final int BUFFER_SIZE = PackedWrappedBuffer .BUFFER_SIZE ;
15+
16+ static final int INITIAL_UNIT_COUNT_OFFSET = 69648 ;
17+ static final int UNITS_START = 69656 ;
18+ static final int MAX_UNITS = 10000 ;
19+ static final int UNITS_END = UNITS_START + MAX_UNITS * ClientData .UnitData .SIZE ;
20+
21+ static final int EVENT_COUNT_OFFSET = 10586620 ;
22+ static final int EVENTS_START = 10586624 ;
23+ static final int MAX_EVENTS = 10000 ;
24+
25+ static final int EVENT_STRING_COUNT_OFFSET = 10706624 ;
26+ static final int EVENT_STRINGS_START = 10706628 ;
27+ static final int EVENT_STRING_SIZE = 256 ;
28+ static final int MAX_EVENT_STRINGS = 1000 ;
29+ static final int STRING_COUNT_OFFSET = 10962628 ;
30+
1531 private static final Unsafe unsafe = UnsafeTools .getUnsafe ();
1632
1733 private WrappedBuffer liveData ;
@@ -20,6 +36,7 @@ class FrameBuffer {
2036 private final int capacity ;
2137 private int stepGame = 0 ;
2238 private int stepBot = 0 ;
39+ private int maxUnitCount = 0 ;
2340 private final ArrayList <WrappedBuffer > dataBuffer = new ArrayList <>();
2441
2542 private final Lock lockWrite = new ReentrantLock ();
@@ -42,6 +59,7 @@ void initialize(WrappedBuffer liveData, PerformanceMetrics performanceMetrics) {
4259 this .performanceMetrics = performanceMetrics ;
4360 stepGame = 0 ;
4461 stepBot = 0 ;
62+ maxUnitCount = 0 ;
4563 }
4664
4765 /**
@@ -151,9 +169,78 @@ void dequeue() {
151169 } finally { lockSize .unlock (); }
152170 }
153171
172+ int getMaxUnitCount () {
173+ return maxUnitCount ;
174+ }
175+
176+ private static int clamp (int value , int min , int max ) {
177+ return Math .max (min , Math .min (max , value ));
178+ }
179+
180+ private static boolean isUnitEvent (int eventTypeId ) {
181+ return (eventTypeId >= EventType .UnitDiscover .id && eventTypeId <= EventType .UnitRenegade .id )
182+ || eventTypeId == EventType .UnitComplete .id ;
183+ }
184+
154185 void copyBuffer (WrappedBuffer source , WrappedBuffer destination ) {
155- unsafe .copyMemory (source .getAddress () + 0 , destination .getAddress () + PackedWrappedBuffer .DEST_SLICE1_OFFSET , PackedWrappedBuffer .DYNAMIC_SLICE1_SIZE );
156- unsafe .copyMemory (source .getAddress () + PackedWrappedBuffer .STATICTILES_END , destination .getAddress () + PackedWrappedBuffer .DEST_SLICE2_OFFSET , PackedWrappedBuffer .DYNAMIC_SLICE2_SIZE );
157- unsafe .copyMemory (source .getAddress () + PackedWrappedBuffer .REGION_END , destination .getAddress () + PackedWrappedBuffer .DEST_SLICE3_OFFSET , PackedWrappedBuffer .DYNAMIC_SLICE3_SIZE );
186+ long srcAddr = source .getAddress ();
187+ long dstAddr = destination .getAddress ();
188+
189+ int initialUnits = clamp (source .getInt (INITIAL_UNIT_COUNT_OFFSET ), 0 , MAX_UNITS );
190+ if (initialUnits > maxUnitCount ) {
191+ maxUnitCount = initialUnits ;
192+ }
193+
194+ int eventCount = clamp (source .getInt (EVENT_COUNT_OFFSET ), 0 , MAX_EVENTS );
195+ for (int i = 0 ; i < eventCount ; i ++) {
196+ int eventOffset = EVENTS_START + i * ClientData .Event .SIZE ;
197+ int eventType = source .getInt (eventOffset );
198+ if (isUnitEvent (eventType )) {
199+ int unitId = source .getInt (eventOffset + 4 );
200+ if (unitId >= 0 && unitId < MAX_UNITS && unitId + 1 > maxUnitCount ) {
201+ maxUnitCount = unitId + 1 ;
202+ }
203+ }
204+ }
205+
206+ int eventStringCount = clamp (source .getInt (EVENT_STRING_COUNT_OFFSET ), 0 , MAX_EVENT_STRINGS );
207+
208+ // Slice 1a: header, forces, players, initialUnitCount, and active units [0 .. maxUnitCount)
209+ int slice1ActiveUnitsEnd = UNITS_START + maxUnitCount * ClientData .UnitData .SIZE ;
210+ unsafe .copyMemory (srcAddr , dstAddr + PackedWrappedBuffer .DEST_SLICE1_OFFSET , slice1ActiveUnitsEnd );
211+
212+ // Slice 1b: unitArray, bullets, nukeDots, frameCount, and other post-unit Slice 1 fields
213+ unsafe .copyMemory (
214+ srcAddr + UNITS_END ,
215+ dstAddr + PackedWrappedBuffer .DEST_SLICE1_OFFSET + UNITS_END ,
216+ PackedWrappedBuffer .STATICTILES_START - UNITS_END );
217+
218+ // Slice 2: dynamic tile flags (isVisible, isExplored, hasCreep, isOccupied)
219+ unsafe .copyMemory (
220+ srcAddr + PackedWrappedBuffer .STATICTILES_END ,
221+ dstAddr + PackedWrappedBuffer .DEST_SLICE2_OFFSET ,
222+ PackedWrappedBuffer .DYNAMIC_SLICE2_SIZE );
223+
224+ // Slice 3a: startLocations, game flags, selectedUnits, self/enemy/neutral, eventCount, and active events [0 .. eventCount)
225+ int slice3ActiveEventsSize = (EVENTS_START - PackedWrappedBuffer .REGION_END ) + eventCount * ClientData .Event .SIZE ;
226+ unsafe .copyMemory (
227+ srcAddr + PackedWrappedBuffer .REGION_END ,
228+ dstAddr + PackedWrappedBuffer .DEST_SLICE3_OFFSET ,
229+ slice3ActiveEventsSize );
230+
231+ // Slice 3b: eventStringCount and active eventStrings [0 .. eventStringCount)
232+ int eventStringOffsetInSlice3 = EVENT_STRING_COUNT_OFFSET - PackedWrappedBuffer .REGION_END ;
233+ int slice3ActiveEventStringsSize = 4 + eventStringCount * EVENT_STRING_SIZE ;
234+ unsafe .copyMemory (
235+ srcAddr + EVENT_STRING_COUNT_OFFSET ,
236+ dstAddr + PackedWrappedBuffer .DEST_SLICE3_OFFSET + eventStringOffsetInSlice3 ,
237+ slice3ActiveEventStringsSize );
238+
239+ // Slice 3c: stringCount (4 bytes at end of Slice 3)
240+ int stringCountOffsetInSlice3 = STRING_COUNT_OFFSET - PackedWrappedBuffer .REGION_END ;
241+ unsafe .copyMemory (
242+ srcAddr + STRING_COUNT_OFFSET ,
243+ dstAddr + PackedWrappedBuffer .DEST_SLICE3_OFFSET + stringCountOffsetInSlice3 ,
244+ 4 );
158245 }
159246}
0 commit comments