View Javadoc

1   package cz.cuni.amis.pogamut.ut2004.tag.tournament;
2   
3   import java.io.File;
4   import java.io.IOException;
5   import java.util.ArrayList;
6   import java.util.Collections;
7   import java.util.Comparator;
8   import java.util.Formatter;
9   import java.util.HashMap;
10  import java.util.List;
11  import java.util.Map;
12  import java.util.Map.Entry;
13  import java.util.concurrent.CountDownLatch;
14  import java.util.concurrent.TimeUnit;
15  import java.util.logging.Level;
16  
17  import cz.cuni.amis.pogamut.base.agent.state.level0.IAgentState;
18  import cz.cuni.amis.pogamut.base.agent.state.level1.IAgentStateDown;
19  import cz.cuni.amis.pogamut.base.agent.state.level1.IAgentStateUp;
20  import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
21  import cz.cuni.amis.pogamut.base.utils.Pogamut;
22  import cz.cuni.amis.pogamut.base.utils.guice.AdaptableProvider;
23  import cz.cuni.amis.pogamut.base.utils.logging.LogCategory;
24  import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
25  import cz.cuni.amis.pogamut.ut2004.agent.module.sensor.AgentStats;
26  import cz.cuni.amis.pogamut.ut2004.analyzer.IUT2004AnalyzerObserver;
27  import cz.cuni.amis.pogamut.ut2004.analyzer.UT2004Analyzer;
28  import cz.cuni.amis.pogamut.ut2004.analyzer.stats.UT2004AnalyzerObsStats;
29  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.StartPlayers;
30  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.PlayerMessage;
31  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.PlayerScore;
32  import cz.cuni.amis.pogamut.ut2004.factory.guice.remoteagent.UT2004ServerFactory;
33  import cz.cuni.amis.pogamut.ut2004.server.impl.UT2004Server;
34  import cz.cuni.amis.pogamut.ut2004.tag.protocol.messages.TagPlayerScoreChanged;
35  import cz.cuni.amis.pogamut.ut2004.tag.server.BotTagRecord;
36  import cz.cuni.amis.pogamut.ut2004.tag.server.UT2004TagServer;
37  import cz.cuni.amis.pogamut.ut2004.tag.server.UT2004TagServerModule;
38  import cz.cuni.amis.pogamut.ut2004.tournament.botexecution.UT2004BotExecution;
39  import cz.cuni.amis.pogamut.ut2004.tournament.deathmatch.UT2004DeathMatchResult;
40  import cz.cuni.amis.pogamut.ut2004.tournament.match.UT2004BotConfig;
41  import cz.cuni.amis.pogamut.ut2004.tournament.match.UT2004Match;
42  import cz.cuni.amis.pogamut.ut2004.tournament.match.UT2004MatchResult;
43  import cz.cuni.amis.pogamut.ut2004.tournament.match.UT2004NativeBotConfig;
44  import cz.cuni.amis.pogamut.ut2004.utils.PogamutUT2004Property;
45  import cz.cuni.amis.pogamut.ut2004.utils.UCCWrapper;
46  import cz.cuni.amis.pogamut.ut2004.utils.UCCWrapperConf;
47  import cz.cuni.amis.pogamut.ut2004.utils.UT2004ServerRunner;
48  import cz.cuni.amis.utils.ExceptionToString;
49  import cz.cuni.amis.utils.FilePath;
50  import cz.cuni.amis.utils.NullCheck;
51  import cz.cuni.amis.utils.exception.PogamutException;
52  import cz.cuni.amis.utils.exception.PogamutIOException;
53  import cz.cuni.amis.utils.exception.PogamutInterruptedException;
54  import cz.cuni.amis.utils.flag.FlagListener;
55  import cz.cuni.amis.utils.maps.HashMapMap;
56  import cz.cuni.amis.utils.token.IToken;
57  
58  public class UT2004Tag extends UT2004Match<UT2004TagConfig, UT2004TagResult> {
59  	
60  	private int targetTagCount;
61  
62  	public UT2004Tag(UT2004TagConfig config, LogCategory log) {
63  		super(false, config, log);		
64  	}
65  	
66  	@Override
67  	protected void changeBotTeam(UT2004Server server, UnrealId botId, int desiredTeam) {
68  		// THERE IS NO NEED TO CHANGE BOT TEAM IN DEATHMATCH!
69  	}
70  
71  	@Override
72  	protected UT2004MatchResult waitMatchFinish(UCCWrapper ucc, UT2004Server server, UT2004Analyzer analyzer, Bots bots, long timeoutInMillis) {
73  		// usually the GB2004 dies out whenever match ends -> just wait till server does not fail + timeout + observe bots
74  		
75  		if (log != null && log.isLoggable(Level.WARNING)) {
76  			log.warning(config.getMatchId().getToken() + ": Waiting for the match to finish...");
77  		}
78  		
79  		if (config.getTimeLimit() * 60 * 1000 + 5 * 60 * 1000 > timeoutInMillis) {
80  			timeoutInMillis = config.getTimeLimit() * 60 * 1000 + 5 * 60 * 1000; // give additional 5 minutes to UT2004 to restart GB2004
81  		}
82  		
83  		Map<IToken, FlagListener<Boolean>> customBotObservers = new HashMap<IToken, FlagListener<Boolean>>(config.getBots().size());
84  		FlagListener<IAgentState> serverObs = null;
85  		FlagListener<Boolean> uccObs = null;
86  		IWorldEventListener<TagPlayerScoreChanged> scoresListener = null;
87  		
88  		final CountDownLatch waitLatch = new CountDownLatch(1);
89  		final AdaptableProvider<Boolean> oneOfBotsDiedOut = new AdaptableProvider<Boolean>(false);
90  		final AdaptableProvider<Boolean> serverDiedOut = new AdaptableProvider<Boolean>(false);
91  		final Map<UnrealId, TagPlayerScoreChanged> scores = new HashMap<UnrealId, TagPlayerScoreChanged>();
92  		
93  		boolean exception = false;
94  		
95  		try {
96  			scoresListener = new IWorldEventListener<TagPlayerScoreChanged>() {
97  
98  				@Override
99  				public void notify(TagPlayerScoreChanged event) {
100 					if (waitLatch == null || waitLatch.getCount() <= 0) {
101 						// DO NOT ACCEPT ANY MORE MESSAGES AFTER WE TERMINATE
102 						return;
103 					}
104 					scores.put(event.getBotId(), event);
105 					if (event.getScore() >= targetTagCount) {
106 						// TARGET SCORE REACHED BY ONE OF THE BOTS!
107 						waitLatch.countDown();
108 					}
109 				}
110 				
111 			};
112 			server.getWorldView().addEventListener(TagPlayerScoreChanged.class, scoresListener);
113 			
114 			for (UT2004BotConfig botConfig : config.getBots().values()) {
115 				FlagListener<Boolean> obs = new FlagListener<Boolean>() {
116 					@Override
117 					public void flagChanged(Boolean changedValue) {
118 						if (!changedValue) {
119 							// bot has died out
120 							oneOfBotsDiedOut.set(true);
121 							waitLatch.countDown();
122 						}
123 					}
124 				};
125 				
126 				bots.bots.get(botConfig.getBotId()).getRunning().addListener(obs);
127 				customBotObservers.put(botConfig.getBotId(), obs);
128 				if (!bots.bots.get(botConfig.getBotId()).getRunning().getFlag()) {
129 					// bot has died out
130 					oneOfBotsDiedOut.set(true);
131 					waitLatch.countDown();
132 					throw new PogamutException("One of custom bots died out from the start, failure!", log, this);
133 				}			
134 			}
135 			
136 			serverObs = new FlagListener<IAgentState>() {
137 	
138 				@Override
139 				public void flagChanged(IAgentState changedValue) {					
140 					if (changedValue instanceof IAgentStateDown) {
141 						// server has died out ... consider match to be over...
142 						serverDiedOut.set(true);
143 						waitLatch.countDown();
144 					}
145 				}
146 				
147 			};
148 			
149 			server.getState().addListener(serverObs);
150 			
151 			if (server.notInState(IAgentStateUp.class)) {
152 				// server has died out ... consider match to be over...
153 				serverDiedOut.set(true);
154 				waitLatch.countDown();
155 				throw new PogamutException("Server is dead from the start, failure!", log, this);
156 			}
157 			
158 			uccObs = new FlagListener<Boolean>() {
159 
160 				@Override
161 				public void flagChanged(Boolean changedValue) {
162 					if (changedValue) {
163 						// GAME IS ENDING!
164 						// Consider match to be over...
165 						serverDiedOut.set(true);
166 						waitLatch.countDown();
167 					}
168 				}
169 				
170 			};
171 			
172 			ucc.getGameEnding().addListener(uccObs);
173 			
174 			waitLatch.await(timeoutInMillis, TimeUnit.MILLISECONDS);
175 			if (waitLatch.getCount() > 0) {
176 				// TIMEOUT!
177 				throw new PogamutException("TIMEOUT! The match did not end in " + (timeoutInMillis / 1000) + " secs.", log, this);
178 			}
179 			
180 			bots.matchEnd = System.currentTimeMillis();
181 					
182 			// WHAT HAS HAPPENED?
183 			if (oneOfBotsDiedOut.get()) {
184 				// check whether the server is down as well... but let GB2004 to process it
185 				try {
186 					Thread.sleep(5000);
187 				} catch (InterruptedException e) {
188 					throw new PogamutInterruptedException("Interrupted while giving GB2004 time to tear down its connection.", log, this);
189 				}
190 				try {
191 					server.getAct().act(new StartPlayers());
192 				} catch (Exception e) {
193 					// YEP, server is down
194 					serverDiedOut.set(true);
195 				}
196 				if (!serverDiedOut.get()) {
197 					// NO SERVER IS STILL RUNNING
198 					log.warning("ONE OF BOTS HAS DIED OUT, BUT SERVER IS STILL RUNNING ... POSSIBLE MATCH FAILURE!");
199 				}
200 			}
201 			if (!serverDiedOut.get() && server.inState(IAgentStateUp.class)) {
202 				// Server is still running? ... This will likely to always happen as frag limit is targetFragCount+10 !!! 
203 				// Kill it...
204 				server.kill();
205 			}
206 			// server is DEAD -> assume that the match has ended
207 			
208 			// KILL UCC TO ENSURE NOTHING WILL CHANGE AFTER THAT
209 			if (ucc != null) {
210 				try {
211 					if (log != null && log.isLoggable(Level.INFO)) {
212 						log.info(config.getMatchId().getToken() + ": Killing UCC...");
213 					} 
214 				} catch (Exception e) {				
215 				}
216 				try {
217 					ucc.stop();
218 				} catch (Exception e) {					
219 				}
220 			}
221 			
222 			List<UnrealId> winners = new ArrayList<UnrealId>(1);
223 			int maxTags = 0;
224 			
225 			// PROCESS THE RESULT
226 			for (Entry<UnrealId, IToken> entry : bots.unrealId2BotId.entrySet()) {
227 				TagPlayerScoreChanged playerScore = scores.get(entry.getKey());
228 				if (playerScore == null) {
229 					throw new PogamutException("Can't resolve the match result. One of the bot with botId '" + entry.getValue().getToken() + "' and corresponding unrealId '" + entry.getKey().getStringId() + "' has no score entry!", log, this);
230 				}
231 				if (playerScore.getScore() == maxTags) {
232 					winners.add(entry.getKey());
233 				} else
234 				if (playerScore.getScore() > maxTags) {
235 					maxTags = playerScore.getScore();
236 					winners.clear();
237 					winners.add(entry.getKey());
238 				}
239 			}
240 			for (Entry<UnrealId, IToken> entry : bots.nativeUnrealId2BotId.entrySet()) {
241 				TagPlayerScoreChanged playerScore = scores.get(entry.getKey());
242 				if (playerScore == null) {
243 					//did not scored, setting score to 0
244 					playerScore = new TagPlayerScoreChanged();
245 					playerScore.setBotId(entry.getKey());
246 					playerScore.setScore(0);					
247 					scores.put(entry.getKey(), playerScore);
248 				}
249 				if (playerScore.getScore() == maxTags) {
250 					winners.add(entry.getKey());
251 				} else
252 				if (playerScore.getScore() > maxTags) {
253 					maxTags = playerScore.getScore();
254 					winners.clear();
255 					winners.add(entry.getKey());
256 				}
257 			}
258 			
259 			if (winners.size() == 0) {
260 				// no one has reached FragLimit
261 				throw new PogamutException("There is no winner, impossible! **puzzled**", log, this);
262 			}
263 			if (winners.size() > 1) {
264 				StringBuffer sb = new StringBuffer();
265 				sb.append("There is more than one bot with highest score == " + maxTags + ": ");
266 				boolean first = true;
267 				for (UnrealId id : winners) {
268 					if (first) first = false;
269 					else sb.append(", ");
270 					sb.append("Bot[botId=" + bots.getBotId(id) + ", unrealId=" + id.getStringId() + ", score=" + scores.get(id).getScore() + "]");
271 				}
272 				sb.append(".");
273 				if (log != null && log.isLoggable(Level.WARNING)) {
274 					log.warning(sb.toString());
275 				}
276 				
277 				//throw new PogamutException(sb.toString(), log, this);
278 			}
279 			
280 			if (log != null && log.isLoggable(Level.WARNING)) {
281 				log.warning(config.getMatchId().getToken() + ": MATCH FINISHED!");
282 			}
283 			
284 			return processResults(ucc, server, analyzer, bots, winners, scores);
285 			
286 		} catch (Exception e) {
287 			exception = true;
288 			throw new PogamutException("Failed to perform the match!", e, log, this);
289 		} finally {
290 			for (Entry<IToken, FlagListener<Boolean>> entry : customBotObservers.entrySet()) {
291 				bots.bots.get(entry.getKey()).getRunning().removeListener(entry.getValue());
292 			}
293 			server.getState().removeListener(serverObs);
294 			server.getWorldView().removeEventListener(PlayerScore.class, scoresListener);
295 		}		
296 
297 	}
298 	
299 	protected UT2004TagResult processResults(UCCWrapper ucc, UT2004Server server, UT2004Analyzer analyzer, Bots bots, List<UnrealId> winners, Map<UnrealId, TagPlayerScoreChanged> finalScores) {
300 		UT2004TagServer tagServer = (UT2004TagServer) server;
301 		
302 		if (log != null && log.isLoggable(Level.FINE)) {
303 			log.fine(config.getMatchId().getToken() + ": Processing results...");
304 		}
305 		
306 		UT2004TagResult result = new UT2004TagResult();
307 		
308 		result.setMatchTimeEnd(((double)bots.matchEnd - (double)bots.matchStart) / (1000));
309 		
310 		for (Entry<UnrealId, BotTagRecord<PlayerMessage>> entry : tagServer.getBotRecords().entrySet()) {
311 			IToken token;
312 			token = bots.getBotId(entry.getKey());
313 			
314 			result.getPlayerScores().put(token, entry.getValue());
315 		}
316 				
317 		
318 		for (Entry<UnrealId, TagPlayerScoreChanged> entry : finalScores.entrySet()) {
319 			result.getTotalTags().put(entry.getKey(), entry.getValue().getScore());
320 		}
321 		
322 		HashMapMap<IToken,IToken, Integer> tagCounts = result.getTagCounts();
323 		Map<IToken,Integer> wasTagged = result.getWasTagged();
324 		for (BotTagRecord<PlayerMessage> tagRecord : tagServer.getBotRecords().values()) {
325 			IToken tagger = bots.getBotId(tagRecord.getBotId());
326 			
327 			for (Entry<UnrealId,List<Long>> entry : tagRecord.getTagPassedMillis().entrySet()) {
328 				IToken receiver = bots.getBotId(entry.getKey());
329 				int tagCnt = entry.getValue().size();
330 				
331 				if (!tagCounts.containsKey(tagger))
332 					tagCounts.put(tagger, new HashMap<IToken, Integer>());				
333 				tagCounts.get(tagger).put(receiver, tagCnt);				
334 			}
335 					
336 			for (Entry<UnrealId,List<Long>> entry : tagRecord.getTagGotMillis().entrySet()) {
337 				IToken enemyTagger = bots.getBotId(entry.getKey());
338 				wasTagged.put(enemyTagger, entry.getValue().size());				
339 			}			
340 													
341 		}
342 				
343 		
344 		for (Entry<IToken, IUT2004AnalyzerObserver> entry : bots.botObservers.entrySet()) {
345 			if (!(entry.getValue() instanceof UT2004AnalyzerObsStats)) {
346 				throw new PogamutException("There is an observer of wrong class, expecting UT2004AnalyzerObsStats, got " + entry.getValue().getClass().getSimpleName() + "!", log, this);
347 			}
348 			result.getBotObservers().put(entry.getKey(), (UT2004AnalyzerObsStats)entry.getValue());
349 		}
350 		
351 		if (winners.size() <= 0) {
352 			throw new PogamutException("There is no winner, impossible! **puzzled**", log, this);
353 		} else 
354 		if (winners.size() == 1) {
355 			result.setWinnerBot(bots.getBotId(winners.get(0)));
356 		} else {
357 			result.setDraw(true);
358 		}
359 		
360 		if (log != null && log.isLoggable(Level.WARNING)) {
361 			log.warning(config.getMatchId().getToken() + ": Results processed, " + (result.isDraw() ? "DRAW!" : "WINNER is Bot[botId=" + result.getWinnerBot().getToken() + ", unrealId=" + bots.getUnrealId(result.getWinnerBot()).getStringId() + "]."));
362 		}
363 		
364 		return result;
365 	}
366 	
367 	protected void outputResults_step1(UT2004TagResult result, File outputDirectory) {
368 		if (log != null && log.isLoggable(Level.FINE)) {
369 			log.fine(config.getMatchId().getToken() + ": Outputting match result into CSV file...");
370 		}
371 		
372 		File file = new File(outputDirectory.getAbsolutePath() + File.separator + "match-" + config.getMatchId().getToken() + "-result.csv");
373 		FilePath.makeDirsToFile(file);
374 		try {
375 			Formatter writer = new Formatter(file);
376 			writer.format("MatchId;TagLimit;TimeLimit;TagEnd;TimeEnd;Winner\n");
377 			writer.format
378 					(
379 						"%s;%d;%d;%d;%.3f;%s",
380 						config.getMatchId().getToken(),
381 						config.getTagLimit(),
382 						config.getTimeLimit(),
383 						result.getWinnerBot() == null ?
384 							// DRAW
385 							(result.getPlayerScores().size() == 0 ?
386 								0
387 								:
388 								result.getPlayerScores().values().iterator().next().getScore()
389 							)
390 							:
391 							// HAS WINNER
392 							result.getPlayerScores().get(result.getWinnerBot()).getScore(),
393 						result.getMatchTimeEnd(),
394 						result.isDraw() ? "DRAW" : String.valueOf(result.getWinnerBot().getToken())
395 					);
396 			try {
397 				writer.close();
398 			} catch (Exception e) {			
399 			}
400 		} catch (IOException e) {
401 			throw new PogamutIOException("Failed to write results!", e, log, this);
402 		}
403 		
404 		if (log != null && log.isLoggable(Level.INFO)) {
405 			log.info(config.getMatchId().getToken() + ": Match result output into " + file.getAbsolutePath() + ".");
406 		}
407 		
408 	}
409 	
410 	protected void outputResults_step2(UT2004TagResult result, File outputDirectory) {
411 		if (log != null && log.isLoggable(Level.FINE)) {
412 			log.fine(config.getMatchId().getToken() + ": Outputting match scores into CSV file...");
413 		}
414 		
415 		File file = new File(outputDirectory.getAbsolutePath() + File.separator + "match-" + config.getMatchId().getToken() + "-bot-scores.csv");
416 		FilePath.makeDirsToFile(file);
417 		try {
418 			Formatter writer = new Formatter(file);
419 			
420 			List<IToken> bots = new ArrayList<IToken>(config.getBots().keySet());
421 			List<IToken> nativeBots = new ArrayList<IToken>(config.getNativeBots().keySet());
422 			
423 			Collections.sort(bots, new Comparator<IToken>() {
424 				@Override
425 				public int compare(IToken o1, IToken o2) {
426 					return o1.getToken().compareTo(o2.getToken());
427 				}				
428 			});
429 			Collections.sort(nativeBots, new Comparator<IToken>() {
430 				@Override
431 				public int compare(IToken o1, IToken o2) {
432 					return o1.getToken().compareTo(o2.getToken());
433 				}				
434 			});
435 			result.setBots(bots);
436 			result.setNativeBots(nativeBots);						
437 			
438 			writer.format("botId");
439 			writer.format(";score;tags;taggedByOthers");
440 			for (IToken token : config.getAllBotIds()) {
441 				writer.format(";");
442 				writer.format(token.getToken());
443 			}
444 			
445 			for (IToken token : config.getAllBotIds()) {
446 				writer.format("\n");
447 				writer.format(token.getToken());
448 				
449 				writer.format(";%d", result.getPlayerScores().get(token).getScore());
450 				writer.format(";%d", result.getTotalTags().get(token));
451 				writer.format(";%d", result.getWasTagged().get(token));
452 				
453 				for (IToken token2 : config.getAllBotIds()) {
454 					writer.format(";%d", result.getTagCounts().get(token).get(token2));
455 				}				
456 			}
457 			
458 			try {
459 				writer.close();
460 			} catch (Exception e) {			
461 			}
462 		} catch (IOException e) {
463 			throw new PogamutIOException("Failed to write results!", e, log, this);
464 		}
465 		
466 		if (log != null && log.isLoggable(Level.INFO)) {
467 			log.info(config.getMatchId().getToken() + ": Match scores output into " + file.getAbsolutePath() + ".");
468 		}
469 		
470 	}
471 	
472 	@Override
473 	protected void outputResults(UCCWrapper ucc, UT2004Server server, UT2004Analyzer analyzer, Bots bots, UT2004MatchResult result,	File outputDirectory) {
474 		if (!(result instanceof UT2004TagResult)) {
475 			throw new PogamutException("Can't out results! Expected results of class UT2004DeathMatchResult and got " + result.getClass().getSimpleName() + "!", log, this);
476 		}
477 		outputResults_step1((UT2004TagResult) result, outputDirectory);
478 		outputResults_step2((UT2004TagResult) result, outputDirectory);
479 	}
480 	
481 	/**
482 	 * Usually STEP 4 ... after the UCC has started up, you usually want to connect to it to confirm it is up and running
483 	 * and be able to observe any changes in the environment / alter the environment, etc.
484 	 * <p><p>
485 	 * This method may need to be override to provide custom implementation of {@link UT2004Server} interface, i.e.,
486 	 * provide your custom control server. Current implementation is using {@link UT2004Server}.
487 	 * <p><p>
488 	 * Raises exception in case of any error.
489 	 * 
490 	 * @param ucc MUST NOT BE NULL
491 	 * @return running control server
492 	 */
493 	@Override
494 	protected UT2004Server startControlServer(UCCWrapper ucc) {
495 		if (log != null && log.isLoggable(Level.FINE)) {
496 			log.fine(config.getMatchId().getToken() + ": Starting UT2004Server...");
497 		}
498 		NullCheck.check(ucc, "ucc");
499         UT2004TagServerModule module = new UT2004TagServerModule();
500         UT2004ServerFactory factory = new UT2004ServerFactory(module);
501         UT2004ServerRunner serverRunner = new UT2004ServerRunner(factory);
502         
503         UT2004Server server = (UT2004Server) serverRunner.setMain(false).startAgent();	
504 		
505 		if (log != null && log.isLoggable(Level.INFO)) {
506 			log.info(config.getMatchId().getToken() + ": UT2004Server started.");
507 		}
508 		return server;
509 	}
510 	
511 	/**
512 	 * Optional (usually) STEP 7 ... set up any listeners needed.
513 	 * <p><p>
514 	 * Current implementation is empty.
515 	 * 
516 	 * @param ucc MUST NOT BE NULL
517 	 * @param server MUST NOT BE NULL
518 	 * @param analyzer may be null
519 	 * @param bots MUST NOT BE NULL
520 	 */
521 	protected void matchIsAboutToBegin(UCCWrapper ucc, UT2004Server server, UT2004Analyzer analyzer, Bots bots) {
522 	    // START THE TAG GAME
523 		UT2004TagServer tagServer = (UT2004TagServer) server;		
524 		tagServer.startGame(config.getTimeLimit(), config.getTagLimit());
525 	}		
526 
527 	/** Only for testing purposes */
528 	/*public static void main(String args[]) throws PogamutException {
529 		UT2004TagConfig cfg = new UT2004TagConfig();
530 		
531 		UCCWrapperConf uccConf = new UCCWrapperConf();
532 		uccConf.setGameType("BotDeathMatch");
533 		uccConf.setUnrealHome("c:\\Games\\UT2004\\");
534 		uccConf.setOptions("BotServerPort=3000?ControlServerPort=3001");
535 		cfg.setUccConf(uccConf);
536 		cfg.setTimeLimit(1);
537 		cfg.setOutputDirectory(new File("."));
538 		cfg.addNativeBot(new UT2004NativeBotConfig().setBotId("1"));
539 		cfg.addNativeBot(new UT2004NativeBotConfig().setBotId("2"));
540 		
541 		UT2004Tag tag = new UT2004Tag(cfg, null);
542 		tag.execute();				
543 	}*/
544     	
545 	
546 	@Override
547 	public UT2004TagResult execute() {
548 		try {
549 			if (log != null && log.isLoggable(Level.WARNING)) {
550 				log.warning(config.getMatchId().getToken() + ": Executing!");
551 			} 
552 		} catch (Exception e) {				
553 		}
554 		
555 		UCCWrapper ucc = null;
556 		UT2004Server server = null;
557 		Bots bots = null;
558 		UT2004Analyzer analyzer = null;
559 		String recordFileName = config.getMatchId().getToken() + "-replay-" + UT2004Match.getCurrentDate();
560 		boolean exception = false;
561 		
562 		// HACK!!!
563 		// We must set frag limit to actually BIGGER NUMBER because otherwise GB2004 would drop the connection sooner before telling us that some bot
564 		// has achieved required score :-/
565 		targetTagCount = getConfig().getTagLimit();
566 				
567 		try {
568 			// STEP 0
569 			setupLogger();
570 			
571 			// STEP 1
572 			validate();
573 			
574 			// STEP 2
575 			createGB2004Ini();
576 			
577 			// STEP 3
578 			ucc = startUCC();
579 			
580 			// STEP 4
581 			server = startControlServer(ucc);
582 			
583 			// STEP 5
584 			bots = startBots(ucc, server);
585 			
586 			// STEP 6
587 			analyzer = startAnalyzer(ucc, bots, getOutputPath("bots"), config.isHumanLikeLogEnabled());
588 			
589 			// STEP 7
590 			matchIsAboutToBegin(ucc, server, analyzer, bots);
591 			
592 			// STEP 8
593 			restartMatch(server, bots);
594 			
595 			// STEP 9			
596 			recordReplay(server, recordFileName);
597 			
598 			// STEP 9.5
599 			UT2004TagResult result = (UT2004TagResult) waitMatchFinish(ucc, server, analyzer, bots, config.getTimeLimit() * 1000 + 60 * 1000);
600 			
601 			// STEP 11
602 			copyReplay(ucc, recordFileName, getOutputPath());
603 
604 			// STEP 12
605 			outputResults(ucc, server, analyzer, bots, result, getOutputPath());
606 			
607 			// STEP 13
608 			shutdownAll(ucc, server, analyzer, bots);
609 			
610 			ucc = null;
611 			server = null;
612 			analyzer = null;
613 			bots = null;
614 			
615 			// WE'RE DONE! ... all that is left is a possible cleanup...
616 			return result;
617 			
618 		} catch (Exception e) {
619 			if (log != null && log.isLoggable(Level.SEVERE)) {
620 				log.severe(ExceptionToString.process(config.getMatchId().getToken() + ": EXCEPTION!", e));
621 			}
622 			exception = true;
623 			if (e instanceof PogamutException) throw (PogamutException)e;
624 			throw new PogamutException(e, log, this);
625 		} finally {	
626 			try {
627 				if (log != null && log.isLoggable(Level.INFO)) {
628 					log.info(config.getMatchId().getToken() + ": Cleaning up...");
629 				} 
630 			} catch (Exception e) {				
631 			}
632 			
633 			if (ucc != null) {
634 				try {
635 					if (log != null && log.isLoggable(Level.INFO)) {
636 						log.info(config.getMatchId().getToken() + ": Killing UCC...");
637 					} 
638 				} catch (Exception e) {				
639 				}
640 				try {
641 					ucc.stop();
642 				} catch (Exception e) {					
643 				}
644 			}
645 			if (server != null) {
646 				try {
647 					if (log != null && log.isLoggable(Level.INFO)) {
648 						log.info(config.getMatchId().getToken() + ": Killing UT2004Server...");
649 					} 
650 				} catch (Exception e) {				
651 				}
652 				try {
653 					server.kill();
654 				} catch (Exception e) {					
655 				}
656 			}
657 			if (bots != null) {
658 				try {
659 					if (log != null && log.isLoggable(Level.INFO)) {
660 						log.info(config.getMatchId().getToken() + ": Killing Custom bots...");
661 					} 
662 				} catch (Exception e) {				
663 				}
664 				for (UT2004BotExecution exec : bots.bots.values()) {
665 					try {
666 						exec.stop();					
667 					} catch (Exception e) {					
668 					}
669 				}
670 				try {
671 					if (log != null && log.isLoggable(Level.INFO)) {
672 						log.info(config.getMatchId().getToken() + ": Killing Custom bot observers...");
673 					} 
674 				} catch (Exception e) {				
675 				}
676 				for (IUT2004AnalyzerObserver obs : bots.botObservers.values()) {
677 					try {
678 						obs.kill();
679 					} catch (Exception e) {						
680 					}
681 				}
682 			}
683 			if (analyzer != null) {
684 				try {
685 					if (log != null && log.isLoggable(Level.INFO)) {
686 						log.info(config.getMatchId().getToken() + ": Killing UT2004Analyzer...");
687 					} 
688 				} catch (Exception e) {				
689 				}
690 				try {
691 					analyzer.kill();
692 				} catch (Exception e) {					
693 				}
694 			}	
695 			
696 			try {
697 				// STEP 10
698 				restoreGB2004IniBackup();
699 			} catch (Exception e) {				
700 			}
701 			
702 			try {
703 				if (log != null && log.isLoggable(Level.WARNING)) {
704 					if (exception) {
705 						log.warning(config.getMatchId().getToken() + ": Cleaned up, MATCH FAILED!");
706 					} else { 
707 						log.warning(config.getMatchId().getToken() + ": Cleaned up, match finished successfully.");
708 					}
709 				} 
710 			} catch (Exception e) {				
711 			}
712 			try {
713 				closeLogger();
714 			} catch (Exception e) {
715 				
716 			}
717 		}
718 		
719 	}
720 
721 }