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