Skip to content

Commit 165db79

Browse files
committed
Assigning the asynchronous bot perspective to Game
1 parent 43c9ac3 commit 165db79

14 files changed

+768
-769
lines changed

src/main/java/bwapi/BWClient.java

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88
public class BWClient {
99
private final BWEventListener eventListener;
10-
private EventHandler handler;
10+
private BotWrapper botWrapper;
1111

1212
public BWClient(final BWEventListener eventListener) {
1313
Objects.requireNonNull(eventListener);
@@ -16,9 +16,10 @@ public BWClient(final BWEventListener eventListener) {
1616

1717
/**
1818
* Get the {@link Game} instance of the currently running game.
19+
* When running in asynchronous mode, this is the game from the bot's perspective, eg. potentially a previous frame.
1920
*/
2021
public Game getGame() {
21-
return handler == null ? null : handler.getGame();
22+
return botWrapper == null ? null : botWrapper.getGame();
2223
}
2324

2425
public void startGame() {
@@ -47,21 +48,44 @@ public void startGame(BWClientConfiguration configuration) {
4748
configuration.validate();
4849
Client client = new Client(configuration);
4950
client.reconnect();
50-
handler = new EventHandler(eventListener, client);
5151

5252
do {
53-
BotWrapper botWrapper;
53+
long lastUpdateTimestampMillis = 0;
54+
System.out.println("Client: Starting game");
5455
while (!getGame().isInGame()) {
5556
if (!client.isConnected()) {
5657
return;
5758
}
59+
System.out.println("Client: connected.");
60+
lastUpdateTimestampMillis = System.currentTimeMillis();
5861
client.update();
5962
}
6063

61-
botWrapper = new BotWrapper(configuration, client.mapFile(), client.clientData(), handler);
64+
System.out.println("Client: Creating bot wrapper");
65+
BotWrapper botWrapper = new BotWrapper(configuration, eventListener, client.mapFile());
6266

6367
while (getGame().isInGame()) {
68+
System.out.println("Client: Stepping bot wrapper");
6469
botWrapper.step();
70+
System.out.println("Client: Waiting for idle bot or frame duration");
71+
72+
// Proceed immediately once framebuffer is empty
73+
// Otherwise, wait for bot to catch up
74+
// TODO: Replace with a wait instead of a sleep
75+
// TODO: Respect configuration.asyncWaitOnFrameZero
76+
while ( ! botWrapper.botIdle()) {
77+
long frameDuration = System.currentTimeMillis() - lastUpdateTimestampMillis;
78+
if (frameDuration > configuration.asyncFrameDurationMillis && (client.clientData().gameData().getFrameCount() > 0 || ! configuration.asyncWaitOnFrameZero)) {
79+
System.out.println("Client: Exceeded frame duration while waiting for bot: " + frameDuration + "ms on frame " + client.clientData().gameData().getFrameCount());
80+
break;
81+
}
82+
try {
83+
Thread.sleep(1);
84+
} catch (InterruptedException ignored) {}
85+
}
86+
87+
System.out.println("Client: Ending frame. Frames buffered: ");
88+
lastUpdateTimestampMillis = System.currentTimeMillis();
6589
client.update();
6690
if (!client.isConnected()) {
6791
System.out.println("Reconnecting...");

src/main/java/bwapi/BWClientConfiguration.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ public class BWClientConfiguration {
2929
public boolean async = false;
3030

3131
/**
32-
* If JBWAPI detects that this much time (in nanoseconds) has passed since a bot's event handlers began, returns control back to BWAPI.
32+
* If JBWAPI detects that this much time (in milliseconds) has passed since a bot's event handlers began, returns control back to BWAPI.
3333
* Real-time human play typically uses the "fastest" game speed, which has 42.86ms (42,860ns) between frames.
3434
*/
35-
public int asyncFrameDurationNanos = 40000;
35+
public int asyncFrameDurationMillis = 40000;
3636

3737
/**
3838
* The maximum number of frames to buffer while waiting on a bot.
@@ -53,8 +53,8 @@ public class BWClientConfiguration {
5353
* Checks that the configuration is in a valid state. Throws an IllegalArgumentException if it isn't.
5454
*/
5555
public void validate() {
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).");
56+
if (async && asyncFrameDurationMillis < 0) {
57+
throw new IllegalArgumentException("asyncFrameDurationMillis needs to be a non-negative number (it's how long JBWAPI waits for a bot response before returning control to BWAPI).");
5858
}
5959
if (async && asyncFrameBufferSize < 1) {
6060
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: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,63 +26,74 @@ of this software and associated documentation files (the "Software"), to deal
2626
package bwapi;
2727

2828
import java.nio.ByteBuffer;
29+
import java.util.EventListener;
2930

3031
/**
3132
* Manages invocation of bot event handlers
3233
*/
3334
class BotWrapper {
3435
private final BWClientConfiguration configuration;
35-
private final ByteBuffer sharedMemory;
36-
private final ClientData clientData;
37-
private final EventHandler eventHandler;
36+
private final BWEventListener eventListener;
37+
private final Game game;
3838
private final FrameBuffer frameBuffer;
3939
private final Thread botThread;
4040

41-
BotWrapper(
42-
BWClientConfiguration configuration,
43-
ByteBuffer sharedMemory,
44-
ClientData clientData,
45-
EventHandler eventHandler) {
41+
BotWrapper(BWClientConfiguration configuration, BWEventListener eventListener, ByteBuffer dataSource) {
4642
this.configuration = configuration;
47-
this.sharedMemory = sharedMemory;
48-
this.clientData = clientData;
49-
this.eventHandler = eventHandler;
43+
this.eventListener = eventListener;
44+
45+
ClientData currentClientData = new ClientData();
46+
currentClientData.setBuffer(dataSource);
47+
game = new Game(currentClientData);
5048

5149
if (configuration.async) {
52-
frameBuffer = new FrameBuffer(configuration.asyncFrameBufferSize);
50+
frameBuffer = new FrameBuffer(configuration.asyncFrameBufferSize, dataSource);
5351
botThread = new Thread(() -> {
5452
while(true) {
55-
while(frameBuffer.empty()) try {
56-
frameBuffer.wait();
57-
} catch (InterruptedException ignored) {}
53+
while(frameBuffer.empty()) try { Thread.sleep(0, 100); } catch (InterruptedException ignored) {}
54+
55+
System.out.println("Bot thread: Dequeuing frame. There are " + frameBuffer.framesBuffered() + " frames buffered.");
56+
game.clientData().setBuffer(frameBuffer.dequeueFrame());
5857

59-
frameBuffer.dequeueFrame(clientData);
58+
System.out.println("Bot thread: Handling events.");
6059
handleEvents();
61-
if (!clientData.new GameData(0).isInGame()) {
60+
if (!game.clientData().gameData().isInGame()) {
61+
System.out.println("Bot thread: Exiting.");
6262
return;
63+
} else {
64+
System.out.println("Bot thread: Handled events.");
6365
}
6466
}
6567
});
68+
botThread.setName("JBWAPI Bot");
69+
botThread.start();
6670
} else {
6771
frameBuffer = null;
6872
botThread = null;
6973
}
7074
}
7175

76+
Game getGame() {
77+
return game;
78+
}
79+
80+
boolean botIdle() {
81+
// TODO: This returns true if the bot is still processing the newest frame, leaving the bot permanently one frame behind
82+
return frameBuffer.empty();
83+
}
84+
7285
void step() {
7386
if (configuration.async) {
74-
frameBuffer.enqueueFrame(sharedMemory);
75-
frameBuffer.notifyAll();
87+
frameBuffer.enqueueFrame();
7688
} else {
7789
handleEvents();
7890
}
7991
}
8092

8193
private void handleEvents() {
82-
for (int i = 0; i < clientData.new GameData(0).getEventCount(); i++) {
83-
eventHandler.operation(clientData.new GameData(0).getEvents(i));
94+
ClientData.GameData gameData = game.clientData().gameData();
95+
for (int i = 0; i < gameData.getEventCount(); i++) {
96+
EventHandler.operation(eventListener, game, gameData.getEvents(i));
8497
}
8598
}
86-
87-
8899
}

src/main/java/bwapi/Client.java

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,10 @@ interface MappingKernel extends Kernel32 {
4141
HANDLE OpenFileMapping(int desiredAccess, boolean inherit, String name);
4242
}
4343

44-
public interface EventHandler {
45-
void operation(ClientData.Event event);
46-
}
47-
4844
private static final int READ_WRITE = 0x1 | 0x2 | 0x4;
4945
private static final int SUPPORTED_BWAPI_VERSION = 10003;
5046

5147
private ClientData clientData;
52-
private ClientData.GameData gameData;
5348
private boolean connected = false;
5449
private RandomAccessFile pipeObjectHandle = null;
5550
private ByteBuffer gameTableFileHandle = null;
@@ -67,17 +62,12 @@ public interface EventHandler {
6762
Client(ByteBuffer buffer) {
6863
clientData = new ClientData();
6964
clientData.setBuffer(buffer);
70-
gameData = clientData.new GameData(0);
7165
}
7266

7367
ClientData clientData() {
7468
return clientData;
7569
}
7670

77-
GameData gameData() {
78-
return gameData;
79-
}
80-
8171
ByteBuffer mapFile() {
8272
return mapFileHandle;
8373
}
@@ -92,7 +82,7 @@ void reconnect() {
9282
}
9383
}
9484

95-
void disconnect() {
85+
private void disconnect() {
9686
if (configuration.debugConnection) {
9787
System.err.print("Disconnect called by: ");
9888
System.err.println(Thread.currentThread().getStackTrace()[2]);
@@ -113,7 +103,7 @@ void disconnect() {
113103

114104
gameTableFileHandle = null;
115105
mapFileHandle = null;
116-
gameData = null;
106+
clientData = null;
117107
connected = false;
118108
}
119109

@@ -204,7 +194,6 @@ boolean connect() {
204194
try {
205195
clientData = new ClientData();
206196
clientData.setBuffer(mapFileHandle);
207-
gameData = clientData.new GameData(0);
208197
}
209198
catch (Exception e) {
210199
System.err.println("Unable to map game data.");
@@ -214,10 +203,10 @@ boolean connect() {
214203
return false;
215204
}
216205

217-
if (SUPPORTED_BWAPI_VERSION != gameData.getClient_version()) {
206+
if (SUPPORTED_BWAPI_VERSION != clientData.gameData().getClient_version()) {
218207
System.err.println("Error: Client and Server are not compatible!");
219208
System.err.println("Client version: " + SUPPORTED_BWAPI_VERSION);
220-
System.err.println("Server version: " + gameData.getClient_version());
209+
System.err.println("Server version: " + clientData.gameData().getClient_version());
221210
disconnect();
222211
sleep(2000);
223212
return false;

0 commit comments

Comments
 (0)