Compare commits
23 commits
Author | SHA1 | Date | |
---|---|---|---|
Gered | 0702faf9b0 | ||
ec71d44c86 | |||
e450e25e99 | |||
688c611e91 | |||
544e825362 | |||
3737998f60 | |||
Gered | b3473776e6 | ||
Gered | 035b9beaa3 | ||
Gered | 38ab003dca | ||
Gered | 564133f95a | ||
Gered | 794678d46e | ||
Gered | 4ab3980d24 | ||
Gered | 57608ad74a | ||
Gered | ad647d1dd7 | ||
Gered | 1f68039507 | ||
Gered | c0a65e037c | ||
Gered | e5721392c6 | ||
Gered | 6b810bcd2e | ||
Gered | c3053f5c7b | ||
Gered | 201af45caf | ||
ffb7cecc3a | |||
d795f83181 | |||
927ac86c9d |
43
README.md
43
README.md
|
@ -1,6 +1,8 @@
|
|||
# BWMirror API
|
||||
|
||||
This is the BWMirror Generator which is used to generate the Java classes that will mirror the ones found in BWAPI and automatically generate the C++ JNI glue that makes it all work.
|
||||
A Java API for [BWAPI](http://bwapi.github.io/) and [BWTA2](https://bitbucket.org/auriarte/bwta2) allowing you to create Starcraft: Broodwar AIs using Java.
|
||||
|
||||
This repository contains a "generator" project which is used to programmatically generate the Java classes that will mirror the ones found in BWAPI/BWTA2 and automatically generate the C++ JNI glue that makes it all work together. It's fairly involved, but if you're just interested in writing a Starcraft AI in Java, then you don't need to worry about the details too much!
|
||||
|
||||
## Usage
|
||||
|
||||
|
@ -12,7 +14,7 @@ If your project uses Maven, you can add a dependency to BWMirror:
|
|||
<dependency>
|
||||
<groupId>com.github.gered</groupId>
|
||||
<artifactId>bwmirror</artifactId>
|
||||
<version>2.6</version>
|
||||
<version>2.7.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
@ -34,12 +36,15 @@ public class ExampleBot {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onEnd(boolean b) {
|
||||
public void onEnd(boolean isWinner) {
|
||||
System.out.println("Game has ended");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrame() {
|
||||
// this object is ONLY valid while Mirror.startGame is running
|
||||
// (which is fine 99% of the time, it just means you should only
|
||||
// use it within code that is invoked from these listener methods)
|
||||
Game game = mirror.getGame();
|
||||
Player self = game.self();
|
||||
|
||||
|
@ -47,19 +52,42 @@ public class ExampleBot {
|
|||
}
|
||||
});
|
||||
|
||||
// blocks indefinitely
|
||||
mirror.startGame();
|
||||
// if you pass false, blocks indefinitely and keeps a Broodwar
|
||||
// instance connection to keep playing subsequent matches.
|
||||
// if you pass true, startGame will return after a single match and
|
||||
// disconnects from the Broodwar instance. you can call startGame as
|
||||
// many times as you wish in this case.
|
||||
mirror.startGame(true);
|
||||
}
|
||||
|
||||
public static void main(String... args) {
|
||||
new TestBot().run();
|
||||
new ExampleBot().run();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Building BWMirror
|
||||
|
||||
This is a fair bit involved due to the nature of the project. If you want to contribute to BWMirror, read on.
|
||||
This is a fair bit involved due to the nature of the project. If you want to contribute to BWMirror, read on. If you are only interested in writing a Starcraft AI, then you can stop here.
|
||||
|
||||
### Introduction: How This All Works
|
||||
|
||||
Basically, in this repository the `/bwmirror` directory contains the final BWMirror Java library. However, you will find it's missing basically all of the code!
|
||||
|
||||
This is because BWMirror consists largely of code that is _automatically generated_ from BWAPI and BWTA2 C++ header files. The generator project under `/generator` is where all of this code generation occurs.
|
||||
|
||||
The generator project works by parsing the C++ header files and building a list of classes and methods that exist on the C++ side of things. Then these are translated into equivalent "mirror" Java classes. These mirror classes will contain public Java methods that bots will use. As well, there will be an equivalent number of private `native` methods which is where JNI code will be used to call the actual BWAPI/BWTA2 libraries. Note that there are a small number of Java classes that were manually developed and do not contain automatically generated code. These classes are simply copied into the BWMirror project as-is and their source is located under `/manual-bwapi-src`.
|
||||
|
||||
`javah` is used to generate JNI function declarations for all of these `native` Java class methods, and then a JNI C++ implementation file, `impl.cpp` is generated with almost all automatically generated function implementations (since the vast majority of these are really basic and simply call BWAPI/BWTA2 functions with the same number of parameters, etc). A very small number of functions in `impl.cpp` are manually implemented using specially-crafted code.
|
||||
|
||||
A Visual Studio 2013 project is also included in `/bwapi_bridge` which needs to be used to compile the automatically generated JNI C++ code into a DLL.
|
||||
|
||||
All of the automatically generated Java code and the JNI DLL are automatically placed into the appropriate places in the `/bwmirror` directory. Once all of this is done, the BWMirror library simply needs to be built.
|
||||
|
||||
> **NOTE:** Generally speaking, you should not be directly editing code under `/bwmirror` or `/output` unless you're just testing/debugging quick changes. This is due to the fact that all of the code in there is automatically being generated/copied.
|
||||
>
|
||||
> The "proper" way to make changes to BWMirror is to modify the generator project and/or edit the Java classes under `/manual-bwapi-src`.
|
||||
|
||||
|
||||
### Installing Pre-Requisites
|
||||
|
||||
|
@ -97,7 +125,6 @@ https://bitbucket.org/auriarte/bwta2/downloads/
|
|||
|
||||
Download and extract the BWTA files somewhere and take note of the location you extracted them to, creating a `BWTA_HOME` environment variable that points to this same location. e.g. "C:\dev\BWTAlib_2.2"
|
||||
|
||||
|
||||
### Running the Generator
|
||||
|
||||
If you've not already done so, clone the BWMirror-Generator repository.
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
<groupId>com.github.gered</groupId>
|
||||
<artifactId>bwmirror</artifactId>
|
||||
<version>2.6</version>
|
||||
<version>2.7.1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BWMirror</name>
|
||||
|
|
|
@ -138,7 +138,7 @@ public class TestBot {
|
|||
}
|
||||
});
|
||||
*/
|
||||
mirror.startGame();
|
||||
mirror.startGame(false);
|
||||
System.out.println("It's over");
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package bwmirror.generator;
|
||||
|
||||
import bwmirror.c.Param;
|
||||
import bwmirror.util.Generic;
|
||||
import bwmirror.util.PointerTest;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created with IntelliJ IDEA.
|
||||
|
@ -19,7 +21,7 @@ public class JavaContext {
|
|||
|
||||
private List<String> valueTypes = Arrays.asList("Position", "TilePosition", "WalkPosition", "Color", "BWTA::RectangleArray<double>", "PositionOrUnit", "Point", "UnitCommand", "Integer", "Long", "Double", "Boolean", "int", "void", "double", "long", "Pair");
|
||||
|
||||
private List<String> constantTypes = Arrays.asList("UnitType", "TechType", "UpgradeType", "Race", "UnitCommand", "WeaponType", "Order", "GameType", "Error", "PlayerType", "UnitCommandType");
|
||||
private List<String> constantTypes = Arrays.asList("UnitType", "TechType", "UpgradeType", "Race", "UnitCommand", "WeaponType", "Order", "GameType", "Error", "PlayerType", "UnitCommandType", "UnitSizeType", "DamageType", "ExplosionType");
|
||||
|
||||
private List<String> enumTypes = Arrays.asList("MouseButton", "Key", "bwapi.Text.Size.Enum", "bwapi.CoordinateType.Enum", "Text::Size::Enum", "CoordinateType::Enum");
|
||||
|
||||
|
@ -29,6 +31,8 @@ public class JavaContext {
|
|||
|
||||
private List<String> selfReturnTypes = Arrays.asList("BWTA::Polygon");
|
||||
|
||||
private Map<String, Param> requiresExplicitDummyPredicate = new HashMap<>();
|
||||
|
||||
|
||||
private String packageName = "bwapi";
|
||||
|
||||
|
@ -39,6 +43,8 @@ public class JavaContext {
|
|||
javaToCType.put("void", "void");
|
||||
javaToCType.put("boolean", "jboolean");
|
||||
javaToCType.put("String", "jstring");
|
||||
|
||||
requiresExplicitDummyPredicate.put("getUnitsInWeaponRange", new Param("", "IdentityUnitFilter"));
|
||||
}
|
||||
|
||||
public boolean isSelfReturnType(String clsName, String methodName) {
|
||||
|
@ -93,6 +99,10 @@ public class JavaContext {
|
|||
return constantTypes.contains(javaType);
|
||||
}
|
||||
|
||||
public boolean needsExtraFilterParameter(String methodName) { return requiresExplicitDummyPredicate.containsKey(methodName); }
|
||||
|
||||
public Param getExtraFilterParameter(String methodName) { return requiresExplicitDummyPredicate.get(methodName); }
|
||||
|
||||
public String ptrCType() {
|
||||
return "jobject";
|
||||
}
|
||||
|
|
|
@ -30,165 +30,154 @@ public class Bind {
|
|||
this.context = context;
|
||||
}
|
||||
|
||||
private void implementConnectionRoutine() {
|
||||
out.println("\t\t\t\tprintln(\"Waiting...\");\n" +
|
||||
"while ( !Broodwar->isInGame() )\n" +
|
||||
" {\n" +
|
||||
" BWAPI::BWAPIClient.update();\n" +
|
||||
" if (!BWAPI::BWAPIClient.isConnected())\n" +
|
||||
" {\n" +
|
||||
" println(\"Reconnecting...\");\n" +
|
||||
" reconnect();\n" +
|
||||
" }\n" +
|
||||
" }");
|
||||
}
|
||||
|
||||
|
||||
private void implementGameStart() {
|
||||
out.println("println(\"Connecting to Broodwar...\");\n" +
|
||||
"\t\treconnect();\n" +
|
||||
"\t\tprintln(\"Connection successful, starting match...\");\n" +
|
||||
"\n" +
|
||||
"\t\tcls = env->GetObjectClass(obj);\n" +
|
||||
"\t\tjclass gamecls = env->FindClass(\"L" + context.getPackageName() + "/Game;\");\n" +
|
||||
"\t\tjclass unitCls = env->FindClass(\"L" + context.getPackageName() + "/Unit;\");\n" +
|
||||
"\t\tjclass playerCls = env->FindClass(\"L" + context.getPackageName() + "/Player;\");\n" +
|
||||
"\t\tjclass posCls = env->FindClass(\"L" + context.getPackageName() + "/Position;\");\n" +
|
||||
"\t\tjobject moduleObj = env->GetObjectField(obj, env->GetFieldID(cls, \"module\", \"L" + context.getPackageName() + "/AIModule;\"));\n" +
|
||||
"\t\tjclass moduleCls = env->GetObjectClass(moduleObj);\n" +
|
||||
"\t\tenv->SetObjectField(obj, env->GetFieldID(cls, \"game\", \"L" + context.getPackageName() + "/Game;\"), " +
|
||||
"env->CallStaticObjectMethod(gamecls, env->GetStaticMethodID(gamecls, \"get\", \"(J)L" + context.getPackageName() + "/Game;\"), (long)BroodwarPtr));\n" +
|
||||
"\n" +
|
||||
"\t\tjmethodID updateMethodID = env->GetMethodID(env->GetObjectClass(obj), \"update\", \"()V\");");
|
||||
|
||||
out.println("\t\tjmethodID matchStartCallback = env->GetMethodID(moduleCls, \"onStart\", \"()V\");\n" +
|
||||
"\t\tjmethodID matchEndCallback = env->GetMethodID(moduleCls, \"onEnd\", \"(Z)V\");\n" +
|
||||
"\t\tjmethodID matchFrameCallback = env->GetMethodID(moduleCls, \"onFrame\", \"()V\");\n" +
|
||||
"\t\tjmethodID sendTextCallback = env->GetMethodID(moduleCls, \"onSendText\", \"(Ljava/lang/String;)V\");\n" +
|
||||
"\t\tjmethodID receiveTextCallback = env->GetMethodID(moduleCls, \"onReceiveText\", \"(L" + context.getPackageName() + "/Player;Ljava/lang/String;)V\");\n" +
|
||||
"\t\tjmethodID playerLeftCallback = env->GetMethodID(moduleCls, \"onPlayerLeft\", \"(L" + context.getPackageName() + "/Player;)V\");\n" +
|
||||
"\t\tjmethodID nukeDetectCallback = env->GetMethodID(moduleCls, \"onNukeDetect\", \"(L" + context.getPackageName() + "/Position;)V\");\n" +
|
||||
"\t\tjmethodID unitDiscoverCallback = env->GetMethodID(moduleCls, \"onUnitDiscover\", \"(L" + context.getPackageName() + "/Unit;)V\");\n" +
|
||||
"\t\tjmethodID unitEvadeCallback = env->GetMethodID(moduleCls, \"onUnitEvade\", \"(L" + context.getPackageName() + "/Unit;)V\");\n" +
|
||||
"\t\tjmethodID unitShowCallback = env->GetMethodID(moduleCls, \"onUnitShow\", \"(L" + context.getPackageName() + "/Unit;)V\");\n" +
|
||||
"\t\tjmethodID unitHideCallback = env->GetMethodID(moduleCls, \"onUnitHide\", \"(L" + context.getPackageName() + "/Unit;)V\");\n" +
|
||||
"\t\tjmethodID unitCreateCallback = env->GetMethodID(moduleCls, \"onUnitCreate\", \"(L" + context.getPackageName() + "/Unit;)V\");\n" +
|
||||
"\t\tjmethodID unitDestroyCallback = env->GetMethodID(moduleCls, \"onUnitDestroy\", \"(L" + context.getPackageName() + "/Unit;)V\");\n" +
|
||||
"\t\tjmethodID unitMorphCallback = env->GetMethodID(moduleCls, \"onUnitMorph\", \"(L" + context.getPackageName() + "/Unit;)V\");\n" +
|
||||
"\t\tjmethodID unitRenegadeCallback = env->GetMethodID(moduleCls, \"onUnitRenegade\", \"(L" + context.getPackageName() + "/Unit;)V\");\n" +
|
||||
"\t\tjmethodID saveGameCallback = env->GetMethodID(moduleCls, \"onSaveGame\", \"(Ljava/lang/String;)V\");\n" +
|
||||
"\t\tjmethodID unitCompleteCallback = env->GetMethodID(moduleCls, \"onUnitComplete\", \"(L" + context.getPackageName() + "/Unit;)V\");\n" +
|
||||
"\t\tjmethodID playerDroppedCallback = env->GetMethodID(moduleCls, \"onPlayerDropped\", \"(L" + context.getPackageName() + "/Player;)V\");");
|
||||
|
||||
out.println("\t\twhile (true) {\n");
|
||||
implementConnectionRoutine();
|
||||
out.println("\t\t\tprintln(\"Game ready!!!\");\n" +
|
||||
"\n" +
|
||||
"\t\t\twhile (Broodwar->isInGame()) {\n" +
|
||||
"\t\t\t\t\n" +
|
||||
"\t\t\t\tenv->CallVoidMethod(obj, updateMethodID);\n");
|
||||
out.println("\n" +
|
||||
"\t\t\t\tfor(std::list<Event>::const_iterator it = Broodwar->getEvents().begin(); it!=Broodwar->getEvents().end(); it++)\n" +
|
||||
"\t\t\t\t {\n" +
|
||||
"\t\t\t\t\t switch (it->getType()) {\n" +
|
||||
|
||||
"\t\t\t\t\t\t case EventType::MatchStart:\n" +
|
||||
"\t\t\t\t\t\t\t env->CallVoidMethod(moduleObj, matchStartCallback);\n" +
|
||||
"\t\t\t\t\t\t break;\n" +
|
||||
"\t\t\t\t\t\t case EventType::MatchEnd:\n" +
|
||||
"\t\t\t\t\t\t\t env->CallVoidMethod(moduleObj, matchEndCallback, it->isWinner());\n" +
|
||||
"\t\t\t\t\t\t break;\n" +
|
||||
"\t\t\t\t\t\t case EventType::MatchFrame:\n" +
|
||||
"\t\t\t\t\t\t\t env->CallVoidMethod(moduleObj, matchFrameCallback);\n" +
|
||||
"\t\t\t\t\t\t break;\n" +
|
||||
"\t\t\t\t\t\t case EventType::SendText:\n" +
|
||||
"\t\t\t\t\t\t\t env->CallVoidMethod(moduleObj, sendTextCallback, env->NewStringUTF(it->getText().c_str()));\n" +
|
||||
"\t\t\t\t\t\t break;\n" +
|
||||
"\t\t\t\t\t\t case EventType::ReceiveText:\n" +
|
||||
"\t\t\t\t\t\t\t env->CallVoidMethod(moduleObj, receiveTextCallback, env->CallStaticObjectMethod(playerCls, env->GetStaticMethodID(playerCls, \"get\", \"(J)L" + context.getPackageName() + "/Player;\"), (jlong)it->getPlayer()), env->NewStringUTF(it->getText().c_str()));\n" +
|
||||
"\t\t\t\t\t\t break;\n" +
|
||||
"\t\t\t\t\t\t case EventType::PlayerLeft:\n" +
|
||||
"\t\t\t\t\t\t\t env->CallVoidMethod(moduleObj, playerLeftCallback, env->CallStaticObjectMethod(playerCls, env->GetStaticMethodID(playerCls, \"get\", \"(J)L" + context.getPackageName() + "/Player;\"), (jlong)it->getPlayer()));\n" +
|
||||
"\t\t\t\t\t\t break;\n" +
|
||||
"\t\t\t\t\t\t case EventType::NukeDetect:\n" +
|
||||
"\t\t\t\t\t\t\t env->CallVoidMethod(moduleObj, nukeDetectCallback, env->NewObject(posCls, env->GetMethodID(posCls,\"<init>\", \"(II)V\"), it->getPosition().x, it->getPosition().y));\n" +
|
||||
"\t\t\t\t\t\t break;\n" +
|
||||
"\t\t\t\t\t\t case EventType::UnitDiscover:\n" +
|
||||
"\t\t\t\t\t\t\t env->CallVoidMethod(moduleObj, unitDiscoverCallback, env->CallStaticObjectMethod(unitCls, env->GetStaticMethodID(unitCls, \"get\", \"(J)L" + context.getPackageName() + "/Unit;\"), (jlong)it->getUnit()));\n" +
|
||||
"\t\t\t\t\t\t break;\n" +
|
||||
"\t\t\t\t\t\t case EventType::UnitEvade:\n" +
|
||||
"\t\t\t\t\t\t\t env->CallVoidMethod(moduleObj, unitEvadeCallback, env->CallStaticObjectMethod(unitCls, env->GetStaticMethodID(unitCls, \"get\", \"(J)L" + context.getPackageName() + "/Unit;\"), (jlong)it->getUnit()));\n" +
|
||||
"\t\t\t\t\t\t break;\n" +
|
||||
"\t\t\t\t\t\t case EventType::UnitShow:\n" +
|
||||
"\t\t\t\t\t\t\t env->CallVoidMethod(moduleObj, unitShowCallback, env->CallStaticObjectMethod(unitCls, env->GetStaticMethodID(unitCls, \"get\", \"(J)L" + context.getPackageName() + "/Unit;\"), (jlong)it->getUnit()));\n" +
|
||||
"\t\t\t\t\t\t break;\n" +
|
||||
"\t\t\t\t\t\t case EventType::UnitHide:\n" +
|
||||
"\t\t\t\t\t\t\t env->CallVoidMethod(moduleObj, unitHideCallback, env->CallStaticObjectMethod(unitCls, env->GetStaticMethodID(unitCls, \"get\", \"(J)L" + context.getPackageName() + "/Unit;\"), (jlong)it->getUnit()));\n" +
|
||||
"\t\t\t\t\t\t break;\n" +
|
||||
"\t\t\t\t\t\t case EventType::UnitCreate:\n" +
|
||||
"\t\t\t\t\t\t\t env->CallVoidMethod(moduleObj, unitCreateCallback, env->CallStaticObjectMethod(unitCls, env->GetStaticMethodID(unitCls, \"get\", \"(J)L" + context.getPackageName() + "/Unit;\"), (jlong)it->getUnit()));\n" +
|
||||
"\t\t\t\t\t\t break;\n" +
|
||||
"\t\t\t\t\t\t case EventType::UnitDestroy:\n" +
|
||||
"\t\t\t\t\t\t\t env->CallVoidMethod(moduleObj, unitDestroyCallback, env->CallStaticObjectMethod(unitCls, env->GetStaticMethodID(unitCls, \"get\", \"(J)L" + context.getPackageName() + "/Unit;\"), (jlong)it->getUnit()));\n" +
|
||||
"\t\t\t\t\t\t break;\n" +
|
||||
"\t\t\t\t\t\t case EventType::UnitMorph:\n" +
|
||||
"\t\t\t\t\t\t\t env->CallVoidMethod(moduleObj, unitMorphCallback, env->CallStaticObjectMethod(unitCls, env->GetStaticMethodID(unitCls, \"get\", \"(J)L" + context.getPackageName() + "/Unit;\"), (jlong)it->getUnit()));\n" +
|
||||
"\t\t\t\t\t\t break;\n" +
|
||||
"\t\t\t\t\t\t case EventType::UnitRenegade:\n" +
|
||||
"\t\t\t\t\t\t\t env->CallVoidMethod(moduleObj, unitRenegadeCallback, env->CallStaticObjectMethod(unitCls, env->GetStaticMethodID(unitCls, \"get\", \"(J)L" + context.getPackageName() + "/Unit;\"), (jlong)it->getUnit()));\n" +
|
||||
"\t\t\t\t\t\t break;\n" +
|
||||
"\t\t\t\t\t\t case EventType::SaveGame:\n" +
|
||||
"\t\t\t\t\t\t\t env->CallVoidMethod(moduleObj, saveGameCallback, env->NewStringUTF(it->getText().c_str()));\n" +
|
||||
"\t\t\t\t\t\t break;\n" +
|
||||
"\t\t\t\t\t\t case EventType::UnitComplete:\n" +
|
||||
"\t\t\t\t\t\t\t env->CallVoidMethod(moduleObj, unitCompleteCallback, env->CallStaticObjectMethod(unitCls, env->GetStaticMethodID(unitCls, \"get\", \"(J)L" + context.getPackageName() + "/Unit;\"), (jlong)it->getUnit()));\n" +
|
||||
"\t\t\t\t\t\t break;\n" +
|
||||
"\n" +
|
||||
"\t\t\t\t\t }\n" +
|
||||
"\t\t\t\t }");
|
||||
out.println(
|
||||
"\t\t\t\tBWAPIClient.update();\n" +
|
||||
"\t\t\t\tif (!BWAPI::BWAPIClient.isConnected()) {\n" +
|
||||
"\t\t\t\t\t\tprintln(\"Reconnecting...\");\n" +
|
||||
"\t\t\t\t\t\treconnect();\n" +
|
||||
"\t\t\t\t}\n" +
|
||||
"\t\t\t}\n" +
|
||||
"println(\"Match ended.\");" +
|
||||
"\t\t}");
|
||||
}
|
||||
|
||||
private void implementHelpers() {
|
||||
out.println("void reconnect()\n" +
|
||||
"{\n" +
|
||||
"\twhile (!BWAPIClient.connect()) {\n" +
|
||||
" std::this_thread::sleep_for(std::chrono::milliseconds{ 1000 });\n" +
|
||||
" }\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"\n");
|
||||
|
||||
out.println(
|
||||
"void flushPrint(const char * text){\n" +
|
||||
"\tprintf(text);\n" +
|
||||
"\tfflush(stdout); \n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"void println(const char * text){\n" +
|
||||
"\tprintf(text);\n" +
|
||||
"\tflushPrint(\"\\n\");\n" +
|
||||
"}\n");
|
||||
" printf(text);\n" +
|
||||
" fflush(stdout); \n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"void println(const char * text){\n" +
|
||||
" printf(text);\n" +
|
||||
" flushPrint(\"\\n\");\n" +
|
||||
"}\n"
|
||||
);
|
||||
out.println();
|
||||
|
||||
}
|
||||
|
||||
private void implementMirrorInit(List<CDeclaration> declarationList) {
|
||||
implementHelpers();
|
||||
out.println("JNIEXPORT void JNICALL Java_" + context.getPackageName() + "_Mirror_startGame(JNIEnv * env, jobject obj){");
|
||||
private void implementMirror_initTables(List<CDeclaration> declarationList) {
|
||||
out.println("JNIEXPORT void JNICALL Java_" + context.getPackageName() + "_Mirror_initTables(JNIEnv * env, jclass jclz){");
|
||||
out.println("if (areTypeTablesInitialized) return;");
|
||||
implementVariablesBind(declarationList);
|
||||
implementGameStart();
|
||||
out.println("areTypeTablesInitialized = true;");
|
||||
out.println("println(\"BWMirror C++ lookup tables are now initialized.\");");
|
||||
out.println("}");
|
||||
out.println();
|
||||
}
|
||||
|
||||
private void implementMirror_getInternalGame() {
|
||||
out.println(
|
||||
"JNIEXPORT jobject JNICALL Java_" + context.getPackageName() + "_Mirror_getInternalGame(JNIEnv * env, jobject obj){\n" +
|
||||
" jclass gamecls = env->FindClass(\"Lbwapi/Game;\");\n" +
|
||||
" jmethodID getMethodID = env->GetStaticMethodID(gamecls, \"get\", \"(J)Lbwapi/Game;\");\n" +
|
||||
" return env->CallStaticObjectMethod(gamecls, getMethodID, (jlong)BroodwarPtr);\n" +
|
||||
"}\n"
|
||||
);
|
||||
out.println();
|
||||
}
|
||||
|
||||
private void implementMirror_processGameEvents() {
|
||||
out.println(
|
||||
"JNIEXPORT void JNICALL Java_" + context.getPackageName() + "_Mirror_processGameEvents(JNIEnv * env, jobject obj){\n" +
|
||||
" jclass cls = env->GetObjectClass(obj);\n" +
|
||||
" jobject moduleObj = env->GetObjectField(obj, env->GetFieldID(cls, \"module\", \"Lbwapi/AIModule;\"));\n" +
|
||||
" jclass moduleCls = env->GetObjectClass(moduleObj);\n" +
|
||||
"\n" +
|
||||
" jclass unitCls = env->FindClass(\"Lbwapi/Unit;\");\n" +
|
||||
" jclass playerCls = env->FindClass(\"Lbwapi/Player;\");\n" +
|
||||
" jclass posCls = env->FindClass(\"Lbwapi/Position;\");\n" +
|
||||
"\n" +
|
||||
" jmethodID matchStartCallback = env->GetMethodID(moduleCls, \"onStart\", \"()V\");\n" +
|
||||
" jmethodID matchEndCallback = env->GetMethodID(moduleCls, \"onEnd\", \"(Z)V\");\n" +
|
||||
" jmethodID matchFrameCallback = env->GetMethodID(moduleCls, \"onFrame\", \"()V\");\n" +
|
||||
" jmethodID sendTextCallback = env->GetMethodID(moduleCls, \"onSendText\", \"(Ljava/lang/String;)V\");\n" +
|
||||
" jmethodID receiveTextCallback = env->GetMethodID(moduleCls, \"onReceiveText\", \"(Lbwapi/Player;Ljava/lang/String;)V\");\n" +
|
||||
" jmethodID playerLeftCallback = env->GetMethodID(moduleCls, \"onPlayerLeft\", \"(Lbwapi/Player;)V\");\n" +
|
||||
" jmethodID nukeDetectCallback = env->GetMethodID(moduleCls, \"onNukeDetect\", \"(Lbwapi/Position;)V\");\n" +
|
||||
" jmethodID unitDiscoverCallback = env->GetMethodID(moduleCls, \"onUnitDiscover\", \"(Lbwapi/Unit;)V\");\n" +
|
||||
" jmethodID unitEvadeCallback = env->GetMethodID(moduleCls, \"onUnitEvade\", \"(Lbwapi/Unit;)V\");\n" +
|
||||
" jmethodID unitShowCallback = env->GetMethodID(moduleCls, \"onUnitShow\", \"(Lbwapi/Unit;)V\");\n" +
|
||||
" jmethodID unitHideCallback = env->GetMethodID(moduleCls, \"onUnitHide\", \"(Lbwapi/Unit;)V\");\n" +
|
||||
" jmethodID unitCreateCallback = env->GetMethodID(moduleCls, \"onUnitCreate\", \"(Lbwapi/Unit;)V\");\n" +
|
||||
" jmethodID unitDestroyCallback = env->GetMethodID(moduleCls, \"onUnitDestroy\", \"(Lbwapi/Unit;)V\");\n" +
|
||||
" jmethodID unitMorphCallback = env->GetMethodID(moduleCls, \"onUnitMorph\", \"(Lbwapi/Unit;)V\");\n" +
|
||||
" jmethodID unitRenegadeCallback = env->GetMethodID(moduleCls, \"onUnitRenegade\", \"(Lbwapi/Unit;)V\");\n" +
|
||||
" jmethodID saveGameCallback = env->GetMethodID(moduleCls, \"onSaveGame\", \"(Ljava/lang/String;)V\");\n" +
|
||||
" jmethodID unitCompleteCallback = env->GetMethodID(moduleCls, \"onUnitComplete\", \"(Lbwapi/Unit;)V\");\n" +
|
||||
"\n" +
|
||||
" for (std::list<Event>::const_iterator it = Broodwar->getEvents().begin(); it != Broodwar->getEvents().end(); it++)\n" +
|
||||
" {\n" +
|
||||
" switch (it->getType()) {\n" +
|
||||
" case EventType::MatchStart:\n" +
|
||||
" env->CallVoidMethod(moduleObj, matchStartCallback);\n" +
|
||||
" break;\n" +
|
||||
" case EventType::MatchEnd:\n" +
|
||||
" env->CallVoidMethod(moduleObj, matchEndCallback, it->isWinner());\n" +
|
||||
" break;\n" +
|
||||
" case EventType::MatchFrame:\n" +
|
||||
" env->CallVoidMethod(moduleObj, matchFrameCallback);\n" +
|
||||
" break;\n" +
|
||||
" case EventType::SendText:\n" +
|
||||
" env->CallVoidMethod(moduleObj, sendTextCallback, env->NewStringUTF(it->getText().c_str()));\n" +
|
||||
" break;\n" +
|
||||
" case EventType::ReceiveText:\n" +
|
||||
" env->CallVoidMethod(moduleObj, receiveTextCallback, env->CallStaticObjectMethod(playerCls, env->GetStaticMethodID(playerCls, \"get\", \"(J)Lbwapi/Player;\"), (jlong)it->getPlayer()), env->NewStringUTF(it->getText().c_str()));\n" +
|
||||
" break;\n" +
|
||||
" case EventType::PlayerLeft:\n" +
|
||||
" env->CallVoidMethod(moduleObj, playerLeftCallback, env->CallStaticObjectMethod(playerCls, env->GetStaticMethodID(playerCls, \"get\", \"(J)Lbwapi/Player;\"), (jlong)it->getPlayer()));\n" +
|
||||
" break;\n" +
|
||||
" case EventType::NukeDetect:\n" +
|
||||
" env->CallVoidMethod(moduleObj, nukeDetectCallback, env->NewObject(posCls, env->GetMethodID(posCls, \"<init>\", \"(II)V\"), it->getPosition().x, it->getPosition().y));\n" +
|
||||
" break;\n" +
|
||||
" case EventType::UnitDiscover:\n" +
|
||||
" env->CallVoidMethod(moduleObj, unitDiscoverCallback, env->CallStaticObjectMethod(unitCls, env->GetStaticMethodID(unitCls, \"get\", \"(J)Lbwapi/Unit;\"), (jlong)it->getUnit()));\n" +
|
||||
" break;\n" +
|
||||
" case EventType::UnitEvade:\n" +
|
||||
" env->CallVoidMethod(moduleObj, unitEvadeCallback, env->CallStaticObjectMethod(unitCls, env->GetStaticMethodID(unitCls, \"get\", \"(J)Lbwapi/Unit;\"), (jlong)it->getUnit()));\n" +
|
||||
" break;\n" +
|
||||
" case EventType::UnitShow:\n" +
|
||||
" env->CallVoidMethod(moduleObj, unitShowCallback, env->CallStaticObjectMethod(unitCls, env->GetStaticMethodID(unitCls, \"get\", \"(J)Lbwapi/Unit;\"), (jlong)it->getUnit()));\n" +
|
||||
" break;\n" +
|
||||
" case EventType::UnitHide:\n" +
|
||||
" env->CallVoidMethod(moduleObj, unitHideCallback, env->CallStaticObjectMethod(unitCls, env->GetStaticMethodID(unitCls, \"get\", \"(J)Lbwapi/Unit;\"), (jlong)it->getUnit()));\n" +
|
||||
" break;\n" +
|
||||
" case EventType::UnitCreate:\n" +
|
||||
" env->CallVoidMethod(moduleObj, unitCreateCallback, env->CallStaticObjectMethod(unitCls, env->GetStaticMethodID(unitCls, \"get\", \"(J)Lbwapi/Unit;\"), (jlong)it->getUnit()));\n" +
|
||||
" break;\n" +
|
||||
" case EventType::UnitDestroy:\n" +
|
||||
" env->CallVoidMethod(moduleObj, unitDestroyCallback, env->CallStaticObjectMethod(unitCls, env->GetStaticMethodID(unitCls, \"get\", \"(J)Lbwapi/Unit;\"), (jlong)it->getUnit()));\n" +
|
||||
" break;\n" +
|
||||
" case EventType::UnitMorph:\n" +
|
||||
" env->CallVoidMethod(moduleObj, unitMorphCallback, env->CallStaticObjectMethod(unitCls, env->GetStaticMethodID(unitCls, \"get\", \"(J)Lbwapi/Unit;\"), (jlong)it->getUnit()));\n" +
|
||||
" break;\n" +
|
||||
" case EventType::UnitRenegade:\n" +
|
||||
" env->CallVoidMethod(moduleObj, unitRenegadeCallback, env->CallStaticObjectMethod(unitCls, env->GetStaticMethodID(unitCls, \"get\", \"(J)Lbwapi/Unit;\"), (jlong)it->getUnit()));\n" +
|
||||
" break;\n" +
|
||||
" case EventType::SaveGame:\n" +
|
||||
" env->CallVoidMethod(moduleObj, saveGameCallback, env->NewStringUTF(it->getText().c_str()));\n" +
|
||||
" break;\n" +
|
||||
" case EventType::UnitComplete:\n" +
|
||||
" env->CallVoidMethod(moduleObj, unitCompleteCallback, env->CallStaticObjectMethod(unitCls, env->GetStaticMethodID(unitCls, \"get\", \"(J)Lbwapi/Unit;\"), (jlong)it->getUnit()));\n" +
|
||||
" break;\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
out.println();
|
||||
}
|
||||
|
||||
private void implementMirror_BWAPIClientGetters() {
|
||||
out.println(
|
||||
"JNIEXPORT jboolean JNICALL Java_bwapi_Mirror_isConnected(JNIEnv * env, jclass jclz){\n" +
|
||||
" return BWAPI::BWAPIClient.isConnected();\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"JNIEXPORT jboolean JNICALL Java_bwapi_Mirror_connect(JNIEnv * env, jclass jclz){\n" +
|
||||
" return BWAPI::BWAPIClient.connect();\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"JNIEXPORT void JNICALL Java_bwapi_Mirror_disconnect(JNIEnv * env, jclass jclz){\n" +
|
||||
" BWAPI::BWAPIClient.disconnect();\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"JNIEXPORT void JNICALL Java_bwapi_Mirror_update(JNIEnv * env, jclass jclz){\n" +
|
||||
" BWAPI::BWAPIClient.update();\n" +
|
||||
"}\n"
|
||||
);
|
||||
out.println();
|
||||
}
|
||||
|
||||
private void implementVariablesBind(List<CDeclaration> declarationList) {
|
||||
out.println("jclass cls;");
|
||||
out.println("jmethodID getId;");
|
||||
|
@ -210,6 +199,7 @@ public class Bind {
|
|||
out.println("getId = env->GetMethodID(cls,\"<init>\", \"(III)V\");");
|
||||
} else {
|
||||
out.println("getId = env->GetStaticMethodID(cls, \"get\", \"(J)L" + context.getPackageName() + "/" + cClass.getName() + ";\");");
|
||||
out.println("table" + cClass.getName() + ".clear();");
|
||||
}
|
||||
printedIntro = true;
|
||||
}
|
||||
|
@ -251,7 +241,12 @@ public class Bind {
|
|||
}
|
||||
|
||||
public void implementBind(List<CDeclaration> declarationList) {
|
||||
implementMirrorInit(declarationList);
|
||||
implementHelpers();
|
||||
implementMirror_initTables(declarationList);
|
||||
implementMirror_getInternalGame();
|
||||
implementMirror_processGameEvents();
|
||||
implementMirror_BWAPIClientGetters();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ public class TypeTable {
|
|||
}
|
||||
}
|
||||
out.println();
|
||||
out.println("bool areTypeTablesInitialized = false;");
|
||||
out.println();
|
||||
}
|
||||
|
||||
private void checkTypeTable(CClass cClass) {
|
||||
|
|
|
@ -60,7 +60,12 @@ public class CallImplementer {
|
|||
"#include <jni.h>\n" +
|
||||
"#include <cstring>\n" +
|
||||
"\n" +
|
||||
"using namespace BWAPI;\n\n");
|
||||
"using namespace BWAPI;\n\n" +
|
||||
"\n" +
|
||||
"bool IdentityUnitFilter(Unit u) {\n" +
|
||||
"\treturn true;\n" +
|
||||
"}\n" +
|
||||
"\n");
|
||||
}
|
||||
|
||||
private void implementAccessObject(String clsName, String objName) {
|
||||
|
@ -382,7 +387,7 @@ public class CallImplementer {
|
|||
out.println("*it;");
|
||||
}
|
||||
if (!javaContext.isValueType(genericType)) {
|
||||
out.println("jobject elem = env->CallStaticObjectMethod(elemClass, getMethodID, (long)elem_ptr) ;");
|
||||
out.println("jobject elem = env->CallStaticObjectMethod(elemClass, getMethodID, (jlong)elem_ptr) ;");
|
||||
} else {
|
||||
out.println("jobject elem = env->NewObject(elemClass, elemConsID" + javaContext.implementCopyReturn(genericType, "elem_ptr") + ")" + SEMICOLON);
|
||||
}
|
||||
|
@ -505,6 +510,8 @@ public class CallImplementer {
|
|||
String genericType = convertToBWTA(Generic.extractGeneric(javaRetType));
|
||||
if (javaPackageName.equals("bwta") || (!genericType.equals("UnitType") && !genericType.equals("Position") && !genericType.equals("TilePosition"))){
|
||||
out.print(wrapInCCollection(genericType, javaMethodName) + " cresult = " + objectAccessor + (javaContext.isSelfReturnType(clsName, javaMethodName) ? "" : javaMethodName + "("));
|
||||
if (javaContext.needsExtraFilterParameter(javaMethodName))
|
||||
params.add(javaContext.getExtraFilterParameter(javaMethodName));
|
||||
implementRealParams(params);
|
||||
if (!javaContext.isSelfReturnType(clsName, javaMethodName)) {
|
||||
out.print(")");
|
||||
|
|
|
@ -41,6 +41,7 @@ public class ClassMirror extends Mirror {
|
|||
out.println();
|
||||
writeFields(cClass.getFields());
|
||||
if (!cClass.isStatic()) {
|
||||
writeEqualsAndHashCodeMethods();
|
||||
writeInstanceMap();
|
||||
writeConstructor();
|
||||
writeInstanceGetter();
|
||||
|
@ -53,6 +54,73 @@ public class ClassMirror extends Mirror {
|
|||
nativeBinds = null;
|
||||
}
|
||||
|
||||
private void writeEqualsMethodBeginning() {
|
||||
out.println(INTEND + "@Override");
|
||||
out.println(INTEND + "public boolean equals(Object o) {");
|
||||
out.println(INTEND + INTEND + "if (this == o) return true;");
|
||||
out.println(INTEND + INTEND + "if (o == null || getClass() != o.getClass()) return false;");
|
||||
out.println();
|
||||
out.println(INTEND + INTEND + cClass.getName() + " other = (" + cClass.getName() + ")o;");
|
||||
out.println();
|
||||
}
|
||||
|
||||
private void writeEqualsMethodEnding() {
|
||||
out.println();
|
||||
out.println(INTEND + INTEND + "return true;");
|
||||
out.println(INTEND + "}");
|
||||
}
|
||||
|
||||
private void writeHashCodeMethodBeginning() {
|
||||
out.println();
|
||||
out.println(INTEND + "@Override");
|
||||
out.println(INTEND + "public int hashCode() {");
|
||||
out.println(INTEND + INTEND + "int result;");
|
||||
}
|
||||
|
||||
private void writeHashCodeMethodEnding() {
|
||||
out.println(INTEND + INTEND + "return result;");
|
||||
out.println(INTEND + "}");
|
||||
out.println();
|
||||
}
|
||||
|
||||
// for the simpler cases where the BWAPI object has an ID, and our equality test
|
||||
// only needs to be a comparison of this ID. ID is assumed to be an int
|
||||
private void writeEqualsAndHashCodeMethodsForObjWithID() {
|
||||
writeEqualsMethodBeginning();
|
||||
out.println(INTEND + INTEND + "if (getID() != other.getID()) return false;");
|
||||
writeEqualsMethodEnding();
|
||||
writeHashCodeMethodBeginning();
|
||||
out.println(INTEND + INTEND + "result = getID();");
|
||||
writeHashCodeMethodEnding();
|
||||
}
|
||||
|
||||
// HACK: manually stuffing in some implementations for specific classes for now.
|
||||
// it would probably take a fair bit of work to rig up some general-purpose
|
||||
// solution for all classes ....
|
||||
private void writeEqualsAndHashCodeMethods() {
|
||||
String className = cClass.getName();
|
||||
String packageName = context.getPackage();
|
||||
|
||||
if (className.equals("BaseLocation")) {
|
||||
writeEqualsMethodBeginning();
|
||||
out.println(INTEND + INTEND + "if (!getPosition().equals(other.getPosition())) return false;");
|
||||
writeEqualsMethodEnding();
|
||||
writeHashCodeMethodBeginning();
|
||||
out.println(INTEND + INTEND + "result = getPosition().hashCode();");
|
||||
writeHashCodeMethodEnding();
|
||||
} else if (className.equals("Bullet")) {
|
||||
writeEqualsAndHashCodeMethodsForObjWithID();
|
||||
} else if (className.equals("Force")) {
|
||||
writeEqualsAndHashCodeMethodsForObjWithID();
|
||||
} else if (className.equals("Player")) {
|
||||
writeEqualsAndHashCodeMethodsForObjWithID();
|
||||
} else if (className.equals("Region") && packageName.equals("bwapi")) {
|
||||
writeEqualsAndHashCodeMethodsForObjWithID();
|
||||
} else if (className.equals("Unit")) {
|
||||
writeEqualsAndHashCodeMethodsForObjWithID();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeInstanceMap() {
|
||||
out.println(INTEND + "private" + SPACE + "static" + SPACE + "Map<Long," + SPACE + cClass.getName() + "> instances = " +
|
||||
"new HashMap<Long," + SPACE + cClass.getName() + ">()" + SEMICOLON);
|
||||
|
|
|
@ -1,26 +1,20 @@
|
|||
package bwapi;
|
||||
|
||||
import bwapi.AIModule;
|
||||
import bwapi.BWEventListener;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
import java.lang.Exception;
|
||||
import java.lang.UnsupportedOperationException;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.*;
|
||||
|
||||
public class Mirror {
|
||||
|
||||
public static final String LOG_TAG = "[BWMirror] ";
|
||||
|
||||
private Game game;
|
||||
|
||||
private AIModule module = new AIModule();
|
||||
|
||||
private FrameCallback frameCallback;
|
||||
|
||||
private static final boolean EXTRACT_JAR = true;
|
||||
|
||||
private static void extractResourceFile(String resourceFilename, String outputFilename) throws Exception {
|
||||
InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceFilename);
|
||||
if (in == null)
|
||||
|
@ -42,16 +36,16 @@ public class Mirror {
|
|||
|
||||
private static boolean extractAndLoadNativeLibraries() {
|
||||
try {
|
||||
System.out.println("Extracting bwapi_bridge.dll");
|
||||
System.out.println(LOG_TAG + "Extracting bwapi_bridge.dll");
|
||||
extractResourceFile("bwapi_bridge.dll", "./bwapi_bridge.dll");
|
||||
|
||||
System.out.println("Extracting libgmp-10.dll");
|
||||
System.out.println(LOG_TAG + "Extracting libgmp-10.dll");
|
||||
extractResourceFile("libgmp-10.dll", "./libgmp-10.dll");
|
||||
|
||||
System.out.println("Extracting libmpfr-4.dll");
|
||||
System.out.println(LOG_TAG + "Extracting libmpfr-4.dll");
|
||||
extractResourceFile("libmpfr-4.dll", "./libmpfr-4.dll");
|
||||
|
||||
System.out.println("Loading native library bwapi_bridge.dll");
|
||||
System.out.println(LOG_TAG + "Loading native library bwapi_bridge.dll");
|
||||
System.load(new File("./bwapi_bridge.dll").getAbsolutePath());
|
||||
|
||||
} catch (Exception e) {
|
||||
|
@ -66,12 +60,11 @@ public class Mirror {
|
|||
try {
|
||||
Collection<String> bwtaFilenames = ResourceList.getResources(Pattern.compile("bwapi\\-data/BWTA2/[a-zA-Z0-9]+\\.bwta"));
|
||||
|
||||
System.out.println("Creating ./bwapi-data/BWTA2 directory");
|
||||
System.out.println(LOG_TAG + "Creating ./bwapi-data/BWTA2 directory");
|
||||
new File("./bwapi-data/BWTA2").mkdirs();
|
||||
|
||||
System.out.println("Extracting " + bwtaFilenames.size() + " BWTA2 files:");
|
||||
System.out.println("Extracting " + bwtaFilenames.size() + " BWTA2 files to ./bwapi-data/BWTA2");
|
||||
for (String filename : bwtaFilenames) {
|
||||
System.out.println(filename);
|
||||
String outputFilename = "./" + filename;
|
||||
extractResourceFile(filename, outputFilename);
|
||||
}
|
||||
|
@ -84,16 +77,24 @@ public class Mirror {
|
|||
return true;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
static {
|
||||
String arch = System.getProperty("os.arch");
|
||||
if(!arch.equals("x86")){
|
||||
throw new UnsupportedOperationException("BWMirror API supports only x86 architecture.");
|
||||
}
|
||||
if (is64BitJRE())
|
||||
throw new UnsupportedOperationException("BWMirror must be run on a 32-bit JRE/JDK.");
|
||||
|
||||
if (!extractAndLoadNativeLibraries())
|
||||
System.exit(1);
|
||||
if (!extractBwtaDataFiles())
|
||||
System.exit(1);
|
||||
|
||||
initTables();
|
||||
}
|
||||
|
||||
public Game getGame() {
|
||||
|
@ -105,26 +106,112 @@ public class Mirror {
|
|||
}
|
||||
|
||||
/**
|
||||
* Starts the API, initializes all constants ( {@link UnitType}, {@link WeaponType} ) and the {@link Game} object.
|
||||
* This method blocks until the end of game.
|
||||
* Initializes all BWAPI constant lookup tables.
|
||||
*/
|
||||
public native void startGame();
|
||||
|
||||
private void update() {
|
||||
if (frameCallback != null) {
|
||||
frameCallback.update();
|
||||
}
|
||||
}
|
||||
|
||||
/*public void setFrameCallback(bwapi.Mirror.FrameCallback frameCallback) {
|
||||
this.frameCallback = frameCallback;
|
||||
} */
|
||||
public static native void initTables();
|
||||
|
||||
/**
|
||||
* The simplest interface to receive update event from Broodwar. The {@link #update()} method is called once each frame.
|
||||
* For a simple bot and implementation of this interface is enough, to receive all in game events, implement {@link BWEventListener}.
|
||||
* 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. Will automatically attempt to
|
||||
* reconnect to Broodwar if the connection is lost at any point. If this method is called
|
||||
* before Broodwar is running, will wait 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.
|
||||
*
|
||||
* @param returnOnMatchEnd
|
||||
* If true, will disconnect from Broodwar and return after the first match ends
|
||||
* (regardless of how it ended). You can call {@link #startGame} again to run another
|
||||
* match as needed.
|
||||
* If false, will run an infinite loop allowing you to keep your bot running as many
|
||||
* subsequent matches as desired.
|
||||
*/
|
||||
/*public*/ private interface FrameCallback {
|
||||
public void update();
|
||||
public void startGame(boolean returnOnMatchEnd) {
|
||||
System.out.println(LOG_TAG + "Connecting to Broodwar...");
|
||||
if (reconnect())
|
||||
System.out.println(LOG_TAG + "Connection successful, starting match...");
|
||||
else {
|
||||
System.out.println(LOG_TAG + "Connection attempt aborted.");
|
||||
return;
|
||||
}
|
||||
|
||||
game = getInternalGame();
|
||||
|
||||
boolean inGame = game.isInGame();
|
||||
boolean previouslyInGame = inGame;
|
||||
if (inGame)
|
||||
System.out.println(LOG_TAG + "Match already running.");
|
||||
|
||||
while (true) {
|
||||
if (!inGame) {
|
||||
if (previouslyInGame) {
|
||||
System.out.println(LOG_TAG + "Match ended.");
|
||||
if (returnOnMatchEnd)
|
||||
break;
|
||||
}
|
||||
|
||||
update();
|
||||
|
||||
} else {
|
||||
if (!previouslyInGame)
|
||||
System.out.println(LOG_TAG + "Game ready!!!");
|
||||
|
||||
processGameEvents();
|
||||
update();
|
||||
}
|
||||
|
||||
if (!isConnected()) {
|
||||
System.out.println(LOG_TAG + "Reconnecting...");
|
||||
reconnect();
|
||||
}
|
||||
|
||||
if (Thread.interrupted()) {
|
||||
System.out.println(LOG_TAG + "Interrupted.");
|
||||
break;
|
||||
}
|
||||
|
||||
previouslyInGame = inGame;
|
||||
inGame = game.isInGame();
|
||||
}
|
||||
|
||||
System.out.println(LOG_TAG + "Finished.");
|
||||
System.out.println(LOG_TAG + "Disconnecting from Broodwar...");
|
||||
|
||||
if (isConnected())
|
||||
disconnect();
|
||||
|
||||
game = null;
|
||||
|
||||
System.out.println(LOG_TAG + "Returning...");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean reconnect() {
|
||||
while (!connect()) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
// needed to set the interrupt status of this thread.
|
||||
// else, subsequent calls to Thread.interrupted() will return false.
|
||||
Thread.currentThread().interrupt();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
|
|
@ -43,8 +43,7 @@ public class ResourceList {
|
|||
return retval;
|
||||
}
|
||||
|
||||
private static Collection<String> getResources(final String element,
|
||||
final Pattern pattern) {
|
||||
private static Collection<String> getResources(final String element, final Pattern pattern) {
|
||||
final ArrayList<String> retval = new ArrayList<>();
|
||||
final File file = new File(element);
|
||||
if (file.isDirectory()) {
|
||||
|
@ -55,8 +54,16 @@ public class ResourceList {
|
|||
throw new java.lang.Error(e);
|
||||
}
|
||||
retval.addAll(getResourcesFromDirectory(file, baseDirectory, pattern));
|
||||
} else {
|
||||
} else if (file.isFile()) {
|
||||
retval.addAll(getResourcesFromJarFile(file, pattern));
|
||||
} else {
|
||||
// if 'element' did not point to an existing file or directory, we don't
|
||||
// add anything to the list of resources to return
|
||||
// sometimes, build tools / IDEs that start up a java process will
|
||||
// specifically add classpath entries that do not point to files/directories
|
||||
// that exist (e.g. Leiningen adds a 'test' directory to the classpath
|
||||
// and this will be listed in 'java.class.path', whether it exists or not).
|
||||
// so it is actually pretty important that we not throw an exception here!
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
|
Reference in a new issue