Skip to content

Commit 287441e

Browse files
author
Jasper Geurtz
committed
Only copy active units, events, and eventStrings in FrameBuffer
1 parent e7fea36 commit 287441e

2 files changed

Lines changed: 133 additions & 3 deletions

File tree

‎src/main/java/bwapi/FrameBuffer.java‎

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,22 @@
1212
*/
1313
class 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
}

‎src/test/java/bwapi/AsyncClientDataTest.java‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public void retainedViewsSurviveLiveAndPackedHandoffs() {
1212
PackedWrappedBuffer packed = new PackedWrappedBuffer(ClientData.GameData.SIZE);
1313
ClientData source = new ClientData();
1414
source.setBuffer(live);
15+
source.gameData().setEventCount(1);
1516
source.gameData().getEvents(0).setType(EventType.MatchFrame);
1617
source.gameData().getStartLocations(0).setX(123);
1718
new FrameBuffer(new BWClientConfiguration.Builder().withAsyncFrameBufferCapacity(1).build())
@@ -213,4 +214,46 @@ public void asyncClientDataWorksWithFullUnpackedBuffer() {
213214
assertTrue(asyncData.gameData().isVisible(10, 20));
214215
assertTrue(asyncData.gameData().isInGame());
215216
}
217+
218+
@Test
219+
public void copyBufferTracksMaxUnitsAndOnlyCopiesActiveUnitsAndEvents() {
220+
WrappedBuffer mainBuffer = new WrappedBuffer(ClientData.GameData.SIZE);
221+
PackedWrappedBuffer packedBuffer = new PackedWrappedBuffer();
222+
223+
ClientData mainData = new ClientData();
224+
mainData.setBuffer(mainBuffer);
225+
226+
// Set initialUnitCount = 3 and populate unit #2 and unit #50 (beyond initialUnitCount)
227+
mainData.gameData().setInitialUnitCount(3);
228+
mainData.gameData().getUnits(2).setHitPoints(100);
229+
mainData.gameData().getUnits(50).setHitPoints(250);
230+
231+
// Populate event #0 (active) and event #5 (beyond eventCount = 1)
232+
mainData.gameData().setEventCount(1);
233+
mainData.gameData().getEvents(0).setType(EventType.MatchFrame);
234+
mainData.gameData().getEvents(5).setType(EventType.MatchEnd);
235+
236+
FrameBuffer frameBuffer = new FrameBuffer(
237+
new BWClientConfiguration.Builder().withAsyncFrameBufferCapacity(1).build());
238+
frameBuffer.copyBuffer(mainBuffer, packedBuffer);
239+
240+
AsyncClientData asyncData = new AsyncClientData(mainBuffer);
241+
asyncData.setBuffer(packedBuffer);
242+
243+
assertEquals(3, frameBuffer.getMaxUnitCount());
244+
assertEquals(100, asyncData.gameData().getUnits(2).getHitPoints());
245+
// Unit #50 and Event #5 were not in the active range, so they were skipped
246+
assertEquals(0, asyncData.gameData().getUnits(50).getHitPoints());
247+
assertEquals(EventType.MatchFrame, asyncData.gameData().getEvents(0).getType());
248+
assertEquals(EventType.MatchStart, asyncData.gameData().getEvents(5).getType());
249+
250+
// Now fire UnitComplete for unit #50 on the next frame
251+
mainData.gameData().setEventCount(1);
252+
mainData.gameData().getEvents(0).setType(EventType.UnitComplete);
253+
mainData.gameData().getEvents(0).setV1(50);
254+
frameBuffer.copyBuffer(mainBuffer, packedBuffer);
255+
256+
assertEquals(51, frameBuffer.getMaxUnitCount());
257+
assertEquals(250, asyncData.gameData().getUnits(50).getHitPoints());
258+
}
216259
}

0 commit comments

Comments
 (0)