1 package cz.cuni.amis.pogamut.ut2004.tournament;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.text.SimpleDateFormat;
6 import java.util.Date;
7 import java.util.Iterator;
8 import java.util.List;
9 import java.util.logging.Level;
10 import java.util.regex.Pattern;
11
12 import org.apache.commons.io.FileUtils;
13
14 import com.martiansoftware.jsap.FlaggedOption;
15 import com.martiansoftware.jsap.JSAP;
16 import com.martiansoftware.jsap.JSAPException;
17 import com.martiansoftware.jsap.JSAPResult;
18 import com.martiansoftware.jsap.Switch;
19
20 import cz.cuni.amis.pogamut.base.utils.logging.LogCategory;
21 import cz.cuni.amis.pogamut.ut2004.tournament.deathmatch.UT2004DeathMatch1v1;
22
23 public class UT2004DeathMatch1v1Console {
24
25 private static final char ARG_UT2004_HOME_DIR_SHORT = 'u';
26
27 private static final String ARG_UT2004_HOME_DIR_LONG = "ut2004-home-dir";
28
29 private static final char ARG_BOT1_JAR_SHORT = 'a';
30
31 private static final String ARG_BOT1_JAR_LONG = "bot1-jar";
32
33 private static final char ARG_BOT1_NAME_SHORT = 'b';
34
35 private static final String ARG_BOT1_NAME_LONG = "bot1-name";
36
37 private static final char ARG_BOT2_JAR_SHORT = 'c';
38
39 private static final String ARG_BOT2_JAR_LONG = "bot2-jar";
40
41 private static final char ARG_BOT2_NAME_SHORT = 'd';
42
43 private static final String ARG_BOT2_NAME_LONG = "bot2-name";
44
45 private static final char ARG_MAP_NAME_SHORT = 'm';
46
47 private static final String ARG_MAP_NAME_LONG = "map-name";
48
49 private static final char ARG_MATCH_NAME_SHORT = 'n';
50
51 private static final String ARG_MATCH_NAME_LONG = "match-name";
52
53 private static final char ARG_RESULT_DIR_SHORT = 'r';
54
55 private static final String ARG_RESULT_DIR_LONG = "result-directory";
56
57 private static final char ARG_SERVER_NAME_SHORT = 's';
58
59 private static final String ARG_SERVER_NAME_LONG = "server-name";
60
61 private static final char ARG_FRAG_LIMIT_SHORT = 'f';
62
63 private static final String ARG_FRAG_LIMIT_LONG = "frag-limit";
64
65 private static final char ARG_TIMEOUT_MINUTES_SHORT = 't';
66
67 private static final String ARG_TIMEOUT_MINUTES_LONG = "timeout-minutes";
68
69 private static final char ARG_HUMAN_LIKE_LOG_SHORT = 'h';
70
71 private static final String ARG_HUMAN_LIKE_LOG_LONG = "human-like-log";
72
73 private static final char ARG_UT2004_PORT_SHORT = 'p';
74
75 private static final String ARG_UT2004_PORT_LONG = "ut2004-port";
76
77 private static JSAP jsap;
78
79 private static boolean headerOutput = false;
80
81 private static String ut2004HomeDir;
82
83 private static String bot1Jar;
84
85 private static String bot1Name;
86
87 private static String bot2Jar;
88
89 private static String bot2Name;
90
91 private static String map;
92
93 private static String serverName;
94
95 private static String resultDir;
96
97 private static String matchName;
98
99 private static int fragLimit;
100
101 private static int timeoutMinutes;
102
103 private static JSAPResult config;
104
105 private static File ut2004HomeDirFile;
106
107 private static File bot1JarFile;
108
109 private static File bot2JarFile;
110
111 private static File mapsDirFile;
112
113 private static File mapFile;
114
115 private static File ut2004SystemDirFile;
116
117 private static File ut2004IniFile;
118
119 private static boolean humanLikeLog;
120
121 private static int ut2004Port;
122
123 private static void fail(String errorMessage) {
124 fail(errorMessage, null);
125 }
126
127 private static void fail(String errorMessage, Throwable e) {
128 header();
129 System.out.println("ERROR: " + errorMessage);
130 System.out.println();
131 if (e != null) {
132 e.printStackTrace();
133 System.out.println("");
134 }
135 System.out.println("Usage: java -jar ut2004-tournament-1v1....jar ");
136 System.out.println(" " + jsap.getUsage());
137 System.out.println();
138 System.out.println(jsap.getHelp());
139 System.out.println();
140 throw new RuntimeException("FAILURE: " + errorMessage);
141 }
142
143 private static void header() {
144 if (headerOutput) return;
145 System.out.println();
146 System.out.println("=================================");
147 System.out.println("Pogamut UT2004 1v1 Match Executor");
148 System.out.println("=================================");
149 System.out.println();
150 headerOutput = true;
151 }
152
153 private static void initJSAP() throws JSAPException {
154 jsap = new JSAP();
155
156 FlaggedOption opt1 = new FlaggedOption(ARG_UT2004_HOME_DIR_LONG)
157 .setStringParser(JSAP.STRING_PARSER)
158 .setRequired(true)
159 .setShortFlag(ARG_UT2004_HOME_DIR_SHORT)
160 .setLongFlag(ARG_UT2004_HOME_DIR_LONG);
161 opt1.setHelp("UT2004 home directory containing GameBots2004 (System/GameBots2004.u) present.");
162
163 jsap.registerParameter(opt1);
164
165 FlaggedOption opt2 = new FlaggedOption(ARG_BOT1_JAR_LONG)
166 .setStringParser(JSAP.STRING_PARSER)
167 .setRequired(true)
168 .setShortFlag(ARG_BOT1_JAR_SHORT)
169 .setLongFlag(ARG_BOT1_JAR_LONG);
170 opt2.setHelp("PATH/TO/JAR/file containing the 1st bot.");
171
172 jsap.registerParameter(opt2);
173
174 FlaggedOption opt3 = new FlaggedOption(ARG_BOT1_NAME_LONG)
175 .setStringParser(JSAP.STRING_PARSER)
176 .setRequired(true)
177 .setShortFlag(ARG_BOT1_NAME_SHORT)
178 .setLongFlag(ARG_BOT1_NAME_LONG);
179 opt3.setHelp("Name that should be given to the 1st bot.");
180
181 jsap.registerParameter(opt3);
182
183 FlaggedOption opt4 = new FlaggedOption(ARG_BOT2_JAR_LONG)
184 .setStringParser(JSAP.STRING_PARSER)
185 .setRequired(true)
186 .setShortFlag(ARG_BOT2_JAR_SHORT)
187 .setLongFlag(ARG_BOT2_JAR_LONG);
188 opt4.setHelp("PATH/TO/JAR/file containing the 2nd bot.");
189
190 jsap.registerParameter(opt4);
191
192 FlaggedOption opt5 = new FlaggedOption(ARG_BOT2_NAME_LONG)
193 .setStringParser(JSAP.STRING_PARSER)
194 .setRequired(true)
195 .setShortFlag(ARG_BOT2_NAME_SHORT)
196 .setLongFlag(ARG_BOT2_NAME_LONG);
197 opt5.setHelp("Name that should be given to the 2nd bot.");
198
199 jsap.registerParameter(opt5);
200
201 FlaggedOption opt6 = new FlaggedOption(ARG_MAP_NAME_LONG)
202 .setStringParser(JSAP.STRING_PARSER)
203 .setRequired(true)
204 .setShortFlag(ARG_MAP_NAME_SHORT)
205 .setLongFlag(ARG_MAP_NAME_LONG);
206 opt6.setHelp("Map where the game should be played (e.g. DM-1on1-Albatross).");
207
208 jsap.registerParameter(opt6);
209
210 FlaggedOption opt7 = new FlaggedOption(ARG_MATCH_NAME_LONG)
211 .setStringParser(JSAP.STRING_PARSER)
212 .setRequired(false)
213 .setShortFlag(ARG_MATCH_NAME_SHORT)
214 .setLongFlag(ARG_MATCH_NAME_LONG)
215 .setDefault("DMMatch1v1");
216 opt7.setHelp("Name of the match == output folder for the results.");
217
218 jsap.registerParameter(opt7);
219
220 FlaggedOption opt8 = new FlaggedOption(ARG_RESULT_DIR_LONG)
221 .setStringParser(JSAP.STRING_PARSER)
222 .setRequired(false)
223 .setShortFlag(ARG_RESULT_DIR_SHORT)
224 .setLongFlag(ARG_RESULT_DIR_LONG)
225 .setDefault(".");
226 opt8.setHelp("PATH/TO/directory where to output results (does not need to exist).");
227
228 jsap.registerParameter(opt8);
229
230 FlaggedOption opt9 = new FlaggedOption(ARG_SERVER_NAME_LONG)
231 .setStringParser(JSAP.STRING_PARSER)
232 .setRequired(false)
233 .setShortFlag(ARG_SERVER_NAME_SHORT)
234 .setLongFlag(ARG_SERVER_NAME_LONG)
235 .setDefault("DMMatch1v1");
236 opt9.setHelp("Server name that should be advertised via LAN.");
237
238 jsap.registerParameter(opt9);
239
240 FlaggedOption opt10 = new FlaggedOption(ARG_FRAG_LIMIT_LONG)
241 .setStringParser(JSAP.INTEGER_PARSER)
242 .setRequired(false)
243 .setShortFlag(ARG_FRAG_LIMIT_SHORT)
244 .setLongFlag(ARG_FRAG_LIMIT_LONG)
245 .setDefault("20");
246 opt10.setHelp("Frag limit for the match.");
247
248 jsap.registerParameter(opt10);
249
250 FlaggedOption opt11 = new FlaggedOption(ARG_TIMEOUT_MINUTES_LONG)
251 .setStringParser(JSAP.INTEGER_PARSER)
252 .setRequired(false)
253 .setShortFlag(ARG_TIMEOUT_MINUTES_SHORT)
254 .setLongFlag(ARG_TIMEOUT_MINUTES_LONG)
255 .setDefault("20");
256 opt11.setHelp("Match timeout in minutes.");
257
258 jsap.registerParameter(opt11);
259
260 Switch opt12 = new Switch(ARG_HUMAN_LIKE_LOG_LONG)
261 .setShortFlag(ARG_HUMAN_LIKE_LOG_SHORT)
262 .setLongFlag(ARG_HUMAN_LIKE_LOG_LONG)
263 .setDefault("false");
264 opt12.setHelp("Whether to produce log for 'HumanLike Project' analysis.");
265
266 jsap.registerParameter(opt12);
267
268 FlaggedOption opt13 = new FlaggedOption(ARG_UT2004_PORT_LONG)
269 .setStringParser(JSAP.INTEGER_PARSER)
270 .setRequired(false)
271 .setShortFlag(ARG_UT2004_PORT_SHORT)
272 .setLongFlag(ARG_UT2004_PORT_LONG)
273 .setDefault("7777");
274 opt13.setHelp("UT2004 port for the dedicated server (1-32000).");
275
276 jsap.registerParameter(opt13);
277 }
278
279 private static void readConfig(String[] args) {
280 System.out.println("Parsing command arguments.");
281
282 try {
283 config = jsap.parse(args);
284 } catch (Exception e) {
285 fail(e.getMessage());
286 System.out.println("");
287 e.printStackTrace();
288 throw new RuntimeException("FAILURE!");
289 }
290
291 if (!config.success()) {
292 String error = "Invalid arguments specified.";
293 Iterator errorIter = config.getErrorMessageIterator();
294 if (!errorIter.hasNext()) {
295 error += "\n-- No details given.";
296 } else {
297 while (errorIter.hasNext()) {
298 error += "\n-- " + errorIter.next();
299 }
300 }
301 fail(error);
302 }
303
304 ut2004HomeDir = config.getString(ARG_UT2004_HOME_DIR_LONG);
305 bot1Jar = config.getString(ARG_BOT1_JAR_LONG);
306 bot1Name = config.getString(ARG_BOT1_NAME_LONG);
307 bot2Jar = config.getString(ARG_BOT2_JAR_LONG);
308 bot2Name = config.getString(ARG_BOT2_NAME_LONG);
309 map = config.getString(ARG_MAP_NAME_LONG);
310 serverName = config.getString(ARG_SERVER_NAME_LONG);
311 resultDir = config.getString(ARG_RESULT_DIR_LONG);
312 matchName = config.getString(ARG_MATCH_NAME_LONG);
313 fragLimit = config.getInt(ARG_FRAG_LIMIT_LONG);
314 timeoutMinutes = config.getInt(ARG_TIMEOUT_MINUTES_LONG);
315 humanLikeLog = config.getBoolean(ARG_HUMAN_LIKE_LOG_LONG);
316 ut2004Port = config.getInt(ARG_UT2004_PORT_LONG);
317 }
318
319 private static void sanityChecks() {
320 System.out.println("Sanity checks...");
321
322 ut2004HomeDirFile = new File(ut2004HomeDir);
323 if (!ut2004HomeDirFile.exists() || !ut2004HomeDirFile.isDirectory()) {
324 fail("UT2004 directory was not found at '" + ut2004HomeDirFile.getAbsolutePath() + "', path resolved from configuration read as '" + ut2004HomeDir + "'.");
325 }
326 System.out.println("-- UT2004 directory found at '" + ut2004HomeDirFile.getAbsolutePath() + "'");
327
328 ut2004SystemDirFile = new File(ut2004HomeDirFile, "System");
329 if (!ut2004SystemDirFile.exists() || !ut2004SystemDirFile.isDirectory()) {
330 fail("UT2004/System directory was not found at '" + ut2004SystemDirFile.getAbsolutePath() + "', invalid UT2004 installation.");
331 }
332 System.out.println("-- UT2004/System directory found at '" + ut2004SystemDirFile.getAbsolutePath() + "'");
333
334 ut2004IniFile = new File(ut2004SystemDirFile, "UT2004.ini");
335 if (!ut2004IniFile.exists() || !ut2004IniFile.isFile()) {
336 fail("UT2004/System/UT2004.ini file was not found at '" + ut2004IniFile.getAbsolutePath() + "', invalid UT2004 installation.");
337 }
338 System.out.println("-- UT2004/System/UT2004.ini file found at '" + ut2004IniFile.getAbsolutePath() + "'");
339
340 bot1JarFile = new File(bot1Jar);
341 if (!bot1JarFile.exists() || !bot1JarFile.isFile()) {
342 fail("Bot1 jar file was not found at '"+ bot1JarFile.getAbsolutePath() + "', path resolved from configuration read as '" + bot1Jar + "'.");
343 }
344 System.out.println("-- Bot1 jar file found at '" + bot1JarFile.getAbsolutePath() + "'");
345
346 bot2JarFile = new File(bot2Jar);
347 if (!bot2JarFile.exists() || !bot2JarFile.isFile()) {
348 fail("Bot2 jar file was not found at '"+ bot2JarFile.getAbsolutePath() + "', path resolved from configuration read as '" + bot2Jar + "'.");
349 }
350 System.out.println("-- Bot2 jar file found at '" + bot2JarFile.getAbsolutePath() + "'");
351
352 if (bot1Name == null || bot1Name.isEmpty()) {
353 fail("Bot1 invalid name '" + bot1Name +"' specified.");
354 }
355 System.out.println("-- Bot1 name set as '" + bot1Name + "'");
356
357 if (bot2Name == null || bot2Name.isEmpty()) {
358 fail("Bot2 invalid name '" + bot1Name +"' specified.");
359 }
360 System.out.println("-- Bot2 name set as '" + bot2Name + "'");
361
362 if (bot1Name.equalsIgnoreCase(bot2Name)) {
363 fail("Bot1 has the name '" + bot1Name + "', which is the same (case-insensitive) as Bot2 name '" + bot2Name + "'.");
364 }
365 System.out.println("-- Bot1+2 names ok");
366
367 mapsDirFile = new File(ut2004HomeDirFile, "Maps");
368 if (!mapsDirFile.exists() || !mapsDirFile.isDirectory()) {
369 fail("UT2004/Maps directory was not found at '" + mapsDirFile.getAbsolutePath() + "', invalid UT2004 installation.");
370 }
371 System.out.println("-- UT2004/Maps directory found at '" + mapsDirFile.getAbsolutePath() + "'");
372
373 mapFile = new File(mapsDirFile, map + ".ut2");
374 if (!mapFile.exists() || !mapFile.isFile()) {
375 fail("Specified map '" + map + "' was not found within UT2004/Maps dir at '" + mapFile.getAbsoluteFile() + "', could not execute the match.");
376 }
377 System.out.println("-- Map '" + map + "' found at '" + mapFile.getAbsolutePath() + "'");
378
379 if (matchName == null || matchName.isEmpty()) {
380 fail("Invalid match name '" + matchName + "' specified.");
381 }
382 System.out.println("-- Match name set as '" + matchName + "'");
383
384 if (serverName == null || serverName.isEmpty()) {
385 fail("Invalid server name '" + serverName + "' specified.");
386 }
387 System.out.println("-- Server name set as '" + serverName + "'");
388
389 if (fragLimit < 1) {
390 fail("Invalid frag limit '" + fragLimit +"' specified, must be >= 1.");
391 }
392 System.out.println("-- Frag limit set as '" + fragLimit + "'");
393
394 if (timeoutMinutes < 1) {
395 fail("Invalid time limit '" + timeoutMinutes +"' specified, must be >= 1.");
396 }
397 System.out.println("-- Timeout set as '" + timeoutMinutes + "' minutes.");
398
399 if (ut2004Port < 1 || ut2004Port > 32000) {
400 fail("Invalid UT2004 port specified '" + ut2004Port + "', must be 1 <= port <= 32000.");
401 }
402 System.out.println("-- UT2004 port set as '" + ut2004Port + "'");
403
404 System.out.println("Sanity checks OK!");
405 }
406
407 private static void setUT2004Ini() {
408 Date date = new Date(System.currentTimeMillis());
409 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
410 File ut2004IniBackup = new File(ut2004SystemDirFile, "UT2004.ini." + sdf.format(date) + ".bak");
411
412 System.out.println("Backing up '" + ut2004IniFile.getAbsolutePath() + "' into '" + ut2004IniBackup.getAbsolutePath() + "' ...");
413 try {
414 FileUtils.copyFile(ut2004IniFile, ut2004IniBackup);
415 } catch (IOException e) {
416 throw new RuntimeException("Failed to backup UT2004.ini", e);
417 }
418
419 System.out.println("Reading '" + ut2004IniFile.getAbsolutePath() + "' ...");
420 List<String> lines;
421 try {
422 lines = FileUtils.readLines(ut2004IniFile);
423 } catch (IOException e) {
424 throw new RuntimeException("Failed to read UT2004.ini", e);
425 }
426 System.out.println("-- " + lines.size() + " lines read.");
427
428 System.out.println("Searching for UT2004 Port and ServerName ...");
429
430
431
432
433 int state = 0;
434
435 Pattern patternSection = Pattern.compile("^\\s*\\[\\s*[^]]*\\s*\\]\\s*$");
436 Pattern patternSectionURL = Pattern.compile("^\\s*\\[\\s*" + UT2004Ini.Section_URL + "\\s*\\]\\s*$");
437 Pattern patternSectionEngineGameReplicationInfo = Pattern.compile("^\\s*\\[\\s*" + UT2004Ini.Section_Engine_GameReplicationInfo + "\\s*\\]\\s*$");
438 Pattern patternPort = Pattern.compile("^\\s*" + UT2004Ini.Key_Port + "\\s*=\\s*.*$");
439 Pattern patternServerName = Pattern.compile("^\\s*" + UT2004Ini.Key_ServerName + "\\s*=\\s*.*$");
440 Pattern patternShortName = Pattern.compile("^\\s*" + UT2004Ini.Key_ShortName + "\\s*=\\s*.*$");
441
442 boolean portFound = false;
443 boolean nameFound = false;
444 boolean shortNameFound = false;
445
446 for (int lineNum = 0;
447
448
449 lineNum <= lines.size()
450
451
452 && (!(portFound && nameFound && shortNameFound));
453 ++lineNum
454 ) {
455
456
457
458 String line = lineNum < lines.size() ? lines.get(lineNum).trim() : null;
459
460
461 if (lineNum < lines.size() && line == null) {
462 continue;
463 }
464
465 if (lineNum == lines.size() || patternSection.matcher(line).matches()) {
466 switch (state) {
467 case 0:
468 break;
469 case 1:
470 if (!portFound) {
471 lines.add(lineNum, UT2004Ini.Key_Port + "=" + ut2004Port);
472 portFound = true;
473 ++lineNum;
474 }
475 break;
476 case 2:
477 if (!nameFound) {
478 lines.add(lineNum, UT2004Ini.Key_ServerName + "=" + serverName);
479 nameFound = true;
480 ++lineNum;
481 }
482 if (!shortNameFound) {
483 lines.add(lineNum, UT2004Ini.Key_ShortName + "=" + serverName);
484 shortNameFound = true;
485 ++lineNum;
486 }
487 break;
488 }
489 if (lineNum == lines.size()) {
490 break;
491 }
492 if (line == null) {
493 continue;
494 }
495 if (patternSectionURL.matcher(line).matches()) {
496 state = 1;
497 } else
498 if (patternSectionEngineGameReplicationInfo.matcher(line).matches()) {
499 state = 2;
500 } else {
501 state = 0;
502 }
503 continue;
504 }
505
506 switch (state) {
507 case 0:
508 continue;
509 case 1:
510 if (!portFound && patternPort.matcher(line).matches()) {
511 lines.set(lineNum, UT2004Ini.Key_Port + "=" + ut2004Port);
512 portFound = true;
513 }
514 continue;
515 case 2:
516 if (!nameFound && patternServerName.matcher(line).matches()) {
517 lines.set(lineNum, UT2004Ini.Key_ServerName + "=" + serverName);
518 nameFound = true;
519 } else
520 if (!shortNameFound && patternShortName.matcher(line).matches()) {
521 lines.set(lineNum, UT2004Ini.Key_ShortName + "=" + serverName);
522 shortNameFound = true;
523 }
524 continue;
525 default:
526 continue;
527 }
528 }
529
530 if (!portFound) {
531 throw new RuntimeException("Failed to set UT2004 port!");
532 }
533 if (!nameFound) {
534 throw new RuntimeException("Failed to set UT2004 ServerName!");
535 }
536
537 System.out.println("UT2004 Port and ServerName set.");
538 System.out.println("Rewriting '" + ut2004IniFile.getAbsolutePath() + "' ...");
539 try {
540 FileUtils.writeLines(ut2004IniFile, lines);
541 } catch (IOException e) {
542 throw new RuntimeException("Failed to write UT2004.ini", e);
543 }
544
545 System.out.println("UT2004 Port and ServerName set.");
546 }
547
548 private static void executeMatch() {
549 UT2004DeathMatch1v1 match = new UT2004DeathMatch1v1(ut2004HomeDir, map, bot1Name, bot1Jar, bot2Name, bot2Jar);
550 match.setOutputDir(resultDir);
551 match.setMatchName(matchName);
552 match.setFragLimit(fragLimit);
553 match.setTimeLimitInMinutes(timeoutMinutes);
554 match.setHumanLikeLogEnabled(humanLikeLog);
555
556 match.getLog().setLevel(Level.WARNING);
557 match.getLog().addConsoleHandler();
558
559 System.out.println("EXECUTING MATCH!");
560
561 match.run();
562 }
563
564 public static void main(String[] args) throws JSAPException {
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592 initJSAP();
593
594 header();
595
596 readConfig(args);
597
598 sanityChecks();
599
600 setUT2004Ini();
601
602 executeMatch();
603 }
604
605 }