Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 29 additions & 64 deletions src/main/java/bwapi/BotWrapperAsync.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package bwapi;

import java.util.concurrent.locks.ReentrantLock;

/**
* Manages invocation of bot event handlers
*/
Expand All @@ -10,9 +8,7 @@ class BotWrapperAsync extends BotWrapper {
private final FrameBuffer frameBuffer;
private WrappedBuffer liveData;
private Thread botThread;
private Throwable lastBotThrow;
private final ReentrantLock lastBotThrowLock = new ReentrantLock();
private final ReentrantLock unsafeReadReadyLock = new ReentrantLock();
private volatile Throwable lastBotThrow;
private boolean unsafeReadReady = false;

BotWrapperAsync(BWClientConfiguration configuration, BWEventListener eventListener) {
Expand All @@ -29,7 +25,12 @@ class BotWrapperAsync extends BotWrapper {
void startNewGame(WrappedBuffer liveData, PerformanceMetrics performanceMetrics) {
frameBuffer.initialize(liveData, performanceMetrics);

super.startNewGame(liveData, performanceMetrics);
this.performanceMetrics = performanceMetrics;
ClientDataAsync clientDataAsync = new ClientDataAsync(liveData);
botGame = new Game(clientDataAsync);
botGame.setConfiguration(configuration);
botGame.botClientData().setBuffer(liveData);
gameOver = false;

liveClientData.setBuffer(liveData);
this.liveData = liveData;
Expand All @@ -43,32 +44,6 @@ void startNewGame(WrappedBuffer liveData, PerformanceMetrics performanceMetrics)
}


private boolean isUnsafeReadReady() {
unsafeReadReadyLock.lock();
try {
return unsafeReadReady;
}
finally {
unsafeReadReadyLock.unlock();
}
}

private void setUnsafeReadReady(boolean value) {
unsafeReadReadyLock.lock();
try {
unsafeReadReady = value;
}
finally {
unsafeReadReadyLock.unlock();
}
frameBuffer.lockSize.lock();
try {
frameBuffer.conditionSize.signalAll();
} finally {
frameBuffer.lockSize.unlock();
}
}

@Override
void onFrame() {
configuration.log("Main: onFrame asynchronous start");
Expand All @@ -89,9 +64,10 @@ void asyncOnFrame() {
if (frameBuffer.empty()) {
configuration.log("Main: Putting bot on live data");
botGame.botClientData().setBuffer(liveData);
setUnsafeReadReady(true);
unsafeReadReady = true;
frameBuffer.conditionSize.signalAll();
} else {
setUnsafeReadReady(false);
unsafeReadReady = false;
}
} finally {
frameBuffer.lockSize.unlock();
Expand All @@ -112,16 +88,6 @@ void asyncOnFrame() {
frameBuffer.lockSize.lock();
try {
while (!frameBuffer.empty()) {
// Unsafe mode: Move the bot off of live data onto the frame buffer
// This is the unsafe step!
// We don't synchronize on calls which access the buffer
// (to avoid tens of thousands of synchronized calls per frame)
// so there's no guarantee of safety here.
if (configuration.getAsyncUnsafe() && frameBuffer.size() == 1) {
configuration.log("Main: Weaning bot off live data");
botGame.botClientData().setBuffer(frameBuffer.peek());
}

// Make bot exceptions fall through to the main thread.
Throwable lastThrow = getLastBotThrow();
if (lastThrow != null) {
Expand All @@ -135,6 +101,14 @@ void asyncOnFrame() {
} else {
long remainingNanos = endNanos - System.nanoTime();
if (remainingNanos <= 0) {
// Unsafe mode: Move the bot off of live data onto the frame buffer
// only when the frame times out and control is about to return to StarCraft.
// When the bot finishes on time, liveData remains frozen for the entire frame.
if (configuration.getAsyncUnsafe() && frameBuffer.size() == 1) {
configuration.log("Main: Weaning bot off live data");
unsafeReadReady = false;
botGame.botClientData().setBuffer(frameBuffer.peek());
}
configuration.log("Main: Out of time in frame #" + frame);
break;
}
Expand Down Expand Up @@ -164,41 +138,34 @@ void endGame() {
}

Throwable getLastBotThrow() {
lastBotThrowLock.lock();
Throwable output = lastBotThrow;
lastBotThrowLock.unlock();
return output;
return lastBotThrow;
}

private Thread createBotThread() {
return new Thread(() -> {
try {
configuration.log("Bot: Thread started");
while (!gameOver) {

boolean doUnsafeRead = false;
configuration.log("Bot: Ready for another frame");
performanceMetrics.getBotIdle().startTiming();
frameBuffer.lockSize.lock();
try {
doUnsafeRead = isUnsafeReadReady();
while ( ! doUnsafeRead && frameBuffer.empty()) {
while (!unsafeReadReady && frameBuffer.empty()) {
configuration.log("Bot: Waiting for a frame");
frameBuffer.conditionSize.awaitUninterruptibly();
doUnsafeRead = isUnsafeReadReady();
}
performanceMetrics.getBotIdle().stopTiming();

if (unsafeReadReady) {
configuration.log("Bot: Reading live frame");
unsafeReadReady = false;
} else {
configuration.log("Bot: Peeking next frame from buffer");
botGame.botClientData().setBuffer(frameBuffer.peek());
}
} finally {
frameBuffer.lockSize.unlock();
}
performanceMetrics.getBotIdle().stopTiming();

if (doUnsafeRead) {
configuration.log("Bot: Reading live frame");
setUnsafeReadReady(false);
} else {
configuration.log("Bot: Peeking next frame from buffer");
botGame.botClientData().setBuffer(frameBuffer.peek());
}

configuration.log("Bot: Handling events on frame #" + botGame.getFrameCount());
handleEvents();
Expand All @@ -210,9 +177,7 @@ private Thread createBotThread() {
// Record the throw,
// Then allow the thread to terminate silently.
// The main thread will look for the stored throw.
lastBotThrowLock.lock();
lastBotThrow = throwable;
lastBotThrowLock.unlock();

// Awaken any threads waiting on bot progress
while (!frameBuffer.empty()) {
Expand Down
73 changes: 39 additions & 34 deletions src/main/java/bwapi/ClientData.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

import com.sun.jna.Pointer;

final class ClientData {
private WrappedBuffer buffer;
private GameData gameData;
class ClientData {
WrappedBuffer buffer;
GameData gameData;
ClientData() {
gameData = new ClientData.GameData(0);
gameData = createGameData(0);
}
GameData createGameData(int offset) {
return new ClientData.GameData(offset);
}
GameData gameData() {
return gameData;
Expand All @@ -17,6 +20,9 @@ void setBuffer(WrappedBuffer buffer) {
void setPointer(Pointer pointer) {
setBuffer(new WrappedBuffer(pointer, GameData.SIZE));
}
WrappedBuffer getStaticBuffer() {
return buffer;
}
class UnitCommand {
static final int SIZE = 24;
private int myOffset;
Expand Down Expand Up @@ -68,7 +74,7 @@ void setExtra(int value) {
}
class GameData {
static final int SIZE = 33017048;
private int myOffset;
protected int myOffset;
GameData(int myOffset) {
this.myOffset = myOffset;
}
Expand Down Expand Up @@ -729,7 +735,7 @@ void setValue2(int value) {
}
class Position {
static final int SIZE = 8;
private int myOffset;
protected int myOffset;
Position(int myOffset) {
this.myOffset = myOffset;
}
Expand All @@ -750,7 +756,7 @@ void setY(int value) {
}
class Event {
static final int SIZE = 12;
private int myOffset;
protected int myOffset;
Event(int myOffset) {
this.myOffset = myOffset;
}
Expand Down Expand Up @@ -784,94 +790,94 @@ class RegionData {
}
int getId() {
int offset = myOffset + 0;
return buffer.getInt(offset);
return getStaticBuffer().getInt(offset);
}
void setId(int value) {
buffer.putInt(myOffset + 0, value);
getStaticBuffer().putInt(myOffset + 0, value);
}
int islandID() {
int offset = myOffset + 4;
return buffer.getInt(offset);
return getStaticBuffer().getInt(offset);
}
void setIslandID(int value) {
buffer.putInt(myOffset + 4, value);
getStaticBuffer().putInt(myOffset + 4, value);
}
int getCenter_x() {
int offset = myOffset + 8;
return buffer.getInt(offset);
return getStaticBuffer().getInt(offset);
}
void setCenter_x(int value) {
buffer.putInt(myOffset + 8, value);
getStaticBuffer().putInt(myOffset + 8, value);
}
int getCenter_y() {
int offset = myOffset + 12;
return buffer.getInt(offset);
return getStaticBuffer().getInt(offset);
}
void setCenter_y(int value) {
buffer.putInt(myOffset + 12, value);
getStaticBuffer().putInt(myOffset + 12, value);
}
int getPriority() {
int offset = myOffset + 16;
return buffer.getInt(offset);
return getStaticBuffer().getInt(offset);
}
void setPriority(int value) {
buffer.putInt(myOffset + 16, value);
getStaticBuffer().putInt(myOffset + 16, value);
}
int getLeftMost() {
int offset = myOffset + 20;
return buffer.getInt(offset);
return getStaticBuffer().getInt(offset);
}
void setLeftMost(int value) {
buffer.putInt(myOffset + 20, value);
getStaticBuffer().putInt(myOffset + 20, value);
}
int getRightMost() {
int offset = myOffset + 24;
return buffer.getInt(offset);
return getStaticBuffer().getInt(offset);
}
void setRightMost(int value) {
buffer.putInt(myOffset + 24, value);
getStaticBuffer().putInt(myOffset + 24, value);
}
int getTopMost() {
int offset = myOffset + 28;
return buffer.getInt(offset);
return getStaticBuffer().getInt(offset);
}
void setTopMost(int value) {
buffer.putInt(myOffset + 28, value);
getStaticBuffer().putInt(myOffset + 28, value);
}
int getBottomMost() {
int offset = myOffset + 32;
return buffer.getInt(offset);
return getStaticBuffer().getInt(offset);
}
void setBottomMost(int value) {
buffer.putInt(myOffset + 32, value);
getStaticBuffer().putInt(myOffset + 32, value);
}
int getNeighborCount() {
int offset = myOffset + 36;
return buffer.getInt(offset);
return getStaticBuffer().getInt(offset);
}
void setNeighborCount(int value) {
buffer.putInt(myOffset + 36, value);
getStaticBuffer().putInt(myOffset + 36, value);
}
int getNeighbors(int i) {
int offset = myOffset + 40 + 4 * 1 * i;
return buffer.getInt(offset);
return getStaticBuffer().getInt(offset);
}
void setNeighbors(int i, int value) {
buffer.putInt(myOffset + 40 + 4 * 1 * i, value);
getStaticBuffer().putInt(myOffset + 40 + 4 * 1 * i, value);
}
boolean isAccessible() {
int offset = myOffset + 1064;
return buffer.getByte(offset) != 0;
return getStaticBuffer().getByte(offset) != 0;
}
void setIsAccessible(boolean value) {
buffer.putByte(myOffset + 1064, (byte) (value ? 1 : 0));
getStaticBuffer().putByte(myOffset + 1064, (byte) (value ? 1 : 0));
}
boolean isHigherGround() {
int offset = myOffset + 1065;
return buffer.getByte(offset) != 0;
return getStaticBuffer().getByte(offset) != 0;
}
void setIsHigherGround(boolean value) {
buffer.putByte(myOffset + 1065, (byte) (value ? 1 : 0));
getStaticBuffer().putByte(myOffset + 1065, (byte) (value ? 1 : 0));
}
}
class ForceData {
Expand Down Expand Up @@ -2002,4 +2008,3 @@ void setReplayID(int value) {
}
}
}

Loading
Loading