Forum: PogamutUT2004

Gracefully Stop Bot

I feel as though I've been struggling with this problem throughout several different versions of Pogamut, and I've come up with several different hack-almost-solutions, but never really had one I was fully satisfied with.

So, if I have a bot running on a server, how do I stop the bot's execution gracefully? I want to stop the bot from running, have it disappear from the server, and be able to execute code afterwards without any nasty exceptions or fatal errors. I would like to do this several times, meaning after one bot stops, I want to start another.

On a related note: how do I gracefully kill a server (instance of IUT2004Server)? Let's say I launch a server to play a single match, and that match has a timelimit (defined in the UCCConfigWrapper). When the match is over, how do I 1) sense this from within Pogamut/Java, and 2) kill the server gracefully before it starts another map?
Just a quick answer, before Jakub answers more thoroughly. There are two methods for this in Pogamut. First one is bot.stop(); that should stop execution of the bot gracefully. Second one is a drastic bot.kill(); that just shuts everything down. However, both methods WILL produce FatalErrors in components - but those are basically only notifications of Pogamut shutting down (components discovered that Pogamut is shutting down).
Alternatively, you can use GameBots command getAct().act(new DisconnectBot()); that shuts down connection from the other side (Unreal side) - sometimes this can work more reliably in a sense that no socket connections remains hanging in the system (we had a minor issue that when using UnrealEngine2Runtime and stop command, sometimes socket connection remained active in JVM somewhere, preventing the bot to be disconnected from server). You can use this DisconnectBot command also for ControlServer connection. This will also produce FatalErrors, but bot and server should be shut down properly.
Jakub, your turn. :-)

Best,
Michal
Ok, that helps, but I'm still having some problems. I have a test bot with the following logic method:

public void logic() throws PogamutException {
if(game.getTime() > getParams().getEvalSeconds()){
System.out.println("Before stop");
bot.getAct().act(new DisconnectBot());
bot.stop();
System.out.println("After stop");
}
// TODO: Control the bot here
}

Also, I launch the bot with the following method:

public static int launchBot(String host, int botPort) throws PogamutException {
UT2004BotRunner runner = new UT2004BotRunner(NetworkControllerBot.class, "NetworkControllerBot").setMain(true).setHost(host).setPort(botPort);
runner.startAgents(new NetworkControllerBotParameters("Test"));
System.out.println("Match over ");
return 1;
}

Basically, I want to be able to return some value after the bot finishes execution. However, neither the "After stop" text in the logic method nor the "Match over" text in the launchBot method ever appear. The logic method doesn't matter too much, but I really need to regain control of execution when the bot it done and return a relevant result (1 is just a place-holder for this example). Instead, I'm getting the following errors:
(in particular, the ParserEOFException seems bad)

-------------------------
Before stop
(EvolvingBot) WARNING 16:38:14.450 Stopping agent NetworkControllerBot2-5 at 192.168.1.102/022ed0ee-e26a-367f-7da2-511e46dee0e8
(UCC) INFO 16:38:14.497 ID0 In: State: Dead, BeginState()
(UCC) INFO 16:38:14.498 ID0 BotConnection DM-1on1-Albatross.BotConnection (Function GameBots2004.BotConnection.ReceivedDisconnect:0045) Accessed None 'theBot'
(EvolvingBot) WARNING 16:38:14.546 Calling Mediator.kill().
(EvolvingBot) SEVERE 16:38:14.546 Fatal error in Mediator: MediatorWorker: Producer exception.
(EvolvingBot) SEVERE 16:38:14.548 Fatal error happenned - component bus is stopping.
FatalErrorEvent[
Component: Mediator[producer=WorldMessageTranslatorparser=UT2004Parser, handler=BotFSM, consumer=UT2004SyncLockableWorldView]
Message: MediatorWorker: Producer exception.
Cause: class cz.cuni.amis.pogamut.base.communication.parser.exception.ParserEOFException: cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Yylex@13e6226: EOF met, assuming that underlying reader has been closed. (at cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Yylex.yylex(Yylex.java:9374))
Stacktrace:
cz.cuni.amis.pogamut.base.component.controller.ComponentController.fatalError(ComponentController.java:518)
cz.cuni.amis.pogamut.base.communication.mediator.impl.Mediator$Worker.run(Mediator.java:312)
java.lang.Thread.run(Thread.java:619)
Caused by: class cz.cuni.amis.pogamut.base.communication.parser.exception.ParserEOFException: cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Yylex@13e6226: EOF met, assuming that underlying reader has been closed. (at cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Yylex.yylex(Yylex.java:9374))
cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Yylex.yylex(Yylex.java:9374)
cz.cuni.amis.pogamut.base.communication.parser.impl.yylex.YylexParser.parse(YylexParser.java:97)
cz.cuni.amis.pogamut.base.communication.translator.impl.WorldMessageTranslator.getEvent(WorldMessageTranslator.java:121)
cz.cuni.amis.pogamut.base.communication.mediator.impl.Mediator$Worker.run(Mediator.java:299)
java.lang.Thread.run(Thread.java:619)
]
(EvolvingBot) SEVERE 16:38:14.548 Received fatal error from Mediator.
(EvolvingBot) WARNING 16:38:14.548 Calling Connection.kill().
(EvolvingBot) SEVERE 16:38:14.548 Connection.kill()ed.
(EvolvingBot) SEVERE 16:38:14.548 Received fatal error from Mediator.
(EvolvingBot) WARNING 16:38:14.549 Calling Parser.kill().
(EvolvingBot) SEVERE 16:38:14.577 Parser.kill()ed.
(EvolvingBot) SEVERE 16:38:14.577 Received fatal error from Mediator.
(EvolvingBot) WARNING 16:38:14.578 Calling WorldMessageTranslator.kill().
(EvolvingBot) SEVERE 16:38:14.578 WorldMessageTranslator.kill()ed.
(EvolvingBot) SEVERE 16:38:14.578 Received fatal error from Mediator.
(EvolvingBot) WARNING 16:38:14.579 Calling WorldView.kill().
(EvolvingBot) SEVERE 16:38:14.579 WorldView.kill()ed.
(EvolvingBot) SEVERE 16:38:14.579 Received fatal error from Mediator.
(EvolvingBot) WARNING 16:38:14.580 Calling Act.kill().
(EvolvingBot) SEVERE 16:38:14.580 Act.kill()ed.
(EvolvingBot) SEVERE 16:38:14.580 Fatal error sensed: FatalErrorEvent[
Component: Mediator[producer=WorldMessageTranslatorparser=UT2004Parser, handler=BotFSM, consumer=UT2004SyncLockableWorldView]
Message: MediatorWorker: Producer exception.
Cause: class cz.cuni.amis.pogamut.base.communication.parser.exception.ParserEOFException: cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Yylex@13e6226: EOF met, assuming that underlying reader has been closed. (at cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Yylex.yylex(Yylex.java:9374))
Stacktrace:
cz.cuni.amis.pogamut.base.component.controller.ComponentController.fatalError(ComponentController.java:518)
cz.cuni.amis.pogamut.base.communication.mediator.impl.Mediator$Worker.run(Mediator.java:312)
java.lang.Thread.run(Thread.java:619)
Caused by: class cz.cuni.amis.pogamut.base.communication.parser.exception.ParserEOFException: cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Yylex@13e6226: EOF met, assuming that underlying reader has been closed. (at cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Yylex.yylex(Yylex.java:9374))
cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Yylex.yylex(Yylex.java:9374)
cz.cuni.amis.pogamut.base.communication.parser.impl.yylex.YylexParser.parse(YylexParser.java:97)
cz.cuni.amis.pogamut.base.communication.translator.impl.WorldMessageTranslator.getEvent(WorldMessageTranslator.java:121)
cz.cuni.amis.pogamut.base.communication.mediator.impl.Mediator$Worker.run(Mediator.java:299)
java.lang.Thread.run(Thread.java:619)
]
(EvolvingBot) WARNING 16:38:14.582 Not running, can't send DISCONNECT
---------------------------

So, how do I return a result after the bot is done executing. Basically, I want to be able to gather some data from the match and return it after executing and shutting down the bot.
First of all - always choose between sending disconnect, or calling the stop method. Calling stop after sending disconnect can sometimes prevent the disconnect command to be sent to GameBots (one row in the error report confirms this: (EvolvingBot) WARNING 16:38:14.582 Not running, can't send DISCONNECT ).

Now what I would do in order to get result from my bot is this:
1) I would detect with some method that I already have all the data I wanted and I am ready to shut down the bot
2) I would export the data to some file or store them to some object - in the mean time I would stop the bot logic (e.g. put in the bot logic something like if (exportingBeforeShutdown == true) return;) and unhook all the listeners I would use to gather data. I can emulate the return value of the bot - instead of returning the value I will store it in some object before I will shutdown the bot.
3) When 2) is done, I would send the disconnect command.

This way I've got data exported and the bot stopped. If I have other thread I am using to launch the bots I can catch the exception there and notify that the bot is finished. Now I can launch a new one.
Now if you want to resume this same bot then it will be a problem, because this is not yet possible. You could emulate something like this for yourself skipping the point 3), but instead resuming the bot execution again.

Does this help?

Best,
Michal
This seems to almost work without a hitch. However, the startAgents method of the bot runner throws a PogamutException. Specifically, AgentRunner throws an Exception at line 421:

throw new PogamutException("Agents can't be started: " + e.getMessage(), e, this);

However, at the point the Exception is thrown, I already have all the info about the bot I want, so I can just put the startAgents call in a try block, catch the PogamutException, and then completely ignore it. This still seems a bit sloppy, but it works.

Thanks for the help