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.HashSet;
9   import java.util.Map;
10  import java.util.Set;
11  import java.util.logging.Level;
12  
13  import com.google.inject.Inject;
14  
15  import cz.cuni.amis.pogamut.base.agent.state.level1.IAgentStateUp;
16  import cz.cuni.amis.pogamut.base.agent.state.level2.IAgentStateRunning;
17  import cz.cuni.amis.pogamut.base.communication.command.IAct;
18  import cz.cuni.amis.pogamut.base.communication.connection.impl.socket.SocketConnection;
19  import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
20  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEvent;
21  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectListener;
22  import cz.cuni.amis.pogamut.base.component.bus.IComponentBus;
23  import cz.cuni.amis.pogamut.base.utils.logging.IAgentLogger;
24  import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
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) != null ? getParams().getFileNames().get(botId) + ".csv" : botId + ".csv";
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  			nonObserved.remove(botId);
77  		}
78  	}
79  	
80  	private void removeObserver(UnrealId botId) {
81  		synchronized(observers) {
82  			if (log.isLoggable(Level.INFO)) log.info("Bot '" + botId.getStringId() + "' has left the game");
83  			IUT2004AnalyzerObserver observer = observers.get(botId);						
84  			if (observer != null) {
85  				if (log.isLoggable(Level.INFO)) log.info("Stopping observer for the bot.");
86  				try {
87  					Thread.sleep(50);
88  				} catch (InterruptedException e) {
89  					throw new PogamutInterruptedException(e, this);
90  				}
91  				if (observer.getState().getFlag() instanceof IAgentStateUp) {
92  					try {
93  						observer.stop();
94  					} catch (Exception e) {
95  						log.warning("Observer for the bot '" + observer.getObservedBotId().getStringId() + "' could not be stopped, killing...");
96  						try {
97  							observer.kill();
98  						} catch (Exception e2) {
99  							log.warning("Observer for the bot '" + observer.getObservedBotId().getStringId() + "' could not be killed: " + e2.getMessage());
100 						}
101 					}
102 				}
103 				observers.remove(botId);
104 				observerRemovedNotifier.setBotId(botId);
105 				observerRemovedNotifier.setObserver(observer);
106 				observerListeners.notify(observerRemovedNotifier);				
107 			}
108 			nonObserved.remove(botId); // must be here in case of "PlayerLeft"!
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 (!observers.containsKey(plr.getId())) return;
117 			synchronized(observers){ 
118 				if (plr.isSpectator()) {
119 					// DO NOT OBSERVE SPECTATORS!
120 					removeObserver(plr.getId());
121 					// MARK AS SPECTATOR
122 					nonObserved.add(plr.getId());
123 				} else {
124 					if (!observers.containsKey(plr.getId())) addObserver(plr.getId(), plr.getName(), false);
125 				}
126 			}
127 		}
128 	};
129 	
130 	private IWorldEventListener<PlayerJoinsGame> playerJoinsGameListener = new IWorldEventListener<PlayerJoinsGame>() {
131 		@Override
132 		public void notify(PlayerJoinsGame event) {
133 			addObserver(event.getId(), event.getName(), false);
134 		}
135 	};
136 	
137 	private IWorldEventListener<PlayerLeft> playerLeftListener = new IWorldEventListener<PlayerLeft>() {
138 
139 		@Override
140 		public void notify(PlayerLeft event) {
141 			removeObserver(event.getId());
142 		}
143 		
144 	};
145 	
146 	/**
147 	 * Stored pointers to observers the analyzer owns.
148 	 */
149 	private Map<UnrealId, IUT2004AnalyzerObserver> observers = new HashMap<UnrealId, IUT2004AnalyzerObserver>();
150 
151 	/**
152 	 * Contains {@link UnrealId} of {@link Player}s that are within the game (we should observe them), but they are spectators,
153 	 * therefore, they are not inside 'observers'. 
154 	 */
155 	private Set<UnrealId> nonObserved = new HashSet<UnrealId>();
156 	
157 	/**
158 	 * Runner that is used to start new instances of {@link IUT2004AnalyzerObserver}
159 	 */
160 	private UT2004ObserverRunner<IUT2004AnalyzerObserver, UT2004AnalyzerFullObserverParameters> observerRunner;
161 	
162 	private File humanLike_outputFile = null;
163 	
164 	private PrintWriter humanLike_writer = null;
165 	
166 	@Inject
167 	public UT2004Analyzer(UT2004AnalyzerParameters params,
168 			IAgentLogger agentLogger, IComponentBus bus,
169 			SocketConnection connection, UT2004WorldView worldView, IAct act) {
170 		super(params, agentLogger, bus, connection, worldView, act);
171 		
172 		observerListeners.setLog(log, "ObserverListeners");
173 		
174 		// WE NEED THESE LISTENERS AS SOON AS POSSIBLE... otherwise we might miss some events
175 		
176 		getWorldView().addEventListener(PlayerJoinsGame.class, playerJoinsGameListener);
177 		getWorldView().addEventListener(PlayerLeft.class, playerLeftListener);
178 		
179 		// WARNING PLAYER UPDATES ARE TRICKY!
180 		// 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 :(
181 		getWorldView().addObjectListener(Player.class, playerListener);
182 	}
183 	
184 	private UT2004ObserverRunner<IUT2004AnalyzerObserver, UT2004AnalyzerFullObserverParameters> getObserverRunner() {
185 		if (observerRunner == null) {
186 			synchronized(mutex) {
187 				if (observerRunner == null) {
188 					observerRunner = 
189 						new UT2004ObserverRunner(
190 							new UT2004ObserverFactory<IUT2004Observer, UT2004AnalyzerFullObserverParameters>(
191 								getParams().getObserverModule())
192 							);
193 				}
194 			}
195 		}		
196 		return observerRunner; 
197 	}
198 	
199 	@Override
200 	public UT2004AnalyzerParameters getParams() {
201 		return (UT2004AnalyzerParameters) super.getParams();
202 	}
203 
204 	@Override
205 	public Map<UnrealId, IUT2004AnalyzerObserver> getObservers() {
206 		synchronized(observers) {
207 			return new HashMap<UnrealId, IUT2004AnalyzerObserver>(observers);
208 		}
209 	}
210 	
211 	@Override
212 	protected void startAgent() {
213 		super.startAgent();
214 		if (getParams().getHumanLikeObserving() != null && getParams().getHumanLikeObserving()) {
215 			File humanLike_outputFileDir = new File(getParams().getOutputPath());
216 			humanLike_outputFileDir.mkdirs();
217 			humanLike_outputFile = new File(getParams().getOutputPath() + System.getProperty("file.separator") + "humanLikeData.log");
218 			try {
219 				humanLike_writer = new PrintWriter(new FileOutputStream(humanLike_outputFile));
220 			} catch (FileNotFoundException e) {
221 				throw new RuntimeException("Could not create writer for human-like log at: " + humanLike_outputFile.getAbsolutePath());
222 			}
223 		}
224 	}
225 	
226 	@Override
227 	protected void startPausedAgent() {
228 		super.startPausedAgent();
229 		if (getParams().getHumanLikeObserving() != null && getParams().getHumanLikeObserving()) {
230 			File humanLike_outputFileDir = new File(getParams().getOutputPath());
231 			humanLike_outputFileDir.mkdirs();
232 			humanLike_outputFile = new File(getParams().getOutputPath() + System.getProperty("file.separator") + "humanLikeData.log");
233 			try {
234 				humanLike_writer = new PrintWriter(new FileOutputStream(humanLike_outputFile));
235 			} catch (FileNotFoundException e) {
236 				throw new RuntimeException("Could not create writer for human-like log at: " + humanLike_outputFile.getAbsolutePath());
237 			}			
238 		}		
239 	}
240 	
241 	@Override
242 	protected void stopAgent() {
243 		super.stopAgent();
244 		cleanUp();
245 	}
246 	
247 	@Override
248 	protected void killAgent() {
249 		super.killAgent();
250 		cleanUp();
251 	}
252 	
253 	protected void init() {
254 		super.init();
255 		synchronized(mutex) {
256 	        getAct().act(new StartPlayers(true, true, true));
257 	        for (Player player : getWorldView().getAll(Player.class).values()) {
258 				addObserver(player.getId(), player.getName(), true);					
259 	        }
260     	}
261     }
262 
263 	/**
264 	 * Called from {@link UT2004Analyzer#stopAgent()} and {@link UT2004Analyzer#killAgent()} to clean up stuff (stops observers).
265 	 */
266 	protected void cleanUp() {
267 		synchronized(observers) {
268 			for (IUT2004AnalyzerObserver observer : observers.values()) {
269 				if (observer.getState().getFlag() instanceof IAgentStateUp) {
270 					try {
271 						observer.stop();
272 					} catch (Exception e) {
273 						if (log.isLoggable(Level.WARNING)) log.warning("Observer for the bot '" + observer.getObservedBotId().getStringId() + "' could not be stopped, killing...");
274 						try {
275 							observer.kill();
276 						} catch (Exception e2) {
277 							if (log.isLoggable(Level.WARNING)) log.warning("Observer for the bot '" + observer.getObservedBotId().getStringId() + "' could not be killed: " + e2.getMessage());
278 						}
279 					}
280 				}
281 			}
282 			if (humanLike_writer != null) {
283 				try {
284 					humanLike_writer.close();
285 				} catch (Exception e) {
286 					if (humanLike_outputFile != null) {
287 						log.warning("Failed to close human-like log file at: " + humanLike_outputFile.getAbsolutePath());
288 					} else {
289 						log.warning("Failed to close human-like log file (unknown location, weird as well...)!");
290 					}
291 				}
292 				humanLike_outputFile = null;
293 				humanLike_writer = null;
294 			}
295 		}
296 	}
297 	
298 	@Override
299 	public void addListener(IAnalyzerObserverListener listener) {
300 		observerListeners.addWeakListener(listener);
301 	}
302 
303 	@Override
304 	public boolean isListening(IAnalyzerObserverListener listener) {
305 		return observerListeners.isListening(listener);
306 	}
307 
308 	@Override
309 	public void removeListener(IAnalyzerObserverListener listener) {
310 		observerListeners.removeListener(listener);
311 	}
312 	
313 	public static void main(String[] args) {
314 		UT2004AnalyzerRunner<IUT2004Analyzer, UT2004AnalyzerParameters> analyzerRunner = new UT2004AnalyzerRunner<IUT2004Analyzer, UT2004AnalyzerParameters>(
315     		new UT2004AnalyzerFactory(
316     			new UT2004AnalyzerModule()
317     		)
318     	);
319     	analyzerRunner.setLogLevel(Level.INFO);
320     	analyzerRunner.setMain(true).startAgent();
321 	}
322 
323 
324 }