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