2014-08-05 04:43:14 -04:00
|
|
|
package bwapi;
|
|
|
|
|
|
|
|
import java.io.*;
|
|
|
|
import java.io.File;
|
|
|
|
import java.lang.Exception;
|
|
|
|
import java.lang.UnsupportedOperationException;
|
|
|
|
import java.util.*;
|
2017-04-11 18:37:30 -04:00
|
|
|
import java.util.regex.Pattern;
|
2014-08-05 04:43:14 -04:00
|
|
|
|
|
|
|
public class Mirror {
|
|
|
|
|
2017-04-14 17:51:38 -04:00
|
|
|
public static final String LOG_TAG = "[BWMirror] ";
|
|
|
|
|
2014-08-05 04:43:14 -04:00
|
|
|
private Game game;
|
|
|
|
|
|
|
|
private AIModule module = new AIModule();
|
|
|
|
|
2017-04-11 18:37:30 -04:00
|
|
|
private static void extractResourceFile(String resourceFilename, String outputFilename) throws Exception {
|
|
|
|
InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceFilename);
|
|
|
|
if (in == null)
|
|
|
|
throw new FileNotFoundException("Resource file not found: " + resourceFilename);
|
|
|
|
FileOutputStream out;
|
|
|
|
try {
|
|
|
|
out = new FileOutputStream(outputFilename);
|
|
|
|
} catch (Exception e) {
|
|
|
|
throw new FileNotFoundException("Could not open output file: " + outputFilename);
|
|
|
|
}
|
|
|
|
byte[] buffer = new byte[4096];
|
|
|
|
int bytesRead;
|
|
|
|
while ((bytesRead = in.read(buffer, 0, buffer.length)) != -1) {
|
|
|
|
out.write(buffer, 0, bytesRead);
|
|
|
|
}
|
|
|
|
out.close();
|
|
|
|
in.close();
|
|
|
|
}
|
2014-08-05 04:43:14 -04:00
|
|
|
|
2017-04-11 18:37:30 -04:00
|
|
|
private static boolean extractAndLoadNativeLibraries() {
|
|
|
|
try {
|
2017-04-14 17:51:38 -04:00
|
|
|
System.out.println(LOG_TAG + "Extracting bwapi_bridge.dll");
|
2017-04-11 18:37:30 -04:00
|
|
|
extractResourceFile("bwapi_bridge.dll", "./bwapi_bridge.dll");
|
|
|
|
|
2017-04-14 17:51:38 -04:00
|
|
|
System.out.println(LOG_TAG + "Extracting libgmp-10.dll");
|
2017-04-11 18:37:30 -04:00
|
|
|
extractResourceFile("libgmp-10.dll", "./libgmp-10.dll");
|
|
|
|
|
2017-04-14 17:51:38 -04:00
|
|
|
System.out.println(LOG_TAG + "Extracting libmpfr-4.dll");
|
2017-04-11 18:37:30 -04:00
|
|
|
extractResourceFile("libmpfr-4.dll", "./libmpfr-4.dll");
|
|
|
|
|
2017-04-14 17:51:38 -04:00
|
|
|
System.out.println(LOG_TAG + "Loading native library bwapi_bridge.dll");
|
2017-04-11 18:37:30 -04:00
|
|
|
System.load(new File("./bwapi_bridge.dll").getAbsolutePath());
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
return false;
|
2014-08-05 04:43:14 -04:00
|
|
|
}
|
2017-04-11 18:37:30 -04:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static boolean extractBwtaDataFiles() {
|
2014-08-05 04:43:14 -04:00
|
|
|
try {
|
2017-04-11 18:37:30 -04:00
|
|
|
Collection<String> bwtaFilenames = ResourceList.getResources(Pattern.compile("bwapi\\-data/BWTA2/[a-zA-Z0-9]+\\.bwta"));
|
2014-08-05 04:43:14 -04:00
|
|
|
|
2017-04-14 17:51:38 -04:00
|
|
|
System.out.println(LOG_TAG + "Creating ./bwapi-data/BWTA2 directory");
|
2017-04-11 18:37:30 -04:00
|
|
|
new File("./bwapi-data/BWTA2").mkdirs();
|
2014-08-05 04:43:14 -04:00
|
|
|
|
2017-04-11 18:37:30 -04:00
|
|
|
for (String filename : bwtaFilenames) {
|
2017-04-14 17:51:38 -04:00
|
|
|
System.out.println(LOG_TAG + "Extracting " + filename);
|
2017-04-11 18:37:30 -04:00
|
|
|
String outputFilename = "./" + filename;
|
|
|
|
extractResourceFile(filename, outputFilename);
|
2014-08-05 04:43:14 -04:00
|
|
|
}
|
2017-04-11 18:37:30 -04:00
|
|
|
|
2014-08-05 04:43:14 -04:00
|
|
|
} catch (Exception e) {
|
2017-04-11 18:37:30 -04:00
|
|
|
e.printStackTrace();
|
|
|
|
return false;
|
2014-08-05 04:43:14 -04:00
|
|
|
}
|
|
|
|
|
2017-04-11 18:37:30 -04:00
|
|
|
return true;
|
|
|
|
}
|
2014-08-05 04:43:14 -04:00
|
|
|
|
2017-04-14 12:40:48 -04:00
|
|
|
public static boolean is64BitJRE() {
|
|
|
|
String bits = System.getProperty("sun.arch.data.model");
|
|
|
|
if (bits == null)
|
|
|
|
return System.getProperty("java.vm.name").contains("64");
|
|
|
|
else
|
|
|
|
return bits.equals("64");
|
|
|
|
}
|
|
|
|
|
2017-04-11 18:37:30 -04:00
|
|
|
static {
|
2017-04-14 12:40:48 -04:00
|
|
|
if (is64BitJRE())
|
|
|
|
throw new UnsupportedOperationException("BWMirror must be run on a 32-bit JRE/JDK.");
|
2017-04-11 18:37:30 -04:00
|
|
|
|
|
|
|
if (!extractAndLoadNativeLibraries())
|
|
|
|
System.exit(1);
|
|
|
|
if (!extractBwtaDataFiles())
|
|
|
|
System.exit(1);
|
2017-04-12 21:45:14 -04:00
|
|
|
|
2017-04-14 17:51:38 -04:00
|
|
|
System.out.println(LOG_TAG + "Initializing constants tables.");
|
2017-04-12 21:45:14 -04:00
|
|
|
initTables();
|
2014-08-05 04:43:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
public Game getGame() {
|
|
|
|
return game;
|
|
|
|
}
|
|
|
|
|
|
|
|
public AIModule getModule() {
|
|
|
|
return module;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-04-12 21:45:14 -04:00
|
|
|
* Initializes all BWAPI constant lookup tables.
|
2014-08-05 04:43:14 -04:00
|
|
|
*/
|
2017-04-12 21:45:14 -04:00
|
|
|
private static native void initTables();
|
2014-08-05 04:43:14 -04:00
|
|
|
|
2017-04-12 21:45:14 -04:00
|
|
|
/**
|
|
|
|
* Initializes a connection to Broodwar, initializes the a {@link Game} object, and dispatches
|
|
|
|
* events to your listener as long as Broodwar is in a game. If this method is called before
|
|
|
|
* Broodwar is running, it will keep retrying until an initial connection can be established.
|
|
|
|
*
|
|
|
|
* The {@link Game} instance returned by {@link #getGame()} is only valid while this method
|
|
|
|
* is running. If your code holds a copy of this object anywhere else, do not try to use it
|
|
|
|
* again after this method returns.
|
|
|
|
*
|
2017-04-14 17:37:48 -04:00
|
|
|
* @param returnOnMatchEnd
|
|
|
|
* If true, will disconnect from Broodwar and return after the first match ends
|
|
|
|
* (regardless of how it ended). Will not attempt to reconnect to Broodwar if the
|
2017-04-12 21:45:14 -04:00
|
|
|
* connection is interrupted once the first match has been started. You can call
|
|
|
|
* {@link #startGame} again to run another match as needed.
|
2017-04-14 17:37:48 -04:00
|
|
|
* If false, will run an infinite loop allowing you to keep your bot running as many
|
|
|
|
* subsequent matches as desired. Will automatically reconnect to a Broodwar instance
|
|
|
|
* if the connection is interrupted.
|
2017-04-12 21:45:14 -04:00
|
|
|
*/
|
2017-04-14 17:37:48 -04:00
|
|
|
public void startGame(boolean returnOnMatchEnd) {
|
2017-04-14 17:51:38 -04:00
|
|
|
System.out.println(LOG_TAG + "Connecting to Broodwar...");
|
2017-04-14 17:37:48 -04:00
|
|
|
if (reconnect())
|
2017-04-14 17:51:38 -04:00
|
|
|
System.out.println(LOG_TAG + "Connection successful, starting match...");
|
2017-04-14 17:37:48 -04:00
|
|
|
else {
|
2017-04-14 17:51:38 -04:00
|
|
|
System.out.println(LOG_TAG + "Connection attempt aborted.");
|
2017-04-14 17:37:48 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
game = getInternalGame();
|
2017-04-12 21:45:14 -04:00
|
|
|
|
2017-04-14 17:37:48 -04:00
|
|
|
boolean inGame = game.isInGame();
|
|
|
|
boolean previouslyInGame = inGame;
|
|
|
|
if (inGame)
|
2017-04-14 17:51:38 -04:00
|
|
|
System.out.println(LOG_TAG + "Match already running.");
|
2017-04-14 17:37:48 -04:00
|
|
|
|
|
|
|
while (true) {
|
|
|
|
if (Thread.interrupted()) {
|
2017-04-14 17:51:38 -04:00
|
|
|
System.out.println(LOG_TAG + "Interrupted.");
|
2017-04-14 17:37:48 -04:00
|
|
|
break;
|
|
|
|
}
|
2017-04-12 21:45:14 -04:00
|
|
|
|
2017-04-14 17:37:48 -04:00
|
|
|
if (!inGame) {
|
|
|
|
if (previouslyInGame) {
|
2017-04-14 17:51:38 -04:00
|
|
|
System.out.println(LOG_TAG + "Match ended.");
|
2017-04-14 17:37:48 -04:00
|
|
|
if (returnOnMatchEnd)
|
|
|
|
break;
|
2017-04-12 21:45:14 -04:00
|
|
|
}
|
|
|
|
|
2017-04-14 17:37:48 -04:00
|
|
|
update();
|
2017-04-12 21:45:14 -04:00
|
|
|
|
2017-04-14 17:37:48 -04:00
|
|
|
} else {
|
|
|
|
if (!previouslyInGame)
|
2017-04-14 17:51:38 -04:00
|
|
|
System.out.println(LOG_TAG + "Game ready!!!");
|
2017-04-12 21:45:14 -04:00
|
|
|
|
2017-04-14 17:37:48 -04:00
|
|
|
processGameEvents();
|
|
|
|
update();
|
|
|
|
}
|
2017-04-12 21:45:14 -04:00
|
|
|
|
2017-04-14 17:37:48 -04:00
|
|
|
if (!isConnected()) {
|
2017-04-14 17:51:38 -04:00
|
|
|
System.out.println(LOG_TAG + "Reconnecting...");
|
2017-04-14 17:37:48 -04:00
|
|
|
reconnect();
|
|
|
|
}
|
2017-04-12 21:45:14 -04:00
|
|
|
|
2017-04-14 17:37:48 -04:00
|
|
|
previouslyInGame = inGame;
|
|
|
|
inGame = game.isInGame();
|
2014-08-05 04:43:14 -04:00
|
|
|
}
|
2017-04-14 17:37:48 -04:00
|
|
|
|
2017-04-14 17:51:38 -04:00
|
|
|
System.out.println(LOG_TAG + "Finished.");
|
|
|
|
System.out.println(LOG_TAG + "Disconnecting from Broodwar...");
|
2017-04-14 17:37:48 -04:00
|
|
|
|
2017-04-12 21:45:14 -04:00
|
|
|
if (isConnected())
|
|
|
|
disconnect();
|
2014-08-05 04:43:14 -04:00
|
|
|
|
2017-04-12 21:45:14 -04:00
|
|
|
game = null;
|
2017-04-14 17:37:48 -04:00
|
|
|
|
2017-04-14 17:51:38 -04:00
|
|
|
System.out.println(LOG_TAG + "Returning...");
|
2017-04-12 21:45:14 -04:00
|
|
|
}
|
2014-08-05 04:43:14 -04:00
|
|
|
|
2017-04-14 17:37:48 -04:00
|
|
|
private boolean reconnect() {
|
2017-04-12 21:45:14 -04:00
|
|
|
while (!connect()) {
|
2017-04-14 17:37:48 -04:00
|
|
|
try {
|
|
|
|
Thread.sleep(1000);
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-04-12 21:45:14 -04:00
|
|
|
}
|
2017-04-14 17:37:48 -04:00
|
|
|
return true;
|
2014-08-05 04:43:14 -04:00
|
|
|
}
|
2017-04-12 21:45:14 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the current connection state to a running Broodwar instance.
|
|
|
|
*/
|
|
|
|
public static native boolean isConnected();
|
|
|
|
|
|
|
|
private static native boolean connect();
|
|
|
|
|
|
|
|
private static native void disconnect();
|
|
|
|
|
|
|
|
private static native void update();
|
|
|
|
|
|
|
|
private native Game getInternalGame();
|
|
|
|
|
|
|
|
private native void processGameEvents();
|
|
|
|
}
|