Skip to content

Latest commit

 

History

History
163 lines (139 loc) · 11.1 KB

README.md

File metadata and controls

163 lines (139 loc) · 11.1 KB

aoc-auto-game

An API for programmatically starting Age of Empires II: The Conquerors matches with specified settings. This is meant for offline usage only.

Features

  • Direct C++ API (library exports)
  • msgpack-rpc based API that is callable from many languages, including Python, Java and C++
  • Change any game setting available in the in-game lobby programmatically
  • Run games faster than real-time (hours long matches can be played in minutes)
  • Allow a single-player match to run even after minimizing the game
  • Launch games with desired settings
  • Determine when a match ends
  • Determine the winning players
  • Restart or exit from a match
  • Run multiple matches simultaneously

Usage

  1. Grab the release or compile the DLL yourself with the dependencies listed at the bottom of this page
  2. Load the DLL into age2_x1 process
  3. Connect with a msgpack-RPC client on address 127.0.0.1:64720
  4. Issue commands to the game to automate starting games

To specify a custom port for the RPC server, start age2_x1 with -autogameport 64721 argument for example or any other port of your choosing. To run multiple instances of the game at the same time, start every age2_x1 process with the -multipleinstances argument.

Right now the best way to run games in the background is to use the SetRunUnfocused(True) to allow the game to continue running after it has been unfocused, and SetUseInGameResolution(False) to prevent the game from capturing the mouse while changing the resolution from the lobby to the in-game one. Optionally, the SetRunFullSpeed(True) can be used to run games at the maximum speed possible.

Check the example_scripts folder for how to do some of this.

Python example

Using the msgpack-rpc-python package.

This assumes that you have already loaded the DLL into the game. For an example on how to do this check the launch_aoc.py script in the example_scripts folder.

This starts a game on an all-visible random map. We'll have custom AIs "Barbarian" and "AT_Empire" facing off against each other in player slots 1 and 2 respectively.

import msgpackrpc
import time

autogame = msgpackrpc.Client(msgpackrpc.Address("127.0.0.1", 64720))
autogame.call('ResetGameSettings')                    # usually reset the settings to make sure everything is valid
autogame.call('SetGameRevealMap', 2)                  # set map to "All Visible"
autogame.call('SetGameMapType', 24)                   # choose a random map

autogame.call('SetPlayerComputer', 1, "Barbarian")    # put the Barbarian AI into player slot 1
autogame.call('SetPlayerComputer', 2, "AT_Empire")    # and AT_Empire into slot 2

autogame.call('SetRunFullSpeed', True)                # run the game logic as fast as possible (experimental)
autogame.call('SetRunUnfocused', True)                # allow the game to run while minimized
autogame.call('StartGame')                            # start the match

while autogame.call('GetGameInProgress'):             # wait until the game has finished
  print("Game is running...")
  time.sleep(1.0)

winner = autogame.call('GetWinningPlayer')
print("Game finished, winner: " + str(winner))
autogame.call('QuitGame')                             # go back to the main menu

Java example

Using the msgpack-rpc from Maven. We're starting an all-visible default map with 2 default AIs playing against each other.

import org.msgpack.rpc.Client;

public class Main
{
    public static void main(String[] args) throws Exception
    {
        Client autogame = new Client("127.0.0.1", 64720);
        autogame.callApply("ResetGameSettings", new Object[]{});
        autogame.callApply("SetGameRevealMap", new Object[]{2});

        autogame.callApply("SetPlayerComputer", new Object[]{1, ""});
        autogame.callApply("SetPlayerComputer", new Object[]{2, ""});

        autogame.callApply("StartGame", new Object[]{});

        while (autogame.callApply("GetGameInProgress", new Object[]{}).asBooleanValue().getBoolean())
        {
            System.out.println("Game is running...");
            Thread.sleep(1000);
        }

        int winner = autogame.callApply("GetWinningPlayer", new Object[]{}).asIntegerValue().getInt();
        System.out.println("Game finished, winner: " + winner);
    }
}

Notes

  • If including a human player, it is recommended to put them into player slot 1.
  • When including many players, it is preferred to use the player slots consecutively and avoid holes.
  • The default settings is a standard 1v1 matchup between a human player in slot 1 and a default AI player in slot 2, on Arabia.

API

Function Param 1 Param 2 Description
ResetGameSettings Reset all game settings and players to their valid default state. This also quits from any ongoing game, since changing settings during the game does not completely work.
StartGame Start the game with previously specified settings.
GetGameInProgress Whether a game is ongoing. Returns a boolean value.
GetGameTime Returns the amount of in-game seconds since the match start.
GetWinningPlayer Returns the first winning player number as an integer.
GetWinningPlayers Returns all winning players as a list.
RestartGame Restart the game if called while the game world has been loaded.
QuitGame Exits the match.
GetApiVersion Returns the aoc-auto-game API version as a float.
SetRunUnfocused bool RunUnfocused Whether to allow the game logic to continue running after the window has been minimized.
SetWindowMinimized bool Minimized Minimize or show the game window.
SetUseInGameResolution bool InGameResolution Change whether the game uses different resolutions for in-game and the main menu. If disabled, this is prevents the game from capturing the mouse when starting a game in the background.
SetRunFullSpeed bool FullSpeed Whether to run the game logic as fast as possible.
SetGameType int Type Sets the type of the game.
0 - Random Map
1 - Regicide
2 - Death Match
3 - Scenario
5 - King of the Hill
6 - Wonder Race
8 - Turbo Random Map
SetGameScenarioName string ScenarioName Scenario map name. Only used if GameType is set to 3.
"test_scenario"
SetGameMapType int MapType Sets the location of the random map.
9 - Arabia
10 - Archipelago
11 - Baltic
12 - Black forest
13 - Coastal
14 - Continental
15 - Crater Lake
16 - Fortress
17 - Gold Rush
18 - Highland
19 - Islands
20 - Mediterranean
21 - Migration
22 - Rivers
23 - Team Islands
24 - Random Map
25 - Scandinavia
26 - Mongolia
27 - Yucatan
28 - Salt marsh
29 - Arena
31 - Oasis
32 - Ghost Lake
33 - Nomad
34 - Iberia
35 - Britain
36 - Mideast
37 - Texas
38 - Italy
39 - Central America
40 - France
41 - Norse Lands
42 - Sea of Japan (East Sea)
43 - Byzantium
45 - Random Land Map
47 - Random Real World Map
48 - Blind Random
49 - Conventional Random Map
SetGameMapSize int MapSize Random map size.
0 - Tiny
1 - Small
2 - Medium
3 - Normal
4 - Large
5 - Giant
SetGameMapSizeDirectly int Width int Height Set the size of the map directly in coordinates rather than predefined size values.
SetGameDifficulty int Difficulty Difficulty of the game.
0 - Hardest
1 - Hard
2 - Moderate
3 - Standard
4 - Easiest
SetGameStartingResources int StartingResources The amount of resources players start the game with.
0 - Standard
1 - Low
2 - Medium
3 - High
SetGamePopulationLimit int PopulationLimit The maximum number of units players can concurrently have.
In increments of 25
SetGameRevealMap int RevealMap How much of the map the players can see.
0 - Normal
1 - Explored
2 - All Visible
SetGameStartingAge int StartingAge UserPatch 1.5 implements some new options in this category, not certain of those IDs.
0 - Standard
2 - Dark Age
3 - Feudal Age
4 - Castle Age
5 - Imperial Age
6 - Post-Imperial Age
SetGameVictoryType int VictoryType int VictoryValue Additional game victory condition.
0 - Standard
1 - Conquest
4 - Relics
7 - Time Limit
8 - Score
For time limit, at normal game speed, every 10 victory value units = 1 year in-game.
For score victory, this is the score required to win.
Otherwise this parameter can be set to 0.
SetGameTeamsTogether bool TeamsTogether Cannot be disabled in scenario maps.
SetGameLockTeams bool LockTeams Turn off diplomacy.
SetGameAllTechs bool AllTechs Lose unique properties of each civilization and allow all techs to be researched.
SetGameRecorded bool Recorded Whether to record the game into a replay file.
SetPlayerHuman int PlayerNumber Specifies that a human will be playing in this slot. Recommended to use slot 1.
1-8
SetPlayerComputer int PlayerNumber string AIFile Adds a specific computer player to the specified slot.
1-8 "Barbarian"
SetPlayerClosed int PlayerNumber Removes a player from a specific slot and marks it as closed.
1-8
SetPlayerCivilization int PlayerNumber int Civilization Set the player's civilization.
1-8 1 - Britons
2 - Franks
3 - Goths
4 - Teutons
5 - Japanese
6 - Chinese
7 - Byzantine
8 - Persians
9 - Saracens
10 - Turks
11 - Vikings
12 - Mongols
13 - Celts
14 - Spanish
15 - Aztec
16 - Mayan
17 - Huns
18 - Koreans
19 - Random
30 - Random
SetPlayerColor int PlayerNumber int Color Set color of the player's units and buildings.
1-8 1-8
SetPlayerTeam int PlayerNumber int Team Set which team the player belongs to.
1-8 0 - No team
1 - Team 1
2 - Team 2
3 - Team 3
4 - Team 4
5 - Random Team
GetPlayerExists int PlayerNumber Returns whether the player slot is taken.
1-8
GetPlayerAlive int PlayerNumber Returns whether the player is alive in the game.
1-8
GetPlayerScore int PlayerNumber Returns the player's in-game score.
1-8

Known issues

  • Calling "QuitGame" or other functions that change the game state from post match stats screen results in looping background music (although there's no reason to do any of that from there anyway).
  • Starting a "Defend the Wonder" match doesn't always work.

Dependencies

Credits

  • abductedPlatypus for the game speedup offset