Tutorial body

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

The outline:

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 stand in the environment, talk and do nothing. 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 this 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" section in the same chapter). 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.1

  • 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. Note that there is also a somehow outdated version of tutorial how to install Pogamut manually. Use it only if regular installation fails. Link: 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 CTRL + H to trigger HUD help on and off). You can try some of the listed functions, but we will address them later on.

Note:In pre-3.3.1 Pogamut version the HUD control key used to be ALT. However ALT button had issue with UT2004 not reacting to keys when ALT + TABing in and out - sometimes the ALT button was "left hanging" in UT2004 when ALT + TABing from the UT window. The solution was to press ALT button again when back in UT2004.

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.

  • beforeFirstLogic() - called after botFirstSpawn method, right before the first call of logic() method. This method is an ideal place to make last preparations of your custom modules before the logic() method gets executed. The advantage here is that when this method gets called your bot is fully initialized and present in the environment.

  • 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. beforeFirstLogic()

  6. 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.

Controlling the Logs. In Pogamut each major module usually has his own log category. When you set your log level to be high for Pogamut and Pogamut modules (to log even less important things) your console will be flooded with thousands of messages and this can cause performance problems in NetBeans (in Eclipse it is ok) - your bot will lag etc. To turn off logging in Pogamut add .setLogLevel(Level.OFF) to UT2004Runner:


    public static void main(String args[]) throws PogamutException {
        // wrapped logic for bots executions, suitable to run single bot in single JVM

        // you can forcingly set logging to aggressive level FINER so you would see (almost) all logs 
        // that describes decision making behind movement of the bot as well as incoming environment events
        new UT2004BotRunner(EmptyBot.class, "EmptyBot").setMain(true).setLogLevel(Level.OFF).startAgent();
    }	  
	  

This will turn off logging for Pogamut modules, however your user log should not be affected. In NetBeans we do not recommend to set log level higher then INFO. Alternatively, you can also turn off logging for Pogamut modules one by one. E.g.


    /**
     * This method is called only once right before actual logic() method is
     * called for the first time.
     */
    @Override
    public void beforeFirstLogic() {        
        //Turns off logging for path executor - logs hundreds of messages, useful when debugging path finding.
        pathExecutor.getLog().setLevel(Level.OFF);
        //Turns off logging for navigation module wrapping pathExecutor module.
        navigation.getLog().setLevel(Level.OFF);
        //Turns off your user specified logs you use in e.g. the logic() method - ( log.info("I'm logging."); )
        log.setLevel(Level.OFF);                       
    } 
	  

Last non-empty method is logic(). There is a commented code implementing a simple follow player behavior. Try to uncomment the code blocks and observe the bot following the first player he sees in the environment. More information about modules used here is in following tutorials.

    public void logic() throws PogamutException {
/*        
        // Mark that new logic iteration has begun        
        log.info("---LOGIC: " + (++logicIterationNumber) + "---");  
       
        // Log logic periods
        long currTime = System.currentTimeMillis();
        if (lastLogicTime > 0) log.info("Logic invoked after: " + (currTime - lastLogicTime) + " ms");
        lastLogicTime = currTime;
        
        // Can my but see any player?
        if (players.canSeePlayers()) {
            // YES!
            log.info("Can see any player/s?: YES");
            // Set my target to nearest visible player ...
            lastPlayer = players.getNearestVisiblePlayer();
            // ... and try to move with straight movement (without any navigation)
            log.info("Running directly to: " + lastPlayer.getId());
            move.moveTo(players.getNearestVisiblePlayer());            
            // We've just switched to manual movement ... stop path navigation if running
            if (navigation.isNavigating()) {
                navigation.stopNavigation();
            }
        } else {
            // NO, I cannot see any player
            log.info("Can see any player/s?: NO");
            
            if (lastPlayer == null) {
                log.info("lastPlayer == null ... no target to pursue, turning around");
                move.turnHorizontal(30);
            } else {
                log.info("lastPlayer == " + lastPlayer.getId() + " ... going to pursue him/her/it");
                // Yes, I should try to get to its last location
                if (info.getDistance(lastPlayer) < 200) { // are we at the last
                    log.info("Arrived to lastPlayer's last known location.");
                    move.turnTo(lastPlayer.getLocation());
                    if (info.isFacing(lastPlayer.getLocation())) {
                        lastPlayer = null;
                    }
                } else {
                    // We are still far from the last known player position
                    // => just tell the navigation to guide us there
                    log.info("Navigating to lastPlayer's last known location.");
                    navigation.navigate(lastPlayer);                    
                }
            }
        }
*/
    }	  
	  

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