Skip to content

Commit bf020d9

Browse files
author
Jasper Geurtz
committed
wrappedbuffer-with-offsets
1 parent 8cbb524 commit bf020d9

File tree

3 files changed

+18
-87
lines changed

3 files changed

+18
-87
lines changed

src/main/java/bwapi/BotWrapperAsync.java

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,26 +22,25 @@ class BotWrapperAsync extends BotWrapper {
2222
// Use reduced priority to encourage Windows to give priority to StarCraft.exe/BWAPI.
2323
// If BWAPI doesn't get priority, it may not detect completion of a frame on our end in timely fashion.
2424
Thread.currentThread().setName("JBWAPI Client");
25-
Thread.currentThread().setPriority(Thread.NORM_PRIORITY - 1);
25+
Thread.currentThread().setPriority(4);
2626
}
2727

2828
/**
2929
* Resets the BotWrapper for a new botGame.
3030
*/
3131
void startNewGame(WrappedBuffer liveData, PerformanceMetrics performanceMetrics) {
32-
frameBuffer.initialize(liveData, performanceMetrics);
33-
3432
super.startNewGame(liveData, performanceMetrics);
33+
34+
frameBuffer.initialize(liveData, performanceMetrics);
3535
liveClientData.setBuffer(liveData);
3636
this.liveData = liveData;
3737

38-
// configuration.log("Main: Starting bot thread");
39-
// botThread = createBotThread();
40-
// botThread.setName("JBWAPI Bot");
41-
// // Reduced priority helps ensure that StarCraft.exe/BWAPI pick up on our frame completion in timely fashion
42-
// botThread.setPriority(Thread.NORM_PRIORITY - 2);
43-
// botThread.start();
44-
botThread = null;
38+
configuration.log("Main: Starting bot thread");
39+
botThread = createBotThread();
40+
botThread.setName("JBWAPI Bot");
41+
// Reduced priority helps ensure that StarCraft.exe/BWAPI pick up on our frame completion in timely fashion
42+
botThread.setPriority(3);
43+
botThread.start();
4544
}
4645

4746

@@ -82,15 +81,6 @@ void asyncOnFrame() {
8281
long startNanos = System.nanoTime();
8382
long endNanos = startNanos + (long) configuration.getMaxFrameDurationMs() * 1000000;
8483

85-
if (botThread == null) {
86-
configuration.log("Main: Starting bot thread");
87-
botThread = createBotThread();
88-
botThread.setName("JBWAPI Bot");
89-
// Reduced priority helps ensure that StarCraft.exe/BWAPI pick up on our frame completion in timely fashion
90-
botThread.setPriority(3);
91-
botThread.start();
92-
}
93-
9484
// Unsafe mode:
9585
// If the frame buffer is empty (meaning the bot must be idle)
9686
// allow the bot to read directly from shared memory while we copy it over

src/main/java/bwapi/FrameBuffer.java

Lines changed: 7 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package bwapi;
22

3-
import sun.misc.Unsafe;
4-
53
import java.util.ArrayList;
64
import java.util.concurrent.locks.Condition;
75
import java.util.concurrent.locks.Lock;
@@ -11,16 +9,12 @@
119
* Circular buffer of game states.
1210
*/
1311
class FrameBuffer {
14-
private static final int BUFFER_SIZE = ClientData.GameData.SIZE;
15-
private static final Unsafe unsafe = UnsafeTools.getUnsafe();
16-
17-
private WrappedBuffer liveData;
1812
private PerformanceMetrics performanceMetrics;
1913
private final BWClientConfiguration configuration;
2014
private final int capacity;
2115
private int stepGame = 0;
2216
private int stepBot = 0;
23-
private final ArrayList<WrappedBuffer> dataBuffer = new ArrayList<>();
17+
private final ArrayList<WrappedBufferOffset> dataBuffer = new ArrayList<>();
2418

2519
private final Lock lockWrite = new ReentrantLock();
2620
final Lock lockSize = new ReentrantLock();
@@ -30,18 +24,18 @@ class FrameBuffer {
3024
this.capacity = configuration.getAsyncFrameBufferCapacity();
3125
this.configuration = configuration;
3226
while(dataBuffer.size() < capacity) {
33-
dataBuffer.add(new WrappedBuffer(BUFFER_SIZE));
27+
dataBuffer.add(new WrappedBufferOffset());
3428
}
3529
}
3630

3731
/**
3832
* Resets for a new game
3933
*/
4034
void initialize(WrappedBuffer liveData, PerformanceMetrics performanceMetrics) {
41-
this.liveData = liveData;
4235
this.performanceMetrics = performanceMetrics;
4336
stepGame = 0;
4437
stepBot = 0;
38+
dataBuffer.forEach(b -> b.setSourceBuffer(liveData));
4539
}
4640

4741
/**
@@ -107,19 +101,10 @@ void enqueueFrame() {
107101
performanceMetrics.getIntentionallyBlocking().stopTiming();
108102
} finally { lockSize.unlock(); };
109103

110-
// For the first frame of the game, populate all buffers completely
111-
// This is to ensure all buffers have access to immutable data like regions/walkability/buildability
112-
// Afterwards, we want to shorten this process by only copying important and mutable data
113-
if (stepGame == 0) {
114-
for (WrappedBuffer frameBuffer : dataBuffer) {
115-
copyBuffer(liveData, frameBuffer, true);
116-
}
117-
} else {
118-
performanceMetrics.getCopyingToBuffer().time(() -> {
119-
WrappedBuffer dataTarget = dataBuffer.get(indexGame());
120-
copyBuffer(liveData, dataTarget, false);
121-
});
122-
}
104+
performanceMetrics.getCopyingToBuffer().time(() -> {
105+
WrappedBufferOffset dataTarget = dataBuffer.get(indexGame());
106+
dataTarget.initialize();
107+
});
123108

124109
lockSize.lock();
125110
try {
@@ -152,46 +137,4 @@ void dequeue() {
152137
conditionSize.signalAll();
153138
} finally { lockSize.unlock(); }
154139
}
155-
156-
/**
157-
*
158-
* @param source Address to copy from
159-
* @param destination Address to copy to
160-
* @param size Number of bytes to copy
161-
*/
162-
private void copyBuffer(WrappedBuffer source, WrappedBuffer destination, long offset, int size) {
163-
long addressSource = source.getAddress() + offset;
164-
long addressDestination = destination.getAddress() + offset;
165-
unsafe.copyMemory(addressSource, addressDestination, size);
166-
}
167-
168-
void copyBuffer(WrappedBuffer source, WrappedBuffer destination, boolean copyEverything) {
169-
/*
170-
The speed at which we copy data into the frame buffer is a major cost of JBWAPI's asynchronous operation.
171-
Copy times observed in the wild for the complete buffer usually range from 2.6ms - 19ms
172-
but are prone to large amounts of variance.
173-
174-
The normal Java way to execute this copy is via ByteBuffer.put(), which has reasonably good performance characteristics.
175-
*/
176-
177-
if (copyEverything) {
178-
copyBuffer(source, destination, 0, FrameBuffer.BUFFER_SIZE);
179-
} else {
180-
// After the buffer has been filled the first time,
181-
// we can omit copying blocks of data which are unused or which don't change after game start.
182-
// These blocks account for *most* of the 33MB shared memory,
183-
// so omitting them drastically reduces the copy duration
184-
final int STATICTILES_START = 3447004; // getGroundHeight, isWalkable, isBuildable
185-
final int STATICTILES_END = 4823260;
186-
final int REGION_START = 5085404; // getMapTileRegionId, ..., getRegions
187-
final int REGION_END = 10586480;
188-
final int STRINGSSHAPES_START = 10962632; // getStringCount, ... getShapes
189-
final int STRINGSHAPES_END = 32242636;
190-
final int UNITFINDER_START = 32962644;
191-
copyBuffer(source, destination, 0, STATICTILES_START);
192-
copyBuffer(source, destination, STATICTILES_END, REGION_START - STATICTILES_END);
193-
copyBuffer(source, destination, REGION_END, STRINGSSHAPES_START - REGION_END);
194-
copyBuffer(source, destination, STRINGSHAPES_END, UNITFINDER_START - STRINGSHAPES_END);
195-
}
196-
}
197140
}

src/main/java/bwapi/WrappedBufferOffset.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@
66
* <p> </p>For now this hardcodes a few things (size, offsets).
77
*/
88
class WrappedBufferOffset extends WrappedBuffer {
9-
// The "real" WrappedBuffer that contains the dynamic & static values.
10-
// WrappedBufferOffset makes a copy of the dynamic ones, but delegates
11-
// to the "real" one for the static values. This saves a lot of memory.
9+
1210
private WrappedBuffer sourceBuffer;
1311

1412
WrappedBufferOffset() {
@@ -165,7 +163,7 @@ static final class DynamicData {
165163
static int offset(int offset) {
166164
for (MemRegion r : MEM_REGIONS) {
167165
if (offset >= r.start && offset < r.end) {
168-
return offset - r.offset;
166+
return r.offset + (offset - r.start);
169167
}
170168
}
171169
return -1;

0 commit comments

Comments
 (0)