Skip to content

Commit d4d03b6

Browse files
committed
First end-to-end working version. Added side effect queue to route commands to live shared memory instead of uselessly writing them to the frame buffer.
1 parent 79f6fe0 commit d4d03b6

File tree

5 files changed

+126
-40
lines changed

5 files changed

+126
-40
lines changed

src/main/java/bwapi/BWClient.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public void startGame(BWClientConfiguration configuration) {
8080
break;
8181
}
8282
long frameDurationMillis = System.currentTimeMillis() - lastUpdateTimestampMillis;
83-
if (frameDurationMillis > configuration.asyncFrameDurationMillis && (client.clientData().gameData().getFrameCount() > 0 || ! configuration.asyncWaitOnFrameZero)) {
83+
if (botWrapper.frameDurationMillis > configuration.asyncFrameDurationMillis && (client.clientData().gameData().getFrameCount() > 0 || ! configuration.asyncWaitOnFrameZero)) {
8484
System.out.println("Client: Proceeding because frame " + botWrapper.getGame().getFrameCount() + " lasted " + frameDurationMillis + "ms");
8585
break;
8686
}
@@ -91,6 +91,7 @@ public void startGame(BWClientConfiguration configuration) {
9191
long frameDurationMillis = currentTimeMillis - lastUpdateTimestampMillis;
9292
lastUpdateTimestampMillis = currentTimeMillis;
9393
System.out.println("Client: Ending frame after " + frameDurationMillis + "ms");
94+
getGame().sideEffects.flush(client.clientData());
9495
client.update();
9596
if (!client.isConnected()) {
9697
System.out.println("Reconnecting...");
@@ -101,7 +102,5 @@ public void startGame(BWClientConfiguration configuration) {
101102
// TODO: Before exiting give async bot time to complete onEnd(), maybe via thread.join().
102103

103104
} while (configuration.autoContinue); // lgtm [java/constant-loop-condition]
104-
105-
106105
}
107106
}

src/main/java/bwapi/BotWrapper.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,6 @@ class BotWrapper {
5151
}
5252
}
5353

54-
void startBot() {
55-
if (configuration.async) {
56-
botThread.start();
57-
}
58-
}
59-
6054
Game getGame() {
6155
return game;
6256
}
@@ -68,6 +62,13 @@ boolean botIdle() {
6862
return idle || ! configuration.async;
6963
}
7064

65+
/**
66+
* True if there is a frame buffer with free capacity.
67+
*/
68+
boolean canBuffer() {
69+
return configuration.async && ! frameBuffer.full();
70+
}
71+
7172
void step() {
7273
if (configuration.async) {
7374
frameBuffer.enqueueFrame();

src/main/java/bwapi/Game.java

Lines changed: 23 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ public class Game {
9999
private Text.Size textSize = Text.Size.Default;
100100
private boolean latcom = true;
101101

102+
public final SideEffectQueue sideEffects = new SideEffectQueue();
103+
102104
Game(ClientData clientData) {
103105
this.clientData = clientData;
104106
}
@@ -305,37 +307,6 @@ void onFrame(final int frame) {
305307
getAllUnits().forEach(u -> u.updatePosition(frame));
306308
}
307309

308-
void addUnitCommand(final int type, final int unit, final int target, final int x, final int y, final int extra) {
309-
ClientData.UnitCommand unitCommand = GameDataUtils.addUnitCommand(gameData());
310-
unitCommand.setTid(type);
311-
unitCommand.setUnitIndex(unit);
312-
unitCommand.setTargetIndex(target);
313-
unitCommand.setX(x);
314-
unitCommand.setY(y);
315-
unitCommand.setExtra(extra);
316-
}
317-
318-
void addCommand(final CommandType type, final int value1, final int value2) {
319-
Command command = GameDataUtils.addCommand(gameData());
320-
command.setType(type);
321-
command.setValue1(value1);
322-
command.setValue2(value2);
323-
}
324-
325-
void addShape(final ShapeType type, final CoordinateType coordType, final int x1, final int y1, final int x2, final int y2, final int extra1, final int extra2, final int color, final boolean isSolid) {
326-
Shape shape = GameDataUtils.addShape(gameData());
327-
shape.setType(type);
328-
shape.setCtype(coordType);
329-
shape.setX1(x1);
330-
shape.setY1(y1);
331-
shape.setX2(x2);
332-
shape.setY2(y2);
333-
shape.setExtra1(extra1);
334-
shape.setExtra2(extra2);
335-
shape.setColor(color);
336-
shape.setIsSolid(isSolid);
337-
}
338-
339310
/**
340311
* Retrieves the set of all teams/forces. Forces are commonly seen in @UMS
341312
* game types and some others such as @TvB and the team versions of game types.
@@ -2688,4 +2659,25 @@ public int getDamageTo(final UnitType toType, final UnitType fromType, final Pla
26882659
public int getRandomSeed() {
26892660
return randomSeed;
26902661
}
2662+
2663+
/**
2664+
* Convenience method for adding a unit command from raw arguments.
2665+
*/
2666+
void addUnitCommand(final int type, final int unit, final int target, final int x, final int y, final int extra) {
2667+
sideEffects.enqueue(SideEffect.addUnitCommand(type, unit, target, x, y, extra));
2668+
}
2669+
2670+
/**
2671+
* Convenience method for adding a game command from raw arguments.
2672+
*/
2673+
void addCommand(final CommandType type, final int value1, final int value2) {
2674+
sideEffects.enqueue(SideEffect.addCommand(type, value1, value2));
2675+
}
2676+
2677+
/**
2678+
* Convenience method for adding a shape from raw arguments.
2679+
*/
2680+
void addShape(final ShapeType type, final CoordinateType coordType, final int x1, final int y1, final int x2, final int y2, final int extra1, final int extra2, final int color, final boolean isSolid) {
2681+
sideEffects.enqueue(SideEffect.addShape(type, coordType, x1, y1, x2, y2, extra1, extra2, color, isSolid));
2682+
}
26912683
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package bwapi;
2+
3+
import java.util.function.Consumer;
4+
import java.util.function.Function;
5+
6+
/**
7+
* A side effect is an interaction that a bot attempts to have with the game.
8+
* This entails sending a game or unit command, or drawing a shape.
9+
*/
10+
class SideEffect {
11+
12+
private Consumer<ClientData> application;
13+
14+
void apply(ClientData clientData) {
15+
application.accept(clientData);
16+
}
17+
18+
private SideEffect() {}
19+
20+
static SideEffect addUnitCommand(final int type, final int unit, final int target, final int x, final int y, final int extra) {
21+
SideEffect output = new SideEffect();
22+
output.application = (ClientData clientData) -> {
23+
ClientData.UnitCommand unitCommand = GameDataUtils.addUnitCommand(clientData.gameData());
24+
unitCommand.setTid(type);
25+
unitCommand.setUnitIndex(unit);
26+
unitCommand.setTargetIndex(target);
27+
unitCommand.setX(x);
28+
unitCommand.setY(y);
29+
unitCommand.setExtra(extra);
30+
};
31+
return output;
32+
}
33+
34+
static SideEffect addCommand(final CommandType type, final int value1, final int value2) {
35+
SideEffect output = new SideEffect();
36+
output.application = (ClientData clientData) -> {
37+
ClientData.Command command = GameDataUtils.addCommand(clientData.gameData());
38+
command.setType(type);
39+
command.setValue1(value1);
40+
command.setValue2(value2);
41+
};
42+
return output;
43+
}
44+
45+
static SideEffect addShape(final ShapeType type, final CoordinateType coordType, final int x1, final int y1, final int x2, final int y2, final int extra1, final int extra2, final int color, final boolean isSolid) {
46+
SideEffect output = new SideEffect();
47+
output.application = (ClientData clientData) -> {
48+
ClientData.Shape shape = GameDataUtils.addShape(clientData.gameData());
49+
shape.setType(type);
50+
shape.setCtype(coordType);
51+
shape.setX1(x1);
52+
shape.setY1(y1);
53+
shape.setX2(x2);
54+
shape.setY2(y2);
55+
shape.setExtra1(extra1);
56+
shape.setExtra2(extra2);
57+
shape.setColor(color);
58+
shape.setIsSolid(isSolid);
59+
};
60+
return output;
61+
}
62+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package bwapi;
2+
3+
import java.util.ArrayList;
4+
5+
/**
6+
* Queue of intended bot interactions with the game, to be flushed as JBWAPI returns control to StarCraft after a frame.
7+
*/
8+
public class SideEffectQueue {
9+
10+
private ArrayList<SideEffect> queue = new ArrayList<>();
11+
12+
/**
13+
* Includes a side effect to be sent back to BWAPI in the future.
14+
*
15+
* @param sideEffect
16+
* A side effect to be applied to the game state the next time the queue is flushed.
17+
*/
18+
synchronized void enqueue(SideEffect sideEffect) {
19+
queue.add(sideEffect);
20+
}
21+
22+
/**
23+
* Applies all enqueued side effects to the current BWAPI frame.
24+
*
25+
* @param liveClientData
26+
* The live game frame's data, using the BWAPI shared memory.
27+
*/
28+
synchronized void flush(ClientData liveClientData) {
29+
queue.forEach(x -> x.apply(liveClientData));
30+
queue.clear();
31+
}
32+
}

0 commit comments

Comments
 (0)