View Javadoc

1   /**
2    * BaseUnrealEnvironment, an implementation of the environment interface standard that 
3    * facilitates the connection between GOAL and the UT2004 engine. 
4    * 
5    * Copyright (C) 2012 BaseUnrealEnvironment authors.
6    * 
7    * This program is free software: you can redistribute it and/or modify it under
8    * the terms of the GNU General Public License as published by the Free Software
9    * Foundation, either version 3 of the License, or (at your option) any later
10   * version.
11   * 
12   * This program is distributed in the hope that it will be useful, but WITHOUT
13   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14   * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15   * details.
16   * 
17   * You should have received a copy of the GNU General Public License along with
18   * this program. If not, see <http://www.gnu.org/licenses/>.
19   */
20  package nl.tudelft.goal.unreal.environment;
21  
22  import java.util.HashMap;
23  import java.util.Map;
24  
25  import nl.tudelft.goal.unreal.messages.BotParameters;
26  import nl.tudelft.goal.unreal.messages.Configuration;
27  import nl.tudelft.goal.unreal.messages.MapOfParameters;
28  import nl.tudelft.goal.unreal.translators.AgentIdTranslator;
29  import nl.tudelft.goal.unreal.translators.BotParametersKeyTranslator;
30  import nl.tudelft.goal.unreal.translators.BotParametersListTranslator;
31  import nl.tudelft.goal.unreal.translators.BotParametersTranslator;
32  import nl.tudelft.goal.unreal.translators.ConfigurationKeyTranslator;
33  import nl.tudelft.goal.unreal.translators.ConfigurationTranslator;
34  import nl.tudelft.goal.unreal.translators.LevelTranslator;
35  import nl.tudelft.goal.unreal.translators.LocationTranslator;
36  import nl.tudelft.goal.unreal.translators.ParameterMapTranslator;
37  import nl.tudelft.goal.unreal.translators.RotationTranslator;
38  import nl.tudelft.goal.unreal.translators.SkinTranslator;
39  import nl.tudelft.goal.unreal.translators.TeamTranslator;
40  import nl.tudelft.goal.unreal.translators.URITranslator;
41  import nl.tudelft.goal.unreal.translators.VelocityTranslator;
42  import nl.tudelft.goal.unreal.util.EnvironmentUtil;
43  import nl.tudelft.goal.unreal.util.vecmathcheck.VecmathCheck;
44  import cz.cuni.amis.pogamut.base.agent.IAgent;
45  import cz.cuni.amis.pogamut.base.agent.IAgentId;
46  import cz.cuni.amis.pogamut.base.agent.exceptions.AgentException;
47  import cz.cuni.amis.pogamut.base.agent.impl.AgentId;
48  import cz.cuni.amis.pogamut.base.agent.state.level0.IAgentState;
49  import cz.cuni.amis.pogamut.base.agent.state.level1.IAgentStateDown;
50  import cz.cuni.amis.pogamut.base.communication.command.IAct;
51  import cz.cuni.amis.pogamut.base.component.IComponent;
52  import cz.cuni.amis.pogamut.base.utils.Pogamut;
53  import cz.cuni.amis.pogamut.base.utils.logging.AgentLogger;
54  import cz.cuni.amis.pogamut.base.utils.logging.IAgentLogger;
55  import cz.cuni.amis.pogamut.base.utils.logging.LogCategory;
56  import cz.cuni.amis.pogamut.base3d.worldview.IVisionWorldView;
57  import cz.cuni.amis.pogamut.ut2004.agent.params.UT2004AgentParameters;
58  import cz.cuni.amis.pogamut.ut2004.bot.IUT2004Bot;
59  import cz.cuni.amis.pogamut.ut2004.bot.IUT2004BotController;
60  import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
61  import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004BotController;
62  import cz.cuni.amis.pogamut.ut2004.bot.params.UT2004BotParameters;
63  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.Pause;
64  import cz.cuni.amis.pogamut.ut2004.factory.guice.remoteagent.UT2004ServerFactory;
65  import cz.cuni.amis.pogamut.ut2004.factory.guice.remoteagent.UT2004ServerModule;
66  import cz.cuni.amis.pogamut.ut2004.server.IUT2004Server;
67  import cz.cuni.amis.pogamut.ut2004.utils.UT2004BotRunner;
68  import cz.cuni.amis.pogamut.ut2004.utils.UT2004ServerRunner;
69  import cz.cuni.amis.pogamut.ut2004.utils.UTBotRunner;
70  import cz.cuni.amis.utils.exception.PogamutException;
71  import cz.cuni.amis.utils.flag.FlagListener;
72  import eis.eis2java.environment.AbstractEnvironment;
73  import eis.eis2java.exception.TranslationException;
74  import eis.eis2java.handlers.ActionHandler;
75  import eis.eis2java.handlers.DefaultActionHandler;
76  import eis.eis2java.handlers.DefaultPerceptHandler;
77  import eis.eis2java.handlers.PerceptHandler;
78  import eis.eis2java.translation.Translator;
79  import eis.exceptions.EntityException;
80  import eis.exceptions.ManagementException;
81  import eis.exceptions.RelationException;
82  import eis.iilang.Action;
83  import eis.iilang.EnvironmentState;
84  import eis.iilang.Parameter;
85  
86  @SuppressWarnings("rawtypes")
87  public abstract class AbstractUnrealEnvironment extends
88  		SimpleTransitioningEnvironment implements IComponent {
89  
90  	/**
91  	 * Generated serialVersionUID.
92  	 */
93  	private static final long serialVersionUID = 6786623950045095814L;
94  	protected final IAgentId id;
95  
96  	// Manager of logs.
97  	protected final IAgentLogger environmentLogger;
98  	// Actual logger
99  	protected final LogCategory log;
100 
101 	// Agent state listeners
102 	private final Map<String, AgentDownListener> agentDownListeners;
103 
104 	// Configuration based on init parameters
105 	protected Configuration configuration;
106 
107 	// Connection to the ut Server. Can be used to pause/resume the game.
108 	private IUT2004Server utServer;
109 
110 	/**
111 	 * Constructs the Unreal Environment. The environment won't be ready until
112 	 * until is has has been initialized.
113 	 * 
114 	 */
115 	public AbstractUnrealEnvironment() {
116 		id = new AgentId(getName());
117 		agentDownListeners = new HashMap<String, AgentDownListener>();
118 		environmentLogger = new AgentLogger(id);
119 		environmentLogger.addDefaultConsoleHandler();
120 		log = environmentLogger.getCategory(this);
121 		log.info("Environment has been created.");
122 		log.addConsoleHandler();
123 
124 		// Register own translators.
125 		Translator translator = Translator.getInstance();
126 		translator.registerParameter2JavaTranslator(new AgentIdTranslator());
127 
128 		translator
129 				.registerParameter2JavaTranslator(new BotParametersKeyTranslator());
130 		translator
131 				.registerParameter2JavaTranslator(new BotParametersListTranslator());
132 		translator
133 				.registerParameter2JavaTranslator(new BotParametersTranslator());
134 
135 		translator
136 				.registerParameter2JavaTranslator(new ConfigurationKeyTranslator());
137 		translator
138 				.registerParameter2JavaTranslator(new ConfigurationTranslator());
139 
140 		translator.registerParameter2JavaTranslator(new LevelTranslator());
141 		translator.registerParameter2JavaTranslator(new LocationTranslator());
142 
143 		translator
144 				.registerParameter2JavaTranslator(new ParameterMapTranslator());
145 
146 		translator.registerParameter2JavaTranslator(new RotationTranslator());
147 
148 		translator.registerParameter2JavaTranslator(new SkinTranslator());
149 		translator.registerParameter2JavaTranslator(new TeamTranslator());
150 
151 		translator.registerParameter2JavaTranslator(new URITranslator());
152 
153 		translator.registerParameter2JavaTranslator(new VelocityTranslator());
154 
155 		// Register translators required by the bot controller.
156 		registerTranslators();
157 	}
158 
159 	protected abstract void registerTranslators();
160 
161 	/**
162 	 * Provides an unique identifier for this component for use by loggers.
163 	 */
164 
165 	@Override
166 	public IAgentId getComponentId() {
167 		return id;
168 	}
169 
170 	/**
171 	 * Returns a string representation of the environment based on ID of the
172 	 * environment.
173 	 * 
174 	 * @return a representation of the environment.
175 	 */
176 	@Override
177 	public String toString() {
178 		return EnvironmentUtil.simplefyID(getComponentId());
179 	}
180 
181 	public String getName() {
182 		return "Unreal Environment for EIS" + requiredVersion();
183 	}
184 
185 	protected synchronized void initializeEnvironment(
186 			Map<String, Parameter> parameters) throws ManagementException {
187 
188 		// Check if we are loading correct version of vecmath. See ticket #2494.
189 		if (!VecmathCheck.check()) {
190 			throw new ManagementException(VecmathCheck.getErrorMessage());
191 		}
192 
193 		// Translate configuration
194 		try {
195 			// Wrapper pending fix to environment init.
196 			Parameter parameterMap = new MapOfParameters(parameters);
197 			configuration = Translator.getInstance().translate2Java(
198 					parameterMap, Configuration.class);
199 			configuration.assignDefaults(Configuration.getDefaults());
200 		} catch (TranslationException e) {
201 			throw new ManagementException("Invalid parameters", e);
202 		}
203 
204 		// Set log level for environment
205 		log.setLevel(configuration.getLogLevel());
206 	}
207 
208 	protected synchronized void connectEnvironment() throws ManagementException {
209 		assert configuration != null;
210 
211 		// 1. Start server.
212 		if (configuration.getControlServer() != null)
213 			startServer();
214 
215 	}
216 
217 	@Override
218 	protected void connectAgents() throws ManagementException {
219 		// 2. Start bots.
220 		for (BotParameters bot : configuration.getBots()) {
221 			startAgent(bot);
222 		}
223 
224 	}
225 
226 	protected void startServer() throws ManagementException {
227 		// 1. Connect to UT server
228 		try {
229 
230 			UT2004ServerRunner<? extends IUT2004Server, ? extends UT2004AgentParameters> serverRunner = createServerRunner();
231 			utServer = serverRunner.startAgent();
232 		} catch (PogamutException e) {
233 			throw new ManagementException(
234 			// Adding exception as String. While Pogamut exceptions
235 			// themselves are properly serializable, their contents may not be.
236 					"Pogmut was unable to start the server. Cause: "
237 							+ e.toString());
238 		}
239 		String simpleID = EnvironmentUtil.simplefyID(utServer.getComponentId());
240 
241 		try {
242 			registerEntity(simpleID, "server", utServer,
243 					createServerActionHandler(utServer),
244 					createServerPerceptHandler(utServer));
245 		} catch (EntityException e) {
246 			utServer.stop();
247 			throw new ManagementException("Unable to register entity", e);
248 		}
249 
250 		// 6. Add bot dead listeners
251 		agentDownListeners.put(simpleID, new AgentDownListener(simpleID,
252 				utServer));
253 
254 		// 7. Check if server is still alive. Throw out if not.
255 		// TODO: How?
256 	}
257 
258 	protected ActionHandler createServerActionHandler(IUT2004Server entity)
259 			throws EntityException {
260 		return new DefaultActionHandler(entity);
261 	}
262 
263 	protected PerceptHandler createServerPerceptHandler(IUT2004Server entity)
264 			throws EntityException {
265 		return new DefaultPerceptHandler(entity);
266 	}
267 
268 	protected UT2004ServerRunner<? extends IUT2004Server, ? extends UT2004AgentParameters> createServerRunner() {
269 		UT2004ServerModule<UT2004AgentParameters> serverModule = new UT2004ServerModule<UT2004AgentParameters>();
270 		UT2004ServerFactory<IUT2004Server, UT2004AgentParameters> serverFactory = new UT2004ServerFactory<IUT2004Server, UT2004AgentParameters>(
271 				serverModule);
272 		UT2004ServerRunner<IUT2004Server, UT2004AgentParameters> serverRunner = new UT2004ServerRunner<IUT2004Server, UT2004AgentParameters>(
273 				serverFactory, "UTServer",
274 				configuration.getControlServerHost(),
275 				configuration.getControlServerPort());
276 		return serverRunner;
277 	}
278 
279 	protected synchronized void startAgent(BotParameters parameters)
280 			throws ManagementException {
281 
282 		// 1. Don't add bots if the environment has been killed.
283 		if (getState() == EnvironmentState.KILLED) {
284 			return;
285 		}
286 
287 		// 2. Unpause the game. Can't add agents to paused game.
288 		utServer.getAct().act(new Pause(false, false));
289 
290 		// 3. Set defaults
291 		parameters.assignDefaults(BotParameters.getDefaults());
292 
293 		UTBotRunner<UT2004Bot<IVisionWorldView, IAct, UT2004BotController>, UT2004BotParameters> runner = getBotRunner(configuration);
294 
295 		runner.setLogLevel(parameters.getLogLevel());
296 		// TODO: File logging from inside agent perhaps?
297 
298 		// 4. Launch bots using the parameters
299 		UT2004Bot<IVisionWorldView, IAct, UT2004BotController> agent;
300 		try {
301 			agent = runner.startAgents(parameters).get(0);
302 		} catch (PogamutException e) {
303 			throw new ManagementException(
304 			// Adding exception as String. While Pogamut exceptions
305 			// themselves are properly serializable, their contents may not be.
306 					"Pogmut was unable to start an agents. Cause: "
307 							+ e.toString());
308 		}
309 
310 		// 5. Notify EIS of new entity.
311 		String simpleID = EnvironmentUtil.simplefyID(agent.getComponentId());
312 		UT2004BotController controller = agent.getController();
313 		try {
314 			registerEntity(simpleID, "bot", controller,
315 					createActionHandler(controller),
316 					createPerceptHandler(controller));
317 		} catch (EntityException e) {
318 			agent.stop();
319 
320 			throw new ManagementException("Unable to register entity", e);
321 		}
322 
323 		// 6. Add bot dead listeners
324 		agentDownListeners
325 				.put(simpleID, new AgentDownListener(simpleID, agent));
326 
327 		// 7. Check if bots are still alive. Throw out if not.
328 		if (agent.inState(IAgentStateDown.class)) {
329 			agentDownListeners.get(simpleID).removeListener();
330 			synchronizedDeleteEntity(simpleID);
331 
332 		}
333 
334 		// 8. Everything aokay
335 	}
336 
337 	protected abstract UTBotRunner<UT2004Bot<IVisionWorldView, IAct, UT2004BotController>, UT2004BotParameters> getBotRunner(
338 			Configuration configuration);
339 
340 	protected abstract Class<? extends IUT2004BotController> getControlerClass();
341 
342 	protected abstract PerceptHandler createPerceptHandler(
343 			UT2004BotController controller) throws EntityException;
344 
345 	protected abstract ActionHandler createActionHandler(
346 			UT2004BotController controller) throws EntityException;
347 
348 	protected void startEnvironment() throws ManagementException {
349 		utServer.getAct().act(new Pause(false, false));
350 	}
351 
352 	protected void pauseEvironment() {
353 		utServer.getAct().act(new Pause(true, false));
354 	}
355 
356 	protected synchronized void killEnvironment() {
357 		// 1. Unpause the environment.
358 		if (utServer != null)
359 			utServer.getAct().act(new Pause(false, false));
360 
361 		// Wait for bots to catch up with unpausing
362 		try {
363 			Thread.sleep(1000);
364 		} catch (InterruptedException e1) {
365 			// If not able to wait, continue on and try anyway.
366 		}
367 
368 		// 2. Shut down all bots.
369 		for (String id : getEntities()) {
370 
371 			// TODO: This is a good example why we need a uniform entity
372 			// interface. Or a way to track the type of an entity.
373 			Object entity = getEntity(id);
374 			IAgent bot;
375 			if (entity instanceof UT2004BotController<?>) {
376 				@SuppressWarnings("unchecked")
377 				UT2004BotController<UT2004Bot> controller = ((UT2004BotController<UT2004Bot>) getEntity(id));
378 				bot = controller.getBot();
379 			} else {
380 				bot = (IAgent) entity;
381 			}
382 
383 			try {
384 				agentDownListeners.get(id).removeListener();
385 				bot.stop();
386 				log.info(bot.getName() + " has been stopped");
387 			} catch (AgentException e) {
388 				// When a bot can not be stopped it will be killed.
389 				log.info(bot.getName() + " has been killed", e);
390 			}
391 		}
392 
393 		// 3. Close the connection to the utServer.
394 		if (utServer != null)
395 			utServer.stop();
396 		utServer = null;
397 
398 		// 4. Clear up config
399 		configuration = null;
400 
401 		// 5. Stop pogamut platform. Prevents memory leak see #1727
402 		Pogamut.getPlatform().close();
403 	}
404 
405 	/**
406 	 * Synchronized version of {@link AbstractEnvironment.deleteEntity}. Used by
407 	 * {@link AgentDownListener} to removed agents that have shut down.
408 	 * 
409 	 * @param entity
410 	 *            to remove.
411 	 */
412 	protected synchronized void synchronizedDeleteEntity(String name) {
413 		try {
414 			deleteEntity(name);
415 		} catch (RelationException e) {
416 			// TODO: This relationship exception is no longer thrown.
417 			log.severe("Could not delete entity " + name);
418 		} catch (EntityException e) {
419 			// TODO: This should be replaced by an assertion in the default
420 			// implementation of EIS.
421 			log.severe("Could not delete entity " + name
422 					+ ", it was already deleted.");
423 		}
424 	}
425 
426 	/**
427 	 * Monitors the the agent state, if the agent goes down it is removed from
428 	 * the environment.
429 	 * 
430 	 * @author M.P. Korstanje
431 	 * 
432 	 */
433 	private class AgentDownListener implements FlagListener<IAgentState> {
434 
435 		private final String key;
436 		private final IAgent agent;
437 
438 		public AgentDownListener(String key, IAgent agent) {
439 			this.key = key;
440 			this.agent = agent;
441 			this.agent.getState().addStrongListener(this);
442 		}
443 
444 		@Override
445 		public void flagChanged(IAgentState state) {
446 			if (state instanceof IAgentStateDown) {
447 				removeListener();
448 				synchronizedDeleteEntity(key);
449 			}
450 		}
451 
452 		public void removeListener() {
453 			agent.getState().removeListener(this);
454 			agentDownListeners.remove(key);
455 		}
456 	}
457 
458 	@Override
459 	protected boolean isSupportedByEnvironment(Action arg0) {
460 		return true;
461 	}
462 
463 	@Override
464 	protected boolean isSupportedByType(Action arg0, String arg1) {
465 		return true;
466 	}
467 
468 }