diff --git a/.github/workflows/e2e-linux.yml b/.github/workflows/e2e-linux.yml new file mode 100644 index 00000000..b06df1dc --- /dev/null +++ b/.github/workflows/e2e-linux.yml @@ -0,0 +1,124 @@ +name: E2E OpenBW Linux + +on: + - pull_request + +jobs: + e2e: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 8 + uses: actions/setup-java@v4 + with: + java-version: '8' + distribution: 'temurin' + cache: maven + + - name: Cache OpenBW runtime and MPQs + id: cache-openbw + uses: actions/cache@v4 + with: + path: openbw-runtime + key: openbw-linux-v2 + + - name: Build OpenBW and fetch MPQs + if: steps.cache-openbw.outputs.cache-hit != 'true' + run: | + sudo apt-get update + sudo apt-get install -y clang libsdl2-dev libsdl2-mixer-dev + + git clone --depth 1 https://github.com/basil-ladder/openbw + git clone --depth 1 -b linux-client-support https://github.com/basil-ladder/bwapi + sed -i 's/switch *(command\.type)/switch ((UnitCommandType) command.type)/g' bwapi/bwapi/include/BWAPI/Client/CommandTemp.h + cmake -S bwapi -B bwapi/build \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_CXX_COMPILER=clang++ \ + -DCMAKE_CXX_FLAGS="-include cstdint" \ + -DCMAKE_BUILD_TYPE=Release \ + -DOPENBW_DIR="$PWD/openbw" \ + -DOPENBW_ENABLE_UI=1 \ + -DCMAKE_POLICY_VERSION_MINIMUM=3.5 + cmake --build bwapi/build --target BWAPILauncher -j$(nproc) + + mkdir -p openbw-runtime/bin openbw-runtime/lib + cp bwapi/build/bin/BWAPILauncher openbw-runtime/bin/ + cp bwapi/build/lib/*.so openbw-runtime/lib/ + ldd bwapi/build/lib/libOpenBWData.so | awk '/=> \// {print $3}' | grep -vE '/(libc|libm|libmvec|libdl|libpthread|librt|libgcc_s|libstdc\+\+|ld-linux)(\.so|-)' | xargs -r cp -L -t openbw-runtime/lib/ + + git clone --depth 1 https://github.com/dgant/mpqs + cp mpqs/STARDAT.MPQ openbw-runtime/StarDat.mpq + cp mpqs/BROODAT.MPQ openbw-runtime/BrooDat.mpq + cp mpqs/patch_rt.mpq openbw-runtime/Patch_rt.mpq + + - name: Compile JBWAPI and test bots + run: | + mvn -B test-compile dependency:build-classpath -Dmdep.outputFile=cp.txt --file pom.xml + javac -cp "target/classes:$(cat cp.txt)" -d target/test-classes it/bots/SittingDuck/src/main/java/SittingDuck.java + + - name: Run MarineHell vs SittingDuck match + run: | + mkdir -p openbw-runtime/maps openbw-runtime/bwapi-data + cp "it/(2)Destination.scx" "openbw-runtime/maps/(2)Destination.scx" + + cat << 'EOF' > openbw-runtime/bwapi-data/bwapi.ini + [ai] + ai = NULL + ai_dbg = NULL + + [auto_menu] + auto_menu = LAN + pause_dbg = OFF + lan_mode = Local Area Network (UDP) + auto_restart = OFF + map = maps/(2)Destination.scx + mapiteration = Random + race = Terran + enemy_count = 1 + enemy_race = Terran + game_type = MELEE + wait_for_min_players = 2 + wait_for_max_players = 2 + wait_for_time = 60000 + + [config] + holiday = OFF + shared_memory = ON + show_warnings = YES + EOF + + BOT_CP="$PWD/target/classes:$PWD/target/test-classes:$(cat cp.txt)" + + cd openbw-runtime + export LD_LIBRARY_PATH="$PWD/lib:${LD_LIBRARY_PATH:-}" + rm -rf /tmp/openbw /tmp/bwapi_socket_* /dev/shm/bwapi_shared_memory_* /tmp/launcher1.log /tmp/launcher2.log /tmp/duck.log /tmp/marinehell.log + + BWAPI_CONFIG_AUTO_MENU__CHARACTER_NAME="SittingDuck" OPENBW_ENABLE_UI=0 ./bin/BWAPILauncher > /tmp/launcher1.log 2>&1 & + L1_PID=$! + + java -cp "$BOT_CP" SittingDuck > /tmp/duck.log 2>&1 & + B1_PID=$! + + for i in $(seq 1 30); do + if grep -q "Connected" /tmp/duck.log 2>/dev/null; then + echo "SittingDuck connected!" + break + fi + sleep 0.5 + done + + BWAPI_CONFIG_AUTO_MENU__CHARACTER_NAME="MarineHell" OPENBW_ENABLE_UI=0 ./bin/BWAPILauncher > /tmp/launcher2.log 2>&1 & + L2_PID=$! + + timeout 60 java -cp "$BOT_CP" marinehell.MarineHell | tee /tmp/marinehell.log || true + kill $L1_PID $L2_PID $B1_PID 2>/dev/null || true + + if ! grep -q "Hello from JBWAPI!" /tmp/launcher1.log /tmp/launcher2.log || ! grep -q "Game ended! Winner: true" /tmp/marinehell.log; then + echo "=== launcher1.log ===" && cat /tmp/launcher1.log || true + echo "=== launcher2.log ===" && cat /tmp/launcher2.log || true + echo "=== duck.log ===" && cat /tmp/duck.log || true + exit 1 + fi diff --git a/.github/workflows/e2e-linux.yml.disabled b/.github/workflows/e2e-linux.yml.disabled deleted file mode 100644 index 00dfa614..00000000 --- a/.github/workflows/e2e-linux.yml.disabled +++ /dev/null @@ -1,50 +0,0 @@ -name: Test Linux - -on: - - pull_request - -jobs: - e2e: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 8 - uses: actions/setup-java@v2 - with: - java-version: '8' - distribution: 'temurin' - cache: maven - - name: Build with Maven - run: mvn -B package --file pom.xml - - - name: Setup and launch openbw - run: | - sudo apt-get update - sudo apt-get install cmake libsdl2-dev libsdl2-mixer-dev - - git clone https://github.com/basil-ladder/openbw - git clone -b linux-client-support https://github.com/basil-ladder/bwapi - cd bwapi - mkdir build - cd build - cmake .. -DCMAKE_BUILD_TYPE=Release -DOPENBW_DIR=../../openbw -DOPENBW_ENABLE_UI=1 - make -j4 - - curl http://www.cs.mun.ca/~dchurchill/starcraftaicomp/files/Starcraft_1161.zip -o starcraft.zip - unzip starcraft.zip patch_rt.mpq BROODAT.MPQ STARDAT.MPQ - mv patch_rt.mpq Patch_rt.mpq && mv BROODAT.MPQ BrooDat.mpq && mv STARDAT.MPQ StarDat.mpq - unzip starcraft.zip "maps/BroodWar/ICCup/ICCup Destination 1.1.scx" - - BWAPI_CONFIG_AUTO_MENU__RACE=Terran BWAPI_CONFIG_AUTO_MENU__MAP="maps/BroodWar/ICCup/ICCup Destination 1.1.scx" ./bin/BWAPILauncher& - BWAPI_CONFIG_AUTO_MENU__RACE=Terran BWAPI_CONFIG_AUTO_MENU__MAP="maps/BroodWar/ICCup/ICCup Destination 1.1.scx" ./bin/BWAPILauncher& - - - name: Build & Run test bots - run: | - mvn install -DskipTests - mvn -f it/bots/pom.xml package - - java -jar it/bots/SittingDuck/target/SittingDuck-*-jar-with-dependencies.jar & - sleep 3 - java -jar it/bots/jbwapibot/target/MarineHell-*-jar-with-dependencies.jar | grep "Hello from JBWAPI!" || exit 1 diff --git a/build_with_openbw.md b/build_with_openbw.md index 8df3f316..eff22363 100644 --- a/build_with_openbw.md +++ b/build_with_openbw.md @@ -16,21 +16,32 @@ cmake .. -DCMAKE_BUILD_TYPE=Release -DOPENBW_DIR=../../openbw -DOPENBW_ENABLE_UI make -j4 # Download runtime deps -curl http://www.cs.mun.ca/~dchurchill/starcraftaicomp/files/Starcraft_1161.zip -o starcraft.zip -unzip starcraft.zip patch_rt.mpq BROODAT.MPQ STARDAT.MPQ -mv patch_rt.mpq Patch_rt.mpq && mv BROODAT.MPQ BrooDat.mpq && mv STARDAT.MPQ StarDat.mpq +git clone --depth 1 https://github.com/dgant/mpqs +cp mpqs/patch_rt.mpq Patch_rt.mpq && cp mpqs/BROODAT.MPQ BrooDat.mpq && cp mpqs/STARDAT.MPQ StarDat.mpq -# Run openbw using the map: Destination 1.1.scx -unzip starcraft.zip "maps/BroodWar/ICCup/ICCup Destination 1.1.scx" - -BWAPI_CONFIG_AUTO_MENU__RACE=Terran BWAPI_CONFIG_AUTO_MENU__MAP="maps/BroodWar/ICCup/ICCup Destination 1.1.scx" ./bin/BWAPILauncher +# Run openbw using the map: (2)Destination.scx +BWAPI_CONFIG_AUTO_MENU__RACE=Terran BWAPI_CONFIG_AUTO_MENU__MAP="it/(2)Destination.scx" ./bin/BWAPILauncher ``` -Compiling and running openbw on macos with client support (not working): +Compiling and running openbw on macos with client support: ```bash -brew install cmake sdl2 sdl2_mixer gcc +# Download build deps +brew install cmake sdl2 sdl2_mixer -cmake .. -D CMAKE_C_COMPILER=gcc-11 -D CMAKE_CXX_COMPILER=g++-11 -DCMAKE_BUILD_TYPE=Release -DOPENBW_DIR=../../openbw -DOPENBW_ENABLE_UI=1 -make -j4 -``` \ No newline at end of file +# Build openbw with client support +git clone https://github.com/basil-ladder/openbw +git clone -b linux-client-support https://github.com/basil-ladder/bwapi +cd bwapi +mkdir build +cd build +cmake .. -DCMAKE_BUILD_TYPE=Release -DOPENBW_DIR=../../openbw -DOPENBW_ENABLE_UI=1 -DCMAKE_POLICY_VERSION_MINIMUM=3.5 +make -j4 + +# Download runtime deps +git clone --depth 1 https://github.com/dgant/mpqs +cp mpqs/patch_rt.mpq Patch_rt.mpq && cp mpqs/BROODAT.MPQ BrooDat.mpq && cp mpqs/STARDAT.MPQ StarDat.mpq + +# Run openbw using the map: (2)Destination.scx +BWAPI_CONFIG_AUTO_MENU__RACE=Terran BWAPI_CONFIG_AUTO_MENU__MAP="it/(2)Destination.scx" OPENBW_ENABLE_UI=1 ./bin/BWAPILauncher +``` diff --git a/it/(2)Destination.scx b/it/(2)Destination.scx new file mode 100644 index 00000000..9b7970dd Binary files /dev/null and b/it/(2)Destination.scx differ diff --git a/pom.xml b/pom.xml index b2848cb1..0dab66e9 100644 --- a/pom.xml +++ b/pom.xml @@ -144,14 +144,14 @@ - no.fiken.oss.junixsocket + com.kohlschutter.junixsocket junixsocket-common - 1.0.2 + 2.10.1 - no.fiken.oss.junixsocket + com.kohlschutter.junixsocket junixsocket-native-common - 1.0.2 + 2.10.1 diff --git a/src/main/java/bwapi/ClientConnectionPosix.java b/src/main/java/bwapi/ClientConnectionPosix.java index 7c2685d9..b74cec76 100644 --- a/src/main/java/bwapi/ClientConnectionPosix.java +++ b/src/main/java/bwapi/ClientConnectionPosix.java @@ -3,6 +3,7 @@ import com.sun.jna.LastErrorException; import com.sun.jna.Library; import com.sun.jna.Native; +import com.sun.jna.NativeLong; import com.sun.jna.Pointer; import com.sun.jna.platform.linux.Fcntl; import com.sun.jna.platform.linux.LibRT; @@ -24,7 +25,7 @@ class ClientConnectionPosix implements ClientConnection { interface LibCExt extends LibC { LibCExt INSTANCE = Native.load(LibCExt.class); - Pointer mmap(Pointer addr, int length, int prot, int flags, int fd, int offset) throws LastErrorException; + Pointer mmap(Pointer addr, NativeLong length, int prot, int flags, int fd, NativeLong offset) throws LastErrorException; } interface PosixShm extends Library { @@ -57,17 +58,26 @@ public void disconnect() { public WrappedBuffer getGameTable() { int fd = PosixShm.INSTANCE.shm_open("/bwapi_shared_memory_game_list", Fcntl.O_RDWR, 0); if (fd < 0) throw new IllegalStateException("SHM not found"); - Pointer gameTableView = LibCExt.INSTANCE.mmap(Pointer.NULL, GameTable.SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - return new WrappedBuffer(gameTableView, GameTable.SIZE); + try { + Pointer gameTableView = LibCExt.INSTANCE.mmap(Pointer.NULL, new NativeLong(GameTable.SIZE), PROT_READ | PROT_WRITE, MAP_SHARED, fd, new NativeLong(0)); + return new WrappedBuffer(gameTableView, GameTable.SIZE); + } finally { + LibCExt.INSTANCE.close(fd); + } } @Override public WrappedBuffer getSharedMemory(int serverProcID) { String sharedMemoryName = "/bwapi_shared_memory_" + serverProcID; try { - Pointer mapFileView = LibCExt.INSTANCE.mmap(Pointer.NULL, ClientData.GameData.SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, LibRT.INSTANCE.shm_open(sharedMemoryName, Fcntl.O_RDWR, 0), - 0); - return new WrappedBuffer(mapFileView, ClientData.GameData.SIZE); + int fd = PosixShm.INSTANCE.shm_open(sharedMemoryName, Fcntl.O_RDWR, 0); + if (fd < 0) throw new IllegalStateException("SHM not found"); + try { + Pointer mapFileView = LibCExt.INSTANCE.mmap(Pointer.NULL, new NativeLong(ClientData.GameData.SIZE), PROT_READ | PROT_WRITE, MAP_SHARED, fd, new NativeLong(0)); + return new WrappedBuffer(mapFileView, ClientData.GameData.SIZE); + } finally { + LibCExt.INSTANCE.close(fd); + } } catch (Exception e) { throw new SharedMemoryConnectionException(sharedMemoryName, e); } diff --git a/src/test/java/marinehell/MarineHell.java b/src/test/java/marinehell/MarineHell.java index bdf48135..ff1be98c 100644 --- a/src/test/java/marinehell/MarineHell.java +++ b/src/test/java/marinehell/MarineHell.java @@ -72,7 +72,7 @@ public void onStart() { BWTA.analyze(); int i = 0; - System.out.printf("Map name: %s; Map file name: %s\n", game.mapName(), game.mapFileName()); + System.out.printf("Map name: %s; Map file name: %s; Race: %s; Units: %d\n", game.mapName(), game.mapFileName(), self.getRace(), self.getUnits().size()); // Add a DrawShape call to ensure this doesn't block game.drawCircleScreen(new Position(200, 200), 3, Color.Black); @@ -170,8 +170,9 @@ public void onFrame() { // patch if (myUnit.getType().isWorker() && myUnit.isIdle()) { boolean skip = false; - if (bunker == null && bunkerBuilder != null && myUnit.equals(bunkerBuilder) - && barracks.isEmpty() == false) { + if ((bunker == null && bunkerBuilder != null && myUnit.equals(bunkerBuilder) + && barracks.isEmpty() == false) + || (myUnit.equals(searcher) && searchingScv > 0)) { skip = true; } @@ -283,7 +284,7 @@ public void onFrame() { for (BaseLocation b : BWTA.getBaseLocations()) { // If this is a possible start location, - if (b.isStartLocation()) { + if (b.isStartLocation() && !b.getTilePosition().equals(self.getStartLocation())) { baseLocations.add(b); } @@ -399,6 +400,11 @@ public void onFrame() { // game.drawTextScreen(10, 25, units.toString()); } + @Override + public void onEnd(boolean isWinner) { + System.out.println("Game ended! Winner: " + isWinner); + } + public static void main(String[] args) { new MarineHell().run(); }