Tutorial body

Note: This tutorial can be applied to PogamutUT2004 and will mostly hold for PogamutUDK examples.

Purpose of this first tutorial is to get you familiar with basic concepts which will be needed in further tutorials. We will show how to create a simple bot that will just stands in the environment talking about wether forecast. It is a Pogamut's analogy of the "Hello world !" program - a simple program used to demonstrate basics of given technology.

Setting up the example

This example is installed by Pogamut UT2004 installer. In NetBeans click New Project -> Maven -> Project From Archetype -> Local Archetypes Catalog and select 00-empty-bot-archetype project. Moreover, as Pogamut 3 has been fully mavenized, you can try and run this example even without installing the Pogamut NetBeans plugin. However in that case you won't be able to use visualization as this is a part of Pogamut NetBeans plugin. To open up this example in NetBeans follow up the steps in Opening Pogamut Examples chapter (if the archetype is not present, follow adding new Pogamut example project guide). This archetype information is below.

For UT2004 example:

  • Group Id: cz.cuni.amis.pogamut.ut2004.examples

  • Artifact Id: 00-empty-bot-archetype

  • Version: 3.3.0

  • Repository:http://diana.ms.mff.cuni.cz:8081/artifactory/repo

For UDK example only change Group Id: to cz.cuni.amis.pogamut.udk.examples and Version: 3.2.5-SNAPSHOT . The rest remains the same. Alternatively, you may consult Pogamut 3 with Maven Quickstart Tutorial.

Note: You will find up-to-date list of available archetypes in Pogamut Maven archetypes catalog

Opening the example, configuring server, executing the bot and getting around the IDE

  1. After setting up the example according to the section above, a new project named "00-empty-bot" will be opened in the Projects tab, open project's Source Packages sub folder and then the package present in this folder

    Note that sometimes instead of "00-empty-bot" you get a project with the name "badly formed maven project". In this case simply right click the project and select Clean and Build option. The project should reappear normally. If the project is reporting some bugs right click it and select the Clean and Build option again. The bugs are reported probably because Maven has not yet downloaded all dependencies (Clean and Build option assures that). If the Javadoc of the project is missing (help does not show up), right click the Dependencies folder and select Download Javadoc option.

  2. Finally double click EmptyBot.java file containing the source code of your first bot

Notice that the project's node in Projects tab is in bold, this means that the Run Main Project (F6) and Debug Main Project (Ctrl + F5) buttons are linked to this project. Before we start your first bot, we have to launch the Unreal Tournament dedicated server - an environment simulator that Pogamut and the bot will connect to. You have two options how to start the server:

  • First way - start standalone server

    1. execute the server by running startGamebotsDMServer.bat, wait until START MATCH string appears in the console, now the server is ready to receive bot connections. Shortcut to bat file startGamebotsDMServer.bat can be found in the start menu under Pogamut folder. File startGamebotsDMServer.bat can be found in /UT2004/System/ directory.

    2. let the server console window opened and return back to Netbeans, switch to Services tab (Ctrl + 5 or WindowServices), right click UT2004 Servers node and select Add server action

    3. in the dialog that will appear you can optionally assign Server name, e.g.. Local UT and you have to specify server's complete URI (first delete the [not set] string and then type the URI), in this case localhost:3001 then press Close button (if localhost does not work, try 127.0.0.1:3001 instead).

    4. just created Local UT server will appear under the UT2004 Servers node, you will have to create this server only once, it will remain registered in the IDE

  • Second way - start server through UT2004 GUI.

    1. Start UT2004 game through shortcut in the start menu or by running UT2004.exe in UT2004/System/ directory.

    2. In the UT2004 game menu click Host Game.

    3. In the next menu select GameType tab and select some of the GameBots2004 game types.

    4. After you finished setting up the server, click listen button located in lower right. The server will be started and you will be connected to it as a player.

    5. Connect to server from NetBeans the same way as shown above.

Now the server is running and the IDE knows how to connect to it. You can start the bot by pressing F6 ( Run Main Project ). Alternatively, you can simply right click the main bot class and select Run File or select the project and click Run. If everything works fine, the bot will connect to the server and begin execution. After starting, the bot will appear under the Local UT node in Pogamut bots folder. It will be represented by a node named EmptyBot. If you select the bot's node, you will see some additional information about the bot in Properties window (WindowProperties). Agent3D section shows properties defining bot's position in the space, Agent configuration section enables you to change bot's behavior/abilities (e.g. if the bot will be able to use raycasting, if it will automatically pick up items etc.). You will see that the bot is standing still, no movement, no rotation, after all, that's what it was designed for.

Inspecting the bot in Unreal Tournament

To see how the bot is doing in the game you will have to start the Unreal Tournament (UT) client, you can:

  • Start the UT directly from Netbeans

    Right click Local UT server node and select Spectate action, the UT2004 will start in the spectate mode and it will be automatically connected to the selected server.

  • Start UT in a standard way

    You can either use

When starting the UT for the first time it will open in a full screen mode, when developing bots you will often want to see both the Netbeans window and the UT. You have two options - use two monitors or configure UT to start in windowed mode, you can do this from main menu of UT SettingsDisplay tab → Resolution panel → uncheck the Full Screen checkbox.

Inspecting the bot directly in UT is a very handy debugging tool. You can see, where the bot exactly is, whether it has stuck etc. Gamebots, Pogamut's extension of the Unreal Tournament, also adds some in game visualization features that can be useful when observing the bot.

Once the UT has connected to the game, press the left mouse button to view the bot. Also notice the green text in the upper half of the screen labeled GAMEBOTS 2004 HUD HELP (press ALT + H to trigger HUD help on and off). You can try some of the listed functions, but we will address them later on.

Note:If the UT window seems to be not reacting to your key presses, try to press ALT key again - sometimes when switching between windows through ALT + TAB, the ALT button is left hanging in UT2004 with UT2004 thinking you are still pressing it even if you are not.

Bot's source code, startup sequence

Now return back to the Netbeans (if UT was in full screen mode use ALT + TAB). We will go through bot's source code in order to describe its basics.

The EmptyBot extends UT2004BotModuleController class, this is declared by line:

public class EmptyBot extends UT2004BotModuleController

UT2004BotModuleController is a base bot class that will suit most situations. UT2004BotModuleController class has several methods that have to be overridden by the EmptyBot and by any other bot extending this class.

These methods are:

  • logic() - method called periodically by an internal thread associated with the bot. It enables the bot to be proactive, that is to act "on his own" without any external stimuli.

  • botKilled(BotKilled event) - called each time the bot has died

  • botInitialized(GameInfo info, ConfigChange config, InitedMessage init) - called when the bot received INITED message from the server. This means that the INIT command succeeded, the handshake between bot and server is over and the bot is ready to receive other commands. Note that this does not mean the bot is already spawned in the environment! If it is set bAutoSpawn=False in GameBots ini file, the bot won't be spawned automatically and user needs to issue command Respawn to spawn the bot. Then botInitialized() method would be the right place to send first Respawn command. Now briefly about method parameters. info object holds information about the game type, map etc. config object holding information about bot current configuration - his vision time delay, autotracing setting, spawn setting etc. init object holds information about bot variables such as his speed in the environment, maximum possible health etc.

  • botFirstSpawn(GameInfo gameInfo, ConfigChange config, InitedMessage init, Self self) - called when the bot is spawned in the game for the first time. This means that the bot graphical represantation is visible in the game and Selfobject was received from the environment holding information about bot location and current state. This is the last place to do some preparations before logic() method will be called periodically.

  • getInitializeCommand() - used for setting initial properties of the agent such as it's name, starting location etc. This method is used by Pogamut to get the initialize command for the bot.

  • prepareBot(UT2004Bot bot) - called before the bot connects to the environment, but after UT2004Bot is constructed. This is the right place to initialize Pogamut bot modules (but about them later)!

Sequence of calling these methods is:

  1. prepareBot(UT2004Bot bot)

  2. getInitializeCommand()

  3. botInitialized(GameInfo info, ConfigChange config, InitedMessage init)

  4. botFirstSpawn(GameInfo gameInfo, ConfigChange config, InitedMessage init, Self self)

  5. logic() - called N times. Note that between two logic iterations, botKilled(BotKilled event) method may be called notifying about bot death.

Let's explore methods implemented in this example. First is getInitializeCommand():

    public Initialize getInitializeCommand() {
        return new Initialize().setName("EmptyBot");
    }

In this method an Initialize object is created, a bot's name is set and the initialized object is returned to the rest of Pogamut platform. Note that all setters in all Pogamut commands always return the same object, so you may chain the setters calls.

        new Initialize().setName("EmptyBot").setTeam(0);
    

Second method is botFirstSpawn(GameInfo gameInfo, ConfigChange config, InitedMessage init, Self self):

    public void botFirstSpawn(GameInfo gameInfo, ConfigChange config, InitedMessage init, Self self) {
        // Display a welcome message in the game engine
    	// right in the time when the bot appears in the environment, i.e., his body has been just spawned
    	// into the UT2004 for the first time.
        body.getCommunication().sendGlobalTextMessage("Hello world! I am alive!");

        // alternatively, you may use getAct() method for issuing arbitrary {@link CommandMessage} for the bot
        getAct().act(new SendMessage().setGlobal(true).setText("And I can speak! Hurray!"));
    }

The "Hello world!" message is printed there. Note that the body is the gateway for the most commands you may issue to the bot to Unreal Tournament. These commands are grouped together in several categories, like body.getCommunication(). All commands are well documented, so you can receive contextual help in NetBeans while coding.

Alternatively you can use the basic command interface accessed through getAct() method to issue commands. The getAct() method returns object implementing IAct interface that defines act(CommandMessage command) method. The act() method is the gateway for issuing actions represented by CommandMessage objects. SendMessage is a command sending message to a global chat channel - the constructor. Notice that the object is created and it's name property is set in only one line. The setText() method returns the instance on which it has been called, so more properties can be set in a chain e.g. new SendMessage().setGlobal(true).setText("And I can speak! Hurray!").

Note

To see all command objects you can use in your bot, open CommandMessage class definition. To do it as quickly as possible press Ctrl + O and type name of the class for search. In this case, type CommandMessage. Once the source opens in the central window press Alt + F12 to open Hierarchy browser. Now you can see list of all classes extending the CommandMessage which are commands provided by the PogamutUT2004.

Last method we will describe in this section is main(). This is the first method called when you execute the bot program (like in any other Java program). This method is responsible for instantiating the bot and connecting it to the environment. Bot runner class facilitates bot execution:

    public static void main(String args[]) throws PogamutException {
    	// wrapped logic for bots executions, suitable to run single bot in single JVM
    	new UT2004BotRunner(EmptyBot.class, "EmptyBot").setMain(true).startAgent();
    }
     

Note that setMain(true) is setter that should be used only in global main methods (i.e., public static void main(String args[])) so the Pogamut platform correctly terminates itself after the end of the bot run. Otherwise the JVM should not exit itself ... blame the JMX guys! Not us :-) So the rule of thumb, you're in main method, use setMain(true), you're starting the agent from different method, do not use it.

The logic() method will be described in the next section.

Logging and introspection

Two features that you will find handy during parameterization and evaluation of the bot are logging and introspection.

Logging is a mechanism for tracing program's execution by printing messages - it can be used both for debugging (multi threaded applications are often hard or impossible to debug using breakpoints) and evaluation (you can process logs of bot's activity and utilize some statistical software).

Introspection is designed to ease the bot's parameterization. It is often needed to adjust multiple behavior parameters at runtime and you will probably end up creating your own GUI (graphical user interface) for this purpose. In introspection, you just annotate desired variables with @JProp annotation and they will be accessible via the Netbeans GUI.

Let's look how logging and introspection works in EmptyBot example. First start the bot (F6), then have a look on it's source code. In the initial section several variables annotated with the @JProp are defined.

    @JProp
    public String stringProp = "Hello bot example";
    @JProp
    public boolean boolProp = true;
    @JProp
    public int intProp = 2;
    @JProp
    public double doubleProp = 1.0;

Now expand bot's node under the UT server node (in Services tab), you will see two new nodes - Logs and Introspection. After selecting the Introspection node the annotated variables will be shown in the Properties (Ctrl + Shift + 7) window. Note that the intProp variable is being continuously updated. New values of variables can be also set in this window.

Last non-empty method is logic(). There a simple follow player behavior is specified. The bot first checks whether he sees someone and if yes he tries to go to that player location and if not and he remembers some player he was following before, he goes to the last known spot of the player using advanced map navigation object pathExecutor.

    public void logic() throws PogamutException {
        if (players.canSeePlayers()) {
            lastPlayer = players.getNearestVisiblePlayer();
            move.moveTo(players.getNearestVisiblePlayer());
        } else {
            if (lastPlayer == null) {
                //we don't see any player and we don't even remeber lastPlayer - we will turn around
                move.turnHorizontal(30);
            } else {
                if (lastPlayer.getLocation().getDistance(info.getLocation()) < 200) {
                    //if we are close we will turn to the spot and forget the lastPlayer
                    move.turnTo(lastPlayer.getLocation());
                    if (info.isFacing(lastPlayer.getLocation())) {
                        lastPlayer = null;
                    }
                } else {
                    //if we are not closed we tell pathExecutor we want to go there - if
                    //we are not going there already
                    if (this.pathExecutor.isExecuting()) {
                        // WAIT TILL WE GET THERE
                    } else {
                        this.pathExecutor.followPath(
                                this.pathPlanner.computePath(
                                info.getLocation(),
                                lastPlayer.getLocation()));
                    }
                }
            }

        }
    }	  
	  

Note that you can observer Pogamut logs accesible by getLog() method under bot's Logs node.