Skip to content

Commit 43c9ac3

Browse files
committed
Finished first pass async implementation, untested
1 parent 486bf4f commit 43c9ac3

File tree

4 files changed

+92
-42
lines changed

4 files changed

+92
-42
lines changed

src/main/java/bwapi/BWClient.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,19 @@ public void startGame(BWClientConfiguration configuration) {
5757
}
5858
client.update();
5959
}
60-
botWrapper = new BotWrapper(configuration, client.mapFile(), client.gameData(), handler);
61-
botWrapper.step();
60+
61+
botWrapper = new BotWrapper(configuration, client.mapFile(), client.clientData(), handler);
62+
6263
while (getGame().isInGame()) {
63-
client.update();
6464
botWrapper.step();
65+
client.update();
6566
if (!client.isConnected()) {
6667
System.out.println("Reconnecting...");
6768
client.reconnect();
6869
}
6970
}
7071

71-
// TODO: Before exiting give async bot time to complete onEnd().
72+
// TODO: Before exiting give async bot time to complete onEnd(), maybe via thread.join().
7273

7374
} while (configuration.autoContinue); // lgtm [java/constant-loop-condition]
7475

src/main/java/bwapi/BWClientConfiguration.java

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,11 @@ public class BWClientConfiguration {
2828
*/
2929
public boolean async = false;
3030

31-
/**
32-
* How frequently (in nanoseconds) to poll for the bot's event handlers completing. Acts as a floor on the bot's frame duration.
33-
*/
34-
public int asyncFrameDurationNanosMin = 500;
35-
3631
/**
3732
* If JBWAPI detects that this much time (in nanoseconds) has passed since a bot's event handlers began, returns control back to BWAPI.
3833
* Real-time human play typically uses the "fastest" game speed, which has 42.86ms (42,860ns) between frames.
3934
*/
40-
public int asyncFrameDurationNanosMax = 40000;
35+
public int asyncFrameDurationNanos = 40000;
4136

4237
/**
4338
* The maximum number of frames to buffer while waiting on a bot.
@@ -58,11 +53,8 @@ public class BWClientConfiguration {
5853
* Checks that the configuration is in a valid state. Throws an IllegalArgumentException if it isn't.
5954
*/
6055
public void validate() {
61-
if (async && asyncFrameDurationNanosMin <= 0) {
62-
throw new IllegalArgumentException("asyncFrameDurationNanosMin needs to be a positive number (it's how long JBWAPI waits to poll for a completed frame.");
63-
}
64-
if (async && asyncFrameDurationNanosMax < 0) {
65-
throw new IllegalArgumentException("asyncFrameDurationNanosMax needs to be a non-negative number (it's how long JBWAPI waits for a bot response before returning control to BWAPI).");
56+
if (async && asyncFrameDurationNanos < 0) {
57+
throw new IllegalArgumentException("asyncFrameDurationNanos needs to be a non-negative number (it's how long JBWAPI waits for a bot response before returning control to BWAPI).");
6658
}
6759
if (async && asyncFrameBufferSize < 1) {
6860
throw new IllegalArgumentException("asyncFrameBufferSize needs to be a positive number (There needs to be at least one frame buffer).");

src/main/java/bwapi/BotWrapper.java

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,36 +30,59 @@ of this software and associated documentation files (the "Software"), to deal
3030
/**
3131
* Manages invocation of bot event handlers
3232
*/
33-
public class BotWrapper {
34-
private BWClientConfiguration configuration;
35-
private ByteBuffer sharedMemory;
36-
private ClientData.GameData gameData;
37-
private EventHandler eventHandler;
38-
private FrameBuffer frameBuffer;
33+
class BotWrapper {
34+
private final BWClientConfiguration configuration;
35+
private final ByteBuffer sharedMemory;
36+
private final ClientData clientData;
37+
private final EventHandler eventHandler;
38+
private final FrameBuffer frameBuffer;
39+
private final Thread botThread;
3940

40-
public BotWrapper(
41+
BotWrapper(
4142
BWClientConfiguration configuration,
4243
ByteBuffer sharedMemory,
43-
ClientData.GameData gameData,
44+
ClientData clientData,
4445
EventHandler eventHandler) {
4546
this.configuration = configuration;
4647
this.sharedMemory = sharedMemory;
47-
this.gameData = gameData;
48+
this.clientData = clientData;
4849
this.eventHandler = eventHandler;
4950

5051
if (configuration.async) {
5152
frameBuffer = new FrameBuffer(configuration.asyncFrameBufferSize);
53+
botThread = new Thread(() -> {
54+
while(true) {
55+
while(frameBuffer.empty()) try {
56+
frameBuffer.wait();
57+
} catch (InterruptedException ignored) {}
58+
59+
frameBuffer.dequeueFrame(clientData);
60+
handleEvents();
61+
if (!clientData.new GameData(0).isInGame()) {
62+
return;
63+
}
64+
}
65+
});
66+
} else {
67+
frameBuffer = null;
68+
botThread = null;
5269
}
5370
}
5471

55-
public void step() {
72+
void step() {
5673
if (configuration.async) {
57-
// TODO: Synchronize
58-
frameBuffer.enqueueFrame();
74+
frameBuffer.enqueueFrame(sharedMemory);
75+
frameBuffer.notifyAll();
5976
} else {
60-
for (int i = 0; i < gameData.getEventCount(); i++) {
61-
eventHandler.operation(gameData.getEvents(i));
62-
}
77+
handleEvents();
6378
}
6479
}
80+
81+
private void handleEvents() {
82+
for (int i = 0; i < clientData.new GameData(0).getEventCount(); i++) {
83+
eventHandler.operation(clientData.new GameData(0).getEvents(i));
84+
}
85+
}
86+
87+
6588
}

src/main/java/bwapi/FrameBuffer.java

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ of this software and associated documentation files (the "Software"), to deal
2626
package bwapi;
2727

2828
import java.nio.ByteBuffer;
29+
import java.util.ArrayList;
30+
import java.util.concurrent.locks.Lock;
31+
import java.util.concurrent.locks.ReentrantLock;
2932

3033
/**
3134
* Circular buffer of game states.
32-
* Doesn't attempt to manage overruns or synchronization.
3335
*/
3436
public class FrameBuffer {
3537

@@ -38,41 +40,73 @@ public class FrameBuffer {
3840
private int stepGame = 0;
3941
private int stepBot = 0;
4042

43+
// Synchronization locks
44+
private Object stepCount;
45+
private ArrayList<Lock> frameLocks;
46+
4147
FrameBuffer(int size) {
4248
this.size = size;
4349
data = ByteBuffer.allocateDirect(size * ClientData.GameData.SIZE);
50+
while (frameLocks.size() < size) {
51+
frameLocks.add(new ReentrantLock());
52+
}
4453
}
4554

4655
/**
4756
* @return The number of frames currently buffered ahead of the bot's current frame
4857
*/
49-
public int framesBuffered() {
50-
return stepGame - stepBot;
58+
int framesBuffered() {
59+
synchronized (stepCount) {
60+
return stepGame - stepBot;
61+
}
62+
}
63+
64+
/**
65+
* @return Whether the frame buffer is empty and has no frames available for the bot to consume.
66+
*/
67+
boolean empty() {
68+
synchronized (stepCount) {
69+
return framesBuffered() <= 0;
70+
}
5171
}
5272

5373
/**
5474
* @return Whether the frame buffer is full and can not buffer any additional frames.
5575
* When the frame buffer is full, JBWAPI must wait for the bot to complete a frame before returning control to StarCraft.
5676
*/
57-
public boolean full() {
58-
return framesBuffered() >= size - 1;
77+
boolean full() {
78+
synchronized (stepCount) {
79+
return framesBuffered() >= size - 1;
80+
}
5981
}
6082

6183
/**
6284
* Copy data from shared memory into the head of the frame buffer.
63-
* Does not attempt to check bounds or handle synchronization.
6485
*/
65-
public void enqueueFrame() {
66-
// TODO: Enqueue
67-
++stepGame;
86+
void enqueueFrame(ByteBuffer source) {
87+
while(full()) try {
88+
wait();
89+
} catch (InterruptedException ignored) {}
90+
synchronized (stepCount) {
91+
data.put(source.array(), indexGame() * ClientData.GameData.SIZE, ClientData.GameData.SIZE);
92+
++stepGame;
93+
}
94+
notifyAll();
6895
}
6996

7097
/**
7198
* Points the bot to the next frame in the buffer.
7299
*/
73-
public void dequeueFrame() {
74-
// TODO: Dequeue
75-
++stepBot;
100+
void dequeueFrame(ClientData clientData) {
101+
while(empty()) try {
102+
wait();
103+
} catch (InterruptedException ignored) {}
104+
synchronized (stepCount) {
105+
clientData.setBuffer(data);
106+
clientData.setBufferOffset(indexBot() * ClientData.GameData.SIZE);
107+
++stepBot;
108+
}
109+
notifyAll();
76110
}
77111

78112
private int indexGame() {

0 commit comments

Comments
 (0)