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+ static final int EVENTS_HEADER_SIZE = EVENTS_START - PackedWrappedBuffer .REGION_END ;
25+
26+ static final int EVENT_STRING_COUNT_OFFSET = 10706624 ;
27+ static final int EVENT_STRING_SIZE = 256 ;
28+ static final int MAX_EVENT_STRINGS = 1000 ;
29+
1530 private static final Unsafe unsafe = UnsafeTools .getUnsafe ();
1631
1732 private WrappedBuffer liveData ;
@@ -20,6 +35,7 @@ class FrameBuffer {
2035 private final int capacity ;
2136 private int stepGame = 0 ;
2237 private int stepBot = 0 ;
38+ private int maxUnitCount = 0 ;
2339 private final ArrayList <WrappedBuffer > dataBuffer = new ArrayList <>();
2440
2541 private final Lock lockWrite = new ReentrantLock ();
@@ -42,6 +58,7 @@ void initialize(WrappedBuffer liveData, PerformanceMetrics performanceMetrics) {
4258 this .performanceMetrics = performanceMetrics ;
4359 stepGame = 0 ;
4460 stepBot = 0 ;
61+ maxUnitCount = 0 ;
4562 }
4663
4764 /**
@@ -151,9 +168,53 @@ void dequeue() {
151168 } finally { lockSize .unlock (); }
152169 }
153170
171+ int getMaxUnitCount () {
172+ return maxUnitCount ;
173+ }
174+
175+ private static int clamp (int value , int min , int max ) {
176+ return Math .max (min , Math .min (max , value ));
177+ }
178+
179+ private int updateMaxUnitCount (WrappedBuffer source , int eventCount ) {
180+ int initialUnits = clamp (source .getInt (INITIAL_UNIT_COUNT_OFFSET ), 0 , MAX_UNITS );
181+ if (initialUnits > maxUnitCount ) {
182+ maxUnitCount = initialUnits ;
183+ }
184+
185+ for (int i = 0 ; i < eventCount ; i ++) {
186+ int eventOffset = EVENTS_START + i * ClientData .Event .SIZE ;
187+ if (source .getInt (eventOffset ) == EventType .UnitDiscover .id ) {
188+ int unitId = source .getInt (eventOffset + 4 );
189+ if (unitId >= 0 && unitId < MAX_UNITS && unitId + 1 > maxUnitCount ) {
190+ maxUnitCount = unitId + 1 ;
191+ }
192+ }
193+ }
194+ return maxUnitCount ;
195+ }
196+
197+ private static void copySlice (long srcAddr , long dstAddr , int srcOffset , int delta , int length ) {
198+ unsafe .copyMemory (srcAddr + srcOffset , dstAddr + srcOffset - delta , length );
199+ }
200+
154201 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 );
202+ long srcAddr = source .getAddress ();
203+ long dstAddr = destination .getAddress ();
204+
205+ int eventCount = clamp (source .getInt (EVENT_COUNT_OFFSET ), 0 , MAX_EVENTS );
206+ int eventStringCount = clamp (source .getInt (EVENT_STRING_COUNT_OFFSET ), 0 , MAX_EVENT_STRINGS );
207+ int activeUnits = updateMaxUnitCount (source , eventCount );
208+
209+ // Slice 1a (dynamic size): header, forces, players, initialUnitCount, and active units [0 .. activeUnits)
210+ copySlice (srcAddr , dstAddr , 0 , 0 , UNITS_START + activeUnits * ClientData .UnitData .SIZE );
211+ // Slice 1b (static size): unitArray, bullets, nukeDots, frameCount, and other post-unit Slice 1 fields
212+ copySlice (srcAddr , dstAddr , UNITS_END , 0 , PackedWrappedBuffer .STATICTILES_START - UNITS_END );
213+ // Slice 2 (static size): tile visibility, exploration, creep, and occupation flags
214+ copySlice (srcAddr , dstAddr , PackedWrappedBuffer .STATICTILES_END , PackedWrappedBuffer .DELTA_RANGE2 , PackedWrappedBuffer .DYNAMIC_SLICE2_SIZE );
215+ // Slice 3a (dynamic size): startLocations, game state flags, selectedUnits, self/enemy/neutral, eventCount, and active events [0 .. eventCount)
216+ copySlice (srcAddr , dstAddr , PackedWrappedBuffer .REGION_END , PackedWrappedBuffer .DELTA_RANGE3 , EVENTS_HEADER_SIZE + eventCount * ClientData .Event .SIZE );
217+ // Slice 3b (dynamic size): eventStringCount and active eventStrings [0 .. eventStringCount)
218+ copySlice (srcAddr , dstAddr , EVENT_STRING_COUNT_OFFSET , PackedWrappedBuffer .DELTA_RANGE3 , 4 + eventStringCount * EVENT_STRING_SIZE );
158219 }
159220}
0 commit comments