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