An API for programmatically starting Age of Empires II: The Conquerors matches with specified settings. This is meant for offline usage only.
- 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
- Grab the release or compile the DLL yourself with the dependencies listed at the bottom of this page
- Load the DLL into age2_x1 process
- Connect with a msgpack-RPC client on address 127.0.0.1:64720
- 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.
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
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);
}
}
- 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.
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 |
- 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.
- Microsoft Detours - https://github.com/Microsoft/Detours
- rpclib (with a minor change) - https://github.com/FLWL/rpclib
- abductedPlatypus for the game speedup offset