View Javadoc

1   package cz.cuni.amis.pogamut.ut2004.analyzer;
2   
3   import java.io.File;
4   import java.io.FileNotFoundException;
5   import java.io.FileOutputStream;
6   import java.io.PrintWriter;
7   import java.util.HashMap;
8   import java.util.Map;
9   import java.util.logging.Level;
10  
11  import com.google.inject.Inject;
12  
13  import cz.cuni.amis.pogamut.base.agent.state.level1.IAgentStateUp;
14  import cz.cuni.amis.pogamut.base.agent.state.level2.IAgentStateRunning;
15  import cz.cuni.amis.pogamut.base.communication.command.IAct;
16  import cz.cuni.amis.pogamut.base.communication.connection.impl.socket.SocketConnection;
17  import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
18  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEvent;
19  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectListener;
20  import cz.cuni.amis.pogamut.base.communication.worldview.object.event.WorldObjectDestroyedEvent;
21  import cz.cuni.amis.pogamut.base.component.bus.IComponentBus;
22  import cz.cuni.amis.pogamut.base.utils.logging.IAgentLogger;
23  import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
24  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.Configuration;
25  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.StartPlayers;
26  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Player;
27  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.PlayerJoinsGame;
28  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.PlayerLeft;
29  import cz.cuni.amis.pogamut.ut2004.communication.worldview.UT2004WorldView;
30  import cz.cuni.amis.pogamut.ut2004.factory.guice.remoteagent.UT2004AnalyzerFactory;
31  import cz.cuni.amis.pogamut.ut2004.factory.guice.remoteagent.UT2004ObserverFactory;
32  import cz.cuni.amis.pogamut.ut2004.observer.IUT2004Observer;
33  import cz.cuni.amis.pogamut.ut2004.server.impl.UT2004Server;
34  import cz.cuni.amis.pogamut.ut2004.utils.UT2004AnalyzerRunner;
35  import cz.cuni.amis.pogamut.ut2004.utils.UT2004ObserverRunner;
36  import cz.cuni.amis.utils.exception.PogamutInterruptedException;
37  import cz.cuni.amis.utils.listener.Listeners;
38  
39  /**
40   * UT2004Analyzer can be used to automatically observe all bots/players in the game sniff their messages.
41   * <p><p>
42   * It creates and launches {@link IUT2004AnalyzerObserver} that is constructed according to the {@link UT2004AnalyzerParameters#getObserverModule()}.
43   * 
44   * @author Jimmy
45   */
46  public class UT2004Analyzer extends UT2004Server implements IUT2004Analyzer {
47  
48  	private Object mutex = new Object();
49  	
50  	private Listeners<IAnalyzerObserverListener> observerListeners = new Listeners<IAnalyzerObserverListener>();
51  	private IAnalyzerObserverListener.ObserverAddedNotifier observerAddedNotifier = new IAnalyzerObserverListener.ObserverAddedNotifier();
52  	private IAnalyzerObserverListener.ObserverRemovedNotifier observerRemovedNotifier = new IAnalyzerObserverListener.ObserverRemovedNotifier();
53  	
54  	private void addObserver(UnrealId botId, String botName, boolean forced) {
55  		if (!forced && !inState(IAgentStateRunning.class)) {
56  			if (log.isLoggable(Level.INFO)) log.info("Not running yet, could not add observer for " + botId + ".");
57  			return;
58  		}
59  		synchronized(observers) {
60  			if (observers.containsKey(botId)) return;
61  			if (log.isLoggable(Level.INFO)) log.info("New bot has connected to the game, creating new observer for the bot with id '" + botId.getStringId() + "'.");
62  			String fileName = getParams().getFileNames() != null ? getParams().getFileNames().get(botId) + ".csv" : null;
63  			IUT2004AnalyzerObserver observer = getObserverRunner().startAgents(
64  					new UT2004AnalyzerFullObserverParameters()
65  						.setObservedAgentId(botId.getStringId())
66  						.setOutputPath(getParams().getOutputPath())
67  						.setWaitForMatchRestart(getParams().isWaitForMatchRestart())
68  						.setFileName(fileName)
69  						.setWorldAddress(getParams().getObserverAddress())
70  						.setHumanLikeObserving(botName, humanLike_writer)
71  			).get(0);
72  			observers.put(botId, observer);
73  			observerAddedNotifier.setBotId(botId);
74  			observerAddedNotifier.setObserver(observer);
75  			observerListeners.notify(observerAddedNotifier);
76  		}
77  	}
78  	
79  	private void removeObserver(UnrealId botId) {
80  		synchronized(observers) {
81  			IUT2004AnalyzerObserver observer = observers.get(botId);
82  			if (log.isLoggable(Level.INFO)) log.info("Bot '" + botId.getStringId() + "' has left the game");			
83  			if (observer != null) {
84  				if (log.isLoggable(Level.INFO)) log.info("Stopping observer for the bot.");
85  				try {
86  					Thread.sleep(50);
87  				} catch (InterruptedException e) {
88  					throw new PogamutInterruptedException(e, this);
89  				}
90  				if (observer.getState().getFlag() instanceof IAgentStateUp) {
91  					try {
92  						observer.stop();
93  					} catch (Exception e) {
94  						log.warning("Observer for the bot '" + observer.getObservedBotId().getStringId() + "' could not be stopped, killing...");
95  						try {
96  							observer.kill();
97  						} catch (Exception e2) {
98  							log.warning("Observer for the bot '" + observer.getObservedBotId().getStringId() + "' could not be killed: " + e2.getMessage());
99  						}
100 					}
101 				}
102 				observers.remove(botId);
103 				observerRemovedNotifier.setBotId(botId);
104 				observerRemovedNotifier.setObserver(observer);
105 				observerListeners.notify(observerRemovedNotifier);				
106 			} else {
107 				if (log.isLoggable(Level.WARNING)) log.warning("The bot '" + botId.getStringId() + "' has no observer attached, was not observer, probably because the analyzer has been started after the bot itself.");
108 			}
109 		}
110 	}
111 	
112 	private IWorldObjectListener<Player> playerListener = new IWorldObjectListener<Player>() {
113 		@Override
114 		public void notify(IWorldObjectEvent<Player> event) {			
115 			Player plr = event.getObject();
116 			if (event instanceof WorldObjectDestroyedEvent) {
117 				removeObserver(plr.getId());
118 			} else {
119 				addObserver(plr.getId(), plr.getName(), false);
120 			}
121 		}
122 	};
123 	
124 	private IWorldEventListener<PlayerJoinsGame> playerJoinsGameListener = new IWorldEventListener<PlayerJoinsGame>() {
125 		@Override
126 		public void notify(PlayerJoinsGame event) {
127 			addObserver(event.getId(), event.getName(), false);
128 		}
129 	};
130 	
131 	private IWorldEventListener<PlayerLeft> playerLeftListener = new IWorldEventListener<PlayerLeft>() {
132 
133 		@Override
134 		public void notify(PlayerLeft event) {
135 			removeObserver(event.getId());
136 		}
137 		
138 	};
139 	
140 	/**
141 	 * Stored pointers to observers the analyzer owns.
142 	 */
143 	private Map<UnrealId, IUT2004AnalyzerObserver> observers = new HashMap<UnrealId, IUT2004AnalyzerObserver>();
144 
145 	/**
146 	 * Runner that is used to start new instances of {@link IUT2004AnalyzerObserver}
147 	 */
148 	private UT2004ObserverRunner<IUT2004AnalyzerObserver, UT2004AnalyzerFullObserverParameters> observerRunner;
149 	
150 	private File humanLike_outputFile = null;
151 	
152 	private PrintWriter humanLike_writer = null;
153 	
154 	@Inject
155 	public UT2004Analyzer(UT2004AnalyzerParameters params,
156 			IAgentLogger agentLogger, IComponentBus bus,
157 			SocketConnection connection, UT2004WorldView worldView, IAct act) {
158 		super(params, agentLogger, bus, connection, worldView, act);
159 		
160 		observerListeners.setLog(log, "ObserverListeners");
161 		
162 		// WE NEED THESE LISTENERS AS SOON AS POSSIBLE... otherwise we might miss some events
163 		
164 		getWorldView().addEventListener(PlayerJoinsGame.class, playerJoinsGameListener);
165 		getWorldView().addEventListener(PlayerLeft.class, playerLeftListener);
166 		
167 		// DO NOT USE PLAYER UPDATES ... THEY ARE TRICKY!
168 		// if some bot leaves the play, first PlayerLeft message is received, but after that (sometimes) another PLR message is received, causing observer to start for already-left bot :(
169 		//getWorldView().addObjectListener(Player.class, playerListener);
170 	}
171 	
172 	private UT2004ObserverRunner<IUT2004AnalyzerObserver, UT2004AnalyzerFullObserverParameters> getObserverRunner() {
173 		if (observerRunner == null) {
174 			synchronized(mutex) {
175 				if (observerRunner == null) {
176 					observerRunner = 
177 						new UT2004ObserverRunner(
178 							new UT2004ObserverFactory<IUT2004Observer, UT2004AnalyzerFullObserverParameters>(
179 								getParams().getObserverModule())
180 							);
181 				}
182 			}
183 		}		
184 		return observerRunner; 
185 	}
186 	
187 	@Override
188 	public UT2004AnalyzerParameters getParams() {
189 		return (UT2004AnalyzerParameters) super.getParams();
190 	}
191 
192 	@Override
193 	public Map<UnrealId, IUT2004AnalyzerObserver> getObservers() {
194 		synchronized(observers) {
195 			return new HashMap<UnrealId, IUT2004AnalyzerObserver>(observers);
196 		}
197 	}
198 	
199 	@Override
200 	protected void startAgent() {
201 		super.startAgent();
202 		if (getParams().getHumanLikeObserving() != null && getParams().getHumanLikeObserving()) {
203 			File humanLike_outputFileDir = new File(getParams().getOutputPath());
204 			humanLike_outputFileDir.mkdirs();
205 			humanLike_outputFile = new File(getParams().getOutputPath() + System.getProperty("file.separator") + "humanLikeData.log");
206 			try {
207 				humanLike_writer = new PrintWriter(new FileOutputStream(humanLike_outputFile));
208 			} catch (FileNotFoundException e) {
209 				throw new RuntimeException("Could not create writer for human-like log at: " + humanLike_outputFile.getAbsolutePath());
210 			}
211 		}
212 	}
213 	
214 	@Override
215 	protected void startPausedAgent() {
216 		super.startPausedAgent();
217 		if (getParams().getHumanLikeObserving() != null && getParams().getHumanLikeObserving()) {
218 			File humanLike_outputFileDir = new File(getParams().getOutputPath());
219 			humanLike_outputFileDir.mkdirs();
220 			humanLike_outputFile = new File(getParams().getOutputPath() + System.getProperty("file.separator") + "humanLikeData.log");
221 			try {
222 				humanLike_writer = new PrintWriter(new FileOutputStream(humanLike_outputFile));
223 			} catch (FileNotFoundException e) {
224 				throw new RuntimeException("Could not create writer for human-like log at: " + humanLike_outputFile.getAbsolutePath());
225 			}			
226 		}		
227 	}
228 	
229 	@Override
230 	protected void stopAgent() {
231 		super.stopAgent();
232 		cleanUp();
233 	}
234 	
235 	@Override
236 	protected void killAgent() {
237 		super.killAgent();
238 		cleanUp();
239 	}
240 	
241 	protected void init() {
242 		super.init();
243 		synchronized(mutex) {
244 	        getAct().act(new StartPlayers(true, true, true));
245 	        for (Player player : getWorldView().getAll(Player.class).values()) {
246 				addObserver(player.getId(), player.getName(), true);					
247 	        }
248     	}
249     }
250 
251 	/**
252 	 * Called from {@link UT2004Analyzer#stopAgent()} and {@link UT2004Analyzer#killAgent()} to clean up stuff (stops observers).
253 	 */
254 	protected void cleanUp() {
255 		synchronized(observers) {
256 			for (IUT2004AnalyzerObserver observer : observers.values()) {
257 				if (observer.getState().getFlag() instanceof IAgentStateUp) {
258 					try {
259 						observer.stop();
260 					} catch (Exception e) {
261 						if (log.isLoggable(Level.WARNING)) log.warning("Observer for the bot '" + observer.getObservedBotId().getStringId() + "' could not be stopped, killing...");
262 						try {
263 							observer.kill();
264 						} catch (Exception e2) {
265 							if (log.isLoggable(Level.WARNING)) log.warning("Observer for the bot '" + observer.getObservedBotId().getStringId() + "' could not be killed: " + e2.getMessage());
266 						}
267 					}
268 				}
269 			}
270 			if (humanLike_writer != null) {
271 				try {
272 					humanLike_writer.close();
273 				} catch (Exception e) {
274 					if (humanLike_outputFile != null) {
275 						log.warning("Failed to close human-like log file at: " + humanLike_outputFile.getAbsolutePath());
276 					} else {
277 						log.warning("Failed to close human-like log file (unknown location, weird as well...)!");
278 					}
279 				}
280 				humanLike_outputFile = null;
281 				humanLike_writer = null;
282 			}
283 		}
284 	}
285 	
286 	@Override
287 	public void addListener(IAnalyzerObserverListener listener) {
288 		observerListeners.addWeakListener(listener);
289 	}
290 
291 	@Override
292 	public boolean isListening(IAnalyzerObserverListener listener) {
293 		return observerListeners.isListening(listener);
294 	}
295 
296 	@Override
297 	public void removeListener(IAnalyzerObserverListener listener) {
298 		observerListeners.removeListener(listener);
299 	}
300 	
301 	public static void main(String[] args) {
302 		UT2004AnalyzerRunner<IUT2004Analyzer, UT2004AnalyzerParameters> analyzerRunner = new UT2004AnalyzerRunner<IUT2004Analyzer, UT2004AnalyzerParameters>(
303     		new UT2004AnalyzerFactory(
304     			new UT2004AnalyzerModule()
305     		)
306     	);
307     	analyzerRunner.setLogLevel(Level.INFO);
308     	analyzerRunner.setMain(true).startAgent();
309 	}
310 
311 
312 }