View Javadoc

1   package cz.cuni.amis.pogamut.ut2004.tournament.match;
2   
3   import java.io.File;
4   import java.util.ArrayList;
5   import java.util.Collections;
6   import java.util.Comparator;
7   import java.util.HashMap;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Map.Entry;
11  
12  import cz.cuni.amis.pogamut.base.utils.Pogamut;
13  import cz.cuni.amis.pogamut.ut2004.tournament.GameBots2004Ini;
14  import cz.cuni.amis.pogamut.ut2004.tournament.utils.UT2004TournamentProperty;
15  import cz.cuni.amis.pogamut.ut2004.utils.UCCWrapperConf;
16  import cz.cuni.amis.utils.Const;
17  import cz.cuni.amis.utils.NullCheck;
18  import cz.cuni.amis.utils.SafeEquals;
19  import cz.cuni.amis.utils.exception.PogamutException;
20  import cz.cuni.amis.utils.token.IToken;
21  import cz.cuni.amis.utils.token.Tokens;
22  
23  /**
24   * Base configuration of the UT2004 match, you have to specify:
25   * <ol>
26   * <li>Match id</li>
27   * <li>Concrete GB2004Ini file to be used via {@link GameBots2004Ini}</li>
28   * <li>Concrete UCC to run via {@link UCCWrapperConf}</li>
29   * <li>Concrete list of bots to be used via {@link UT2004BotConfig}</li>
30   * </ol>
31   * Guess what... everything is preinitialized... but you should at least adjust uccConf.
32   * 
33   * @author Jimmy
34   */
35  public class UT2004MatchConfig {
36  
37  	protected File outputDirectory = new File("./results/matches");
38  	
39  	protected IToken matchId = Tokens.get("DMMatch");
40  	
41  	protected UCCWrapperConf uccConf = new UCCWrapperConf();
42  	
43  	protected GameBots2004Ini gb2004Ini = new GameBots2004Ini();
44  	
45  	protected Map<IToken, UT2004BotConfig> bots = new HashMap<IToken, UT2004BotConfig>();
46  	
47  	protected Map<IToken, UT2004NativeBotConfig> nativeBots = new HashMap<IToken, UT2004NativeBotConfig>();
48  
49  	private boolean humanLikeLogEnabled;
50  	
51  	public UT2004MatchConfig() {
52  		String unrealHome = Pogamut.getPlatform().getProperty(UT2004TournamentProperty.UT2004_DIR.getKey());
53  		if (unrealHome != null) {
54  			uccConf.setUnrealHome(unrealHome);
55  		}
56  	}
57  	
58  	/**
59  	 * Copy-constructor.
60  	 * @param orig
61  	 */
62  	public UT2004MatchConfig(UT2004MatchConfig orig) {
63  		this.matchId = orig.matchId;
64  		this.uccConf = new UCCWrapperConf(orig.getUccConf());
65  		this.gb2004Ini = new GameBots2004Ini(orig.getGb2004Ini());
66  		for (Entry<IToken, UT2004BotConfig> bot : orig.getBots().entrySet()) {
67  			this.bots.put(bot.getKey(), new UT2004BotConfig(bot.getValue()));
68  		}
69  		for (Entry<IToken, UT2004NativeBotConfig> bot : orig.getNativeBots().entrySet()) {
70  			this.nativeBots.put(bot.getKey(), new UT2004NativeBotConfig(bot.getValue()));
71  		}
72  	}
73  		
74  	@Override
75  	public int hashCode() {
76  		return matchId == null ? super.hashCode() : matchId.hashCode();
77  	}
78  	
79  	@Override
80  	public boolean equals(Object obj) {
81  		if (this == obj) return true;
82  		if (obj == null) return false;
83  		if (!(obj instanceof UT2004MatchConfig)) return false;
84  		return SafeEquals.equals(matchId, ((UT2004MatchConfig)obj).getMatchId());
85  	}
86  
87  	public File getOutputDirectory() {
88  		return outputDirectory;
89  	}
90  
91  	public IToken getMatchId() {
92  		return matchId;
93  	}
94  
95  	public void setMatchId(IToken matchId) {
96  		this.matchId = matchId;
97  	}
98  	
99  	public void setMatchId(String matchId) {
100 		this.matchId = Tokens.get(matchId);
101 	}
102 
103 	public UCCWrapperConf getUccConf() {
104 		return uccConf;
105 	}
106 
107 	/**
108 	 * Preinitialized automatically.
109 	 * @return
110 	 */
111 	public GameBots2004Ini getGb2004Ini() {
112 		return gb2004Ini;
113 	}
114 	
115 	public Map<IToken, UT2004BotConfig> getBots() {
116 		return bots;
117 	}
118 	
119 	public Map<IToken, UT2004NativeBotConfig> getNativeBots() {
120 		return nativeBots;
121 	}
122 	
123 	/**
124 	 * Ids are sorted: 1) custom bots, 2) native bots.
125 	 * <p><p>
126 	 * WARNING: O(n*log(n)) complexity!
127 	 * 
128 	 * @return
129 	 */
130 	public List<IToken> getAllBotIds() {
131 		List<IToken> bots = new ArrayList<IToken>(getBots().keySet());
132 		List<IToken> nativeBots = new ArrayList<IToken>(getNativeBots().keySet());
133 		Collections.sort(bots, new Comparator<IToken>() {
134 			@Override
135 			public int compare(IToken o1, IToken o2) {
136 				return o1.getToken().compareTo(o2.getToken());
137 			}				
138 		});
139 		Collections.sort(nativeBots, new Comparator<IToken>() {
140 			@Override
141 			public int compare(IToken o1, IToken o2) {
142 				return o1.getToken().compareTo(o2.getToken());
143 			}				
144 		});		
145 		List<IToken> botIds = new ArrayList<IToken>(bots);
146 		botIds.addAll(nativeBots);
147 		return botIds;
148 	}
149 	
150 	public UT2004MatchConfig setOutputDirectory(File outputDirectory) {
151 		this.outputDirectory = outputDirectory;
152 		return this;
153 	}
154 	
155 	public UT2004MatchConfig setUccConf(UCCWrapperConf uccConf) {
156 		this.uccConf = uccConf;
157 		return this;
158 	}
159 	
160 	/**
161 	 * No need to call, preinitialized automatically.
162 	 * @return
163 	 */
164 	public UT2004MatchConfig setGb2004Ini(GameBots2004Ini gb2004Ini) {
165 		this.gb2004Ini = gb2004Ini;
166 		return this;
167 	}
168 
169 	public UT2004MatchConfig setBots(Map<IToken, UT2004BotConfig> bots) {
170 		this.bots = bots;
171 		return this;
172 	}
173 	
174 	public UT2004MatchConfig setNativeBots(Map<IToken, UT2004NativeBotConfig> nativeBots) {
175 		this.nativeBots = nativeBots;
176 		return this;
177 	}
178 	
179 	public UT2004MatchConfig clearBots() {
180 		this.bots.clear();
181 		return this;
182 	}
183 	
184 	public UT2004MatchConfig clearNativeBots() {
185 		this.nativeBots.clear();
186 		return this;
187 	}
188 	
189 	/**
190 	 * Adds NEW bot configuration into the object, checks whether there is no BotId clash (if so, throws an exception).
191 	 * @param bots
192 	 * @return
193 	 */
194 	public UT2004MatchConfig addBot(UT2004BotConfig... bots) {
195 		if (bots == null) return this;
196 		for (UT2004BotConfig bot : bots) {
197 			NullCheck.check(bot.getBotId(), "bot.getBotId()");
198 			if (this.bots.containsKey(bot.getBotId())) {
199 				throw new PogamutException("Can't add another bot under the id " + bot.getBotId().getToken() + ", there is already an existing custom bot configuration under this ID. If you need to override it, use setBot().", this);
200 			}
201 			if (this.nativeBots.containsKey(bot.getBotId())) {
202 				throw new PogamutException("Can't add another bot under the id " + bot.getBotId().getToken() + ", there is already an existing native bot configuration under this ID. If you need to override it, use setBot().", this);
203 			}
204 			this.bots.put(bot.getBotId(), bot);
205 		}
206 		return this;
207 	}
208 	
209 	/**
210 	 * Sets bot configuration into the object, does not checks whether there is BotId clash.
211 	 * 
212 	 * @param bots
213 	 * @return
214 	 */
215 	public UT2004MatchConfig setBot(UT2004BotConfig... bots) {
216 		if (bots == null) return this;
217 		for (UT2004BotConfig bot : bots) {
218 			NullCheck.check(bot.getBotId(), "bot.getBotId()");
219 			if (this.nativeBots.containsKey(bot.getBotId())) {
220 				throw new PogamutException("Can't add another bot under the id " + bot.getBotId().getToken() + ", there is already an existing native bot configuration under this ID. If you need to override it, use setBot().", this);
221 			}
222 			this.bots.put(bot.getBotId(), bot);
223 		}
224 		return this;
225 	}
226 	
227 	/**
228 	 * Adds NEW native bot configuration into the object, checks whether there is no BotId clash (if so, throws an exception).
229 	 * @param bots
230 	 * @return
231 	 */
232 	public UT2004MatchConfig addNativeBot(UT2004NativeBotConfig... bots) {
233 		if (bots == null) return this;
234 		for (UT2004NativeBotConfig bot : bots) {
235 			NullCheck.check(bot.getBotId(), "bot.getBotId()");
236 			if (this.bots.containsKey(bot.getBotId())) {
237 				throw new PogamutException("Can't add another bot under the id " + bot.getBotId().getToken() + ", there is already an existing custom bot configuration under this ID. If you need to override it, use setBot().", this);
238 			}
239 			if (this.nativeBots.containsKey(bot.getBotId())) {
240 				throw new PogamutException("Can't add another bot under the id " + bot.getBotId().getToken() + ", there is already an existing native bot configuration under this ID. If you need to override it, use setNativeBot().", this);
241 			}
242 			this.nativeBots.put(bot.getBotId(), bot);
243 		}
244 		return this;
245 	}
246 	
247 	/**
248 	 * Sets native bot configuration into the object, does not checks whether there is BotId clash.
249 	 * @param bots
250 	 * @return
251 	 */
252 	public UT2004MatchConfig setNativeBot(UT2004NativeBotConfig... bots) {
253 		if (bots == null) return this;
254 		for (UT2004NativeBotConfig bot : bots) {
255 			NullCheck.check(bot.getBotId(), "bot.getBotId()");
256 			if (this.bots.containsKey(bot.getBotId())) {
257 				throw new PogamutException("Can't add another bot under the id " + bot.getBotId().getToken() + ", there is already an existing custom bot configuration under this ID. If you need to override it, use setBot().", this);
258 			}
259 			this.nativeBots.put(bot.getBotId(), bot);
260 		}
261 		return this;
262 	}
263 	
264 	public boolean isNativeBot(IToken botId) {
265 		return nativeBots.containsKey(botId);
266 	}
267 	
268 	public boolean isHumanLikeLogEnabled() {
269 		return humanLikeLogEnabled;
270 	}
271 	
272 	public UT2004MatchConfig setHumanLikeLogEnabled(boolean humanLikeLog) {
273 		this.humanLikeLogEnabled = humanLikeLog;
274 		return this;
275 	}
276 	
277 	//
278 	// VALIDATION
279 	//
280 	
281 	protected final StringBuffer validationBuffer = new StringBuffer();
282 	
283 	protected boolean validationError = false;
284 	
285 	/**
286 	 * Performs validation of the match configuration:
287 	 * <ol>
288 	 * <li>Checks whether the match id is non-null.</li>
289 	 * <li>Checks whether all (custom+native) bots have path-to-jar configured + the file exists.</li>
290 	 * <li>{@link UccWrapper} is not null and its directory exists.</li>
291 	 * <li>Presence of GameBots2004.u as a new game type inside UT2004.</li> 
292 	 * <li>{@link GameBots2004Ini} is not null.</li>
293 	 * <li>Whether there are at least 2 bots defined for the match.</li>
294 	 * </ol>
295 	 * <p><p>
296 	 * Override to provide/add custom validation (might require copy-paste of the code from this method). 
297 	 */
298 	protected void validateInner() {
299 		if (matchId == null) {
300 			validationError = true;
301 			validationBuffer.append(Const.NEW_LINE);
302 			validationBuffer.append("Match ID is NULL");
303 		}
304 		for (UT2004BotConfig config : bots.values()) {
305 			if (config.getBotId() == null) {
306 				validationError = true;
307 				validationBuffer.append(Const.NEW_LINE);
308 				validationBuffer.append("One of custom bots has NULL bot-id.");
309 			}
310 			if (config.getPathToBotJar() == null) {
311 				validationError = true;
312 				validationBuffer.append(Const.NEW_LINE);
313 				validationBuffer.append(config.getBotId() == null ? "One of custom bots" : "Bot " + config.getBotId().getToken() + " has NULL path-to-jar.");
314 			} else {
315 				if (!config.isBotJarExist()) {
316 					validationError = true;
317 					validationBuffer.append(Const.NEW_LINE);
318 					validationBuffer.append(config.getBotId() == null ? "One of custom bots" : "Bot " + config.getBotId().getToken() + " has path-to-jar pointing to non-existing file " + config.getJarFile().getAbsolutePath());
319 				}
320 			}
321 		}
322 		for (UT2004NativeBotConfig config : nativeBots.values()) {
323 			if (config.getBotId() == null) {
324 				validationError = true;
325 				validationBuffer.append(Const.NEW_LINE);
326 				validationBuffer.append("One of native bots has NULL bot-id.");
327 			}			
328 		}
329 		if (gb2004Ini == null) {
330 			validationError = true;
331 			validationBuffer.append(Const.NEW_LINE);
332 			validationBuffer.append("GameBots2004Ini is NULL.");			
333 		}
334 		if (uccConf == null) {
335 			validationError = true;
336 			validationBuffer.append(Const.NEW_LINE);
337 			validationBuffer.append("UccWrapper is NULL.");
338 		} else {
339 			if (uccConf.getUnrealHome() == null) {
340 				validationError = true;
341 				validationBuffer.append(Const.NEW_LINE);
342 				validationBuffer.append("UccWrapper does not have UnrealHome set, is NULL.");
343 			} else {
344 				File uccHome = new File(uccConf.getUnrealHome());
345 				if (!uccHome.exists() || !uccHome.isDirectory()) {
346 					validationError = true;
347 					validationBuffer.append(Const.NEW_LINE);
348 					validationBuffer.append("UccWrapper.UnrealHome does not point to directory, used path: " + uccHome.getAbsolutePath());
349 				} else {
350 					File uccHome2 = new File(uccConf.getUnrealHome() + File.separator + "System");
351 					if (!uccHome2.exists() || !uccHome2.isDirectory()) {
352 						validationError = true;
353 						validationBuffer.append(Const.NEW_LINE);
354 						validationBuffer.append("UccWrapper.UnrealHome" + File.separator + "System does not point to directory, used path: " + uccHome2.getAbsolutePath());
355 					} else { 
356 						File gb = new File(uccConf.getUnrealHome() + File.separator + "System" + File.separator + "GameBots2004.u");
357 						if (!gb.exists() || !gb.isFile()) {
358 							validationError = true;
359 							validationBuffer.append(Const.NEW_LINE);
360 							validationBuffer.append("GameBots2004 was not installed into specified UT2004, the file GameBots2004.u was not found at: " + gb.getAbsolutePath());
361 						}
362 					}
363 				}
364 			}
365 		}
366 		if (bots.size() + nativeBots.size() < 2) {
367 			validationError = true;
368 			validationBuffer.append(Const.NEW_LINE);
369 			validationBuffer.append("There are not enough bot specified for the match. Custom bots: " + bots.size() + ", native bots: " + nativeBots.size() + ". There must be at least 2 bots to perform the match!");
370 		}
371 	}
372 	
373 	/**
374 	 * Checks the contents, whether everything is set-up correctly, if not, raises an exception with explanation.
375 	 * <p><p>
376 	 * Recalls {@link UT2004MatchConfig#validateInner()} that should fill {@link UT2004MatchConfig#validationBuffer} with
377 	 * messages and if there is a fatal error in the configuration, it should set true to {@link UT2004MatchConfig#validationError}.
378 	 */
379 	public final void validate() {
380 		synchronized(validationBuffer) {
381 			validationError = false;
382 			if (validationBuffer.length() > 0) {
383 				validationBuffer.delete(0, validationBuffer.length());
384 			}
385 			validateInner();
386 			if (validationError) {
387 				throw new PogamutException(this + " validation error!" + validationBuffer.toString(), this);
388 			}
389 		}
390 	}
391 	
392 }