View Javadoc

1   package cz.cuni.amis.pogamut.ut2004.tag.server;
2   
3   import java.util.ArrayList;
4   import java.util.HashSet;
5   import java.util.Iterator;
6   import java.util.List;
7   import java.util.Map;
8   import java.util.Random;
9   import java.util.Set;
10  
11  import com.google.inject.Inject;
12  
13  import cz.cuni.amis.pogamut.base.communication.command.IAct;
14  import cz.cuni.amis.pogamut.base.communication.connection.impl.socket.SocketConnection;
15  import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
16  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEvent;
17  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectListener;
18  import cz.cuni.amis.pogamut.base.component.bus.IComponentBus;
19  import cz.cuni.amis.pogamut.base.utils.logging.IAgentLogger;
20  import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
21  import cz.cuni.amis.pogamut.ut2004.agent.params.UT2004AgentParameters;
22  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.SendControlMessage;
23  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.SendMessage;
24  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.StartPlayers;
25  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.BeginMessage;
26  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.EndMessage;
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.messages.gbinfomessages.PlayerMessage;
30  import cz.cuni.amis.pogamut.ut2004.communication.worldview.UT2004WorldView;
31  import cz.cuni.amis.pogamut.ut2004.server.IUT2004Server;
32  import cz.cuni.amis.pogamut.ut2004.server.impl.UT2004Server;
33  import cz.cuni.amis.pogamut.ut2004.tag.protocol.TagMessages;
34  import cz.cuni.amis.pogamut.ut2004.tag.protocol.messages.TagGameEnd;
35  import cz.cuni.amis.pogamut.ut2004.tag.protocol.messages.TagGameRunning;
36  import cz.cuni.amis.pogamut.ut2004.tag.protocol.messages.TagGameStart;
37  import cz.cuni.amis.pogamut.ut2004.tag.protocol.messages.TagMessage;
38  import cz.cuni.amis.pogamut.ut2004.tag.protocol.messages.TagPassed;
39  import cz.cuni.amis.pogamut.ut2004.tag.protocol.messages.TagPlayerImmunity;
40  import cz.cuni.amis.pogamut.ut2004.tag.protocol.messages.TagPlayerScoreChanged;
41  import cz.cuni.amis.pogamut.ut2004.tag.protocol.messages.TagPlayerStatusChanged;
42  import cz.cuni.amis.utils.Tuple3;
43  import cz.cuni.amis.utils.flag.Flag;
44  import cz.cuni.amis.utils.maps.LazyMap;
45  
46  public class UT2004TagServer extends UT2004Server implements IUT2004Server {
47  	
48  	public static final UnrealId SERVER_UNREAL_ID = UnrealId.get("TAG_SERVER");
49  
50  	public static final double TAG_DISTANCE = 80;
51  	
52  	public static final long GAME_RUNNING_PERIOD_SECS = 5;
53  
54  	private Random random = new Random(System.currentTimeMillis());
55  	
56  	private Object mutex = new Object();
57  	
58      /**
59       * BeginMessage listener - we get current server time here.
60       */
61      private IWorldEventListener<BeginMessage> myBeginMessageListener = new IWorldEventListener<BeginMessage>() {
62          public void notify(BeginMessage event) {
63              timeUpdate(event);
64          }
65      };
66      
67      /**
68       * BeginMessage listener - we get current server time here.
69       */
70      private IWorldEventListener<EndMessage> myEndMessageListener = new IWorldEventListener<EndMessage>() {
71          public void notify(EndMessage event) {
72              batchEnd(event);
73          }
74      };
75      
76      /**
77       * PlayerJoinsGame listener - we get informed that new player/bot has entered the game.
78       */
79      private IWorldEventListener<PlayerJoinsGame> myPlayerJoinsGameMessageListener = new IWorldEventListener<PlayerJoinsGame>() {
80          public void notify(PlayerJoinsGame event) {
81              playerJoinsGame(event);
82          }
83      };
84      
85      /**
86       * PlayerLeft listener - we get informed that new player/bot has entered the game.
87       */
88      private IWorldEventListener<PlayerLeft> myPlayerLeftMessageListener = new IWorldEventListener<PlayerLeft>() {
89          public void notify(PlayerLeft event) {
90              playerLeft(event);
91          }
92      };
93  
94      /**
95       * Player listener - we simply print out all player messages we receive.
96       */
97      private IWorldObjectListener<PlayerMessage> myPlayerListener = new IWorldObjectListener<PlayerMessage>() {
98          public void notify(IWorldObjectEvent<PlayerMessage> event) {
99              playerUpdate(event);
100         }
101     };
102     
103     
104     private TagMessages messages = new TagMessages();
105 
106     @Inject
107     public UT2004TagServer(UT2004AgentParameters params, IAgentLogger agentLogger, IComponentBus bus, SocketConnection connection, UT2004WorldView worldView, IAct act) {
108         super(params, agentLogger, bus, connection, worldView, act);
109         getWorldView().addEventListener(BeginMessage.class, myBeginMessageListener);
110         getWorldView().addEventListener(EndMessage.class, myEndMessageListener);
111         getWorldView().addEventListener(PlayerJoinsGame.class, myPlayerJoinsGameMessageListener);
112         getWorldView().addEventListener(PlayerLeft.class, myPlayerLeftMessageListener);
113         getWorldView().addObjectListener(PlayerMessage.class, myPlayerListener);
114     }
115     
116     // ==========
117     // LIFE-CYCLE
118     // ==========
119     
120     @Override
121     protected void init() {
122     	super.init();
123     	//getLogger().getCategory(YylexParser.COMPONENT_ID.getToken()).setLevel(Level.ALL);
124     	//getLogger().getCategory(getWorldView()).setLevel(Level.ALL);
125     	synchronized(mutex) {
126 	        getAct().act(new StartPlayers(true, true, false));
127     	}
128     }
129     
130     @Override
131     protected void reset() {
132     	super.reset();
133     }
134     
135     // =======
136     // CONTROL
137     // =======
138     
139     private Flag<Boolean> gameRunning = new Flag<Boolean>(false);
140     
141     private double tagGameStartUT;
142     
143     private double tagGameLengthUT;
144     
145     private double tagGameTimeLeftUT;
146     
147     private int tagGameMaxScore;
148     
149     public void startGame(int gameTimeUT, int toScore) {
150     	if (gameRunning.getFlag()) throw new RuntimeException("Cannot start the game, game is already running!");
151     	
152     	synchronized(mutex) {
153 	    	resetTagGame();    	
154 	    	gameRunning.setFlag(true);
155 	    	speak("Game STARTed!");
156 	    	
157 	    	tagGameStartUT    = utTimeCurrent;
158 	    	tagGameLengthUT   = gameTimeUT;
159 	    	tagGameTimeLeftUT = gameTimeUT;
160 	    	tagGameMaxScore   = toScore;
161 	    	
162 	    	TagGameStart startMsg = new TagGameStart();
163 	    	startMsg.setGameTimeUT(gameTimeUT);
164 	    	startMsg.setGameMaxScore(toScore);    
165 	    	startMsg.setTagPassDistance(TAG_DISTANCE);
166 	    	send(startMsg);
167 	    	    	
168 	    	if (tagged.size() == 0) {
169 	    		assignTagRandom();
170 	    	}
171     	}
172     }
173     
174     public void endGame() {
175     	if (!gameRunning.getFlag()) throw new RuntimeException("Cannot end game, game is not running!");
176     	
177     	synchronized(mutex) {
178 	    	TagGameEnd endMsg = new TagGameEnd();
179 	    	send(endMsg);
180 	    	
181 	    	long finishTime = System.currentTimeMillis();
182 	    	for (BotTagRecord<PlayerMessage> record : records.values()) {
183 	    		if (!record.isInGame()) continue;
184 	    		record.setFinishTime(finishTime);
185 	    	}
186 	    	
187 	    	speak("Game ENDed!");
188 	    	
189 	    	gameRunning.setFlag(false);
190     	}
191     }
192     
193     public Flag<Boolean> isGameRunning() {
194     	return gameRunning;
195     }
196     
197     public Map<UnrealId, BotTagRecord<PlayerMessage>> getBotRecords() {
198     	return records;
199     }
200     
201     // =======
202     // MEMBERS
203     // =======
204     
205 	private double utTimeCurrent = -1;
206 	
207 	private double utTimeLast = -1;
208 	
209 	private double utTimeDelta = -1;	
210 	
211 	private long utTimeRunningNext = GAME_RUNNING_PERIOD_SECS;
212 	
213 	private Set<BotTagRecord<PlayerMessage>> tagged = new HashSet<BotTagRecord<PlayerMessage>>();
214 	
215 	private Map<UnrealId, BotTagRecord<PlayerMessage>> records = new LazyMap<UnrealId, BotTagRecord<PlayerMessage>>() {
216 
217 		@Override
218 		protected BotTagRecord<PlayerMessage> create(UnrealId key) {
219 			return new BotTagRecord<PlayerMessage>(key);
220 		}
221 		
222 	};
223 
224     // ==============
225     // EVENT HANDLERS
226     // ==============
227     
228     private void playerUpdate(IWorldObjectEvent<PlayerMessage> event) {
229     	synchronized(mutex) {
230     		PlayerMessage player = event.getObject();
231 	    	BotTagRecord<PlayerMessage> record = ensurePlayer(player.getId());
232 	    	record.setPlayer(player);
233     	}
234     }
235     
236     private void playerJoinsGame(PlayerJoinsGame event) {
237     	synchronized(mutex) {
238     		ensurePlayer(event.getId());
239     	}
240     }
241     
242     private void playerLeft(PlayerLeft event) {
243     	synchronized(mutex) {
244 	    	if (!records.containsKey(event.getId())) return;
245 	    	
246 	    	BotTagRecord<PlayerMessage> record = records.get(event.getId());
247 	    	record.setInGame(false);
248 	    	record.setFinishTime(System.currentTimeMillis());
249 	    	
250 	    	if (!gameRunning.getFlag()) return;
251 	    	
252 	    	if (record.isHasTag()) {
253 	    		assignTag(record, null);
254 	    	}
255 	    	if (tagged.size() == 0) {
256 	    		assignTagRandom();
257 	    	}
258     	}
259     }
260     
261     private void timeUpdate(BeginMessage event) {
262     	synchronized(mutex) {
263 	    	utTimeLast = utTimeCurrent;
264 	    	utTimeCurrent = event.getTime();
265 	    	utTimeDelta = utTimeCurrent - utTimeLast;
266 	    	
267 	    	if (!gameRunning.getFlag()) return;    	
268 	    	
269 	    	if (utTimeLast > 0 && utTimeCurrent > 0 && utTimeDelta > 0) {
270 	    		tagGameTimeLeftUT -= utTimeDelta;
271 	    		utTimeRunningNext -= utTimeDelta;
272 	    		if (utTimeRunningNext < 0) {
273 	    			TagGameRunning runningMsg = new TagGameRunning();
274 	    			runningMsg.setGameMaxScore(tagGameMaxScore);
275 	    			runningMsg.setGameTimeUT((int)tagGameLengthUT);
276 	    			runningMsg.setTagPassDistance(TAG_DISTANCE);
277 	    			send(runningMsg);
278 	    			utTimeRunningNext = GAME_RUNNING_PERIOD_SECS;
279 	    		}
280 	    	}
281 	    	if (tagGameTimeLeftUT <= 0) {
282 	    		endGame();
283 	    	}
284     	}
285     }
286     
287     private void batchEnd(EndMessage event) {
288     	synchronized(mutex) {
289 	    	if (!gameRunning.getFlag()) return;
290 	    	
291 	    	List<Tuple3<BotTagRecord<PlayerMessage>, BotTagRecord<PlayerMessage>, Double>> passing = new ArrayList<Tuple3<BotTagRecord<PlayerMessage>, BotTagRecord<PlayerMessage>, Double>>();
292 	    	
293 	    	List<BotTagRecord<PlayerMessage>> inGame = getInGameBots();    	
294 	    	for (BotTagRecord<PlayerMessage> botHasTag : tagged) {
295 	    		if (botHasTag.getPlayer() == null) continue;
296 	    		
297 	    		Tuple3<BotTagRecord<PlayerMessage>, BotTagRecord<PlayerMessage>, Double> passTo = 
298 	    			new Tuple3<BotTagRecord<PlayerMessage>, BotTagRecord<PlayerMessage>, Double>(
299 	    				botHasTag, 
300 	    				null, 
301 	    				TAG_DISTANCE+1
302 	    			);
303 	    		
304 	    		for (BotTagRecord<PlayerMessage> otherBot : inGame) {
305 	    			if (botHasTag == otherBot || // same bot 
306 	    				otherBot.isHasTag() ||   // other bot already has a tag
307 	    				otherBot.getImmunity() == botHasTag.getBotId() // other bot is immute to botHasTag
308 	    			) {
309 	    				continue;
310 	    			}    			
311 	    			if (otherBot.getPlayer() == null) {
312 	    				continue;
313 	    			}
314 	    			
315 	    			double distance = botHasTag.getPlayer().getLocation().getDistance(otherBot.getPlayer().getLocation());
316 	    			if (distance < passTo.getThird()) {
317 	    				passTo.setSecond(otherBot);
318 	    				passTo.setThird(distance);
319 	    			}
320 	    		}
321 	    		
322 	    		if (passTo.getSecond() != null) {
323 	    			passing.add(passTo);
324 	    		}
325 	    	}
326 	    	
327 	    	for (Tuple3<BotTagRecord<PlayerMessage>, BotTagRecord<PlayerMessage>, Double> pass : passing) {
328 	    		assignTag(pass.getFirst(), pass.getSecond());
329 	    	}
330     	}
331     }
332     
333     // =====
334     // UTILS
335     // =====
336     
337     private BotTagRecord<PlayerMessage> ensurePlayer(UnrealId botId) {
338 		if (botId == null) return null;
339     	
340     	BotTagRecord<PlayerMessage> record = records.get(botId);    	
341     	if (record.isInGame()) return record;
342     	
343     	record.reset();
344     	record.setInGame(true);
345     	record.setHasTag(false);
346     	
347     	if (!gameRunning.getFlag()) return record;
348     	
349     	tagScoreChanged(record);    	
350     	if (tagged.size() == 0) {
351     		assignTag(null, record);
352     	} else {
353     		tagStatusChanged(record);
354     	}    	
355     	
356     	return record;
357     }
358     
359     private List<BotTagRecord<PlayerMessage>> getInGameBots() {
360 		List<BotTagRecord<PlayerMessage>> result = new ArrayList<BotTagRecord<PlayerMessage>>();
361     	for (BotTagRecord<PlayerMessage> record : records.values()) {
362     		if (!record.isInGame()) continue;
363     		if (record.getPlayer().getJmx() == null) continue;
364     		result.add(record);
365     	}
366     	return result;
367     }
368         
369     private void assignTagRandom() {
370     	assert(gameRunning.getFlag());
371     	
372     	List<BotTagRecord<PlayerMessage>> alive = getInGameBots();
373     	if (alive.size() == 0) return;
374     	BotTagRecord<PlayerMessage> record = alive.get(random.nextInt(alive.size()));
375     	assignTag(null, record);
376     }
377     
378     /**
379      * @param from may be null == server, will use {@link #SERVER_UNREAL_ID}
380      * @param to may be null == server, will use {@link #SERVER_UNREAL_ID}
381      */
382     private void assignTag(BotTagRecord<PlayerMessage> from, BotTagRecord<PlayerMessage> to) {
383     	assert(gameRunning.getFlag());
384     	
385     	if (from != null || to != null) {
386     		TagPassed passedMsg = new TagPassed();
387     		passedMsg.setFromBotId(from == null ? null : from.getBotId());
388     		passedMsg.setToBotId  (to   == null ? null : to  .getBotId());
389     		send(passedMsg);
390        	}
391     	
392     	if (from != null) {
393     		removeTag(from, to == null ? SERVER_UNREAL_ID : to.getBotId());    		
394     	}
395     	if (to != null) {
396     		addTag(to, from == null ? SERVER_UNREAL_ID : from.getBotId());
397     		speak(to.getPlayer().getName() + " now has the tag!");
398     	}   
399     	
400     }
401     
402     /**
403      * TO BE CALLED FROM {@link #assignTag(BotTagRecord, BotTagRecord)} ONLY!
404      * @param to
405      * @param from
406      */
407     private void addTag(BotTagRecord<PlayerMessage> to, UnrealId from) {
408     	assert(gameRunning.getFlag());
409     	
410     	if (from == null) from = SERVER_UNREAL_ID;
411     	if (to.isHasTag()) return;
412     	
413     	to.tagged(from);
414     	tagged.add(to);
415     	
416     	if (from != SERVER_UNREAL_ID) tagScoreChanged(to);
417     	tagStatusChanged(to);
418     	
419     	if (to.getImmunity() != null) {
420     		removeImmunity(to);
421     	}
422     	
423     	if (from != SERVER_UNREAL_ID && records.containsKey(from)) {
424     		BotTagRecord<PlayerMessage> fromRecord = records.get(from);
425     		addImmunity(fromRecord, to.getBotId());
426     	}
427     	
428     	if (to.getScore() >= tagGameMaxScore) {
429     		endGame();
430     	}
431     }
432     
433     /**
434      * TO BE CALLED FROM {@link #assignTag(BotTagRecord, BotTagRecord)} ONLY!
435      * @param to
436      * @param from
437      */
438     private void removeTag(BotTagRecord<PlayerMessage> from, UnrealId to) {
439     	assert(gameRunning.getFlag());
440     	
441     	if (to == null) to = SERVER_UNREAL_ID;
442     	if (!from.isHasTag()) return;
443     	
444     	from.tagPassed(to);
445     	tagged.remove(from);
446     	
447     	tagStatusChanged(from);
448     	
449     	for (BotTagRecord<PlayerMessage> record : records.values()) {
450 			if (record.getImmunity() == from.getBotId()) {
451 				removeImmunity(record);
452 			}
453 		}
454     }
455     
456     private void addImmunity(BotTagRecord<PlayerMessage> record, UnrealId immuneTo) {
457     	assert(gameRunning.getFlag());
458     	
459     	if (record.getImmunity() != null) {
460     		removeImmunity(record);
461     	}
462     	
463     	record.setImmunity(immuneTo);
464     	
465     	TagPlayerImmunity msg = new TagPlayerImmunity();
466     	msg.setBotId(record.getBotId());
467     	msg.setImmuneFromBotId(record.getImmunity());
468     	msg.setStatus(true);
469     	send(msg);    	
470     }
471     
472     private void removeImmunity(BotTagRecord<PlayerMessage> record) {
473     	assert(gameRunning.getFlag());
474     	
475     	if (record.getImmunity() == null) return;
476     	
477     	TagPlayerImmunity msg = new TagPlayerImmunity();
478     	msg.setBotId(record.getBotId());
479     	msg.setImmuneFromBotId(record.getImmunity());
480     	msg.setStatus(false);
481     	send(msg);
482     	
483     	record.setImmunity(null);
484     }
485     
486     private void tagScoreChanged(BotTagRecord<PlayerMessage> record) {
487     	TagPlayerScoreChanged scoreMsg = new TagPlayerScoreChanged();
488     	scoreMsg.setBotId(record.getBotId());
489     	scoreMsg.setScore(record.getScore());
490     	send(scoreMsg);
491     }
492     
493     private void tagStatusChanged(BotTagRecord<PlayerMessage> record) {
494     	TagPlayerStatusChanged statusMsg = new TagPlayerStatusChanged();
495     	statusMsg.setBotId(record.getBotId());
496     	statusMsg.setTagStatus(record.isHasTag());
497     	send(statusMsg);
498     }
499     
500     private void send(TagMessage message) {
501     	if (gameRunning.getFlag()) {
502     		SendControlMessage command = messages.write(message);
503     		command.setSendAll(true);
504     		getAct().act(command);
505     	}
506     }
507     
508     private void speak(String message) {
509     	if (gameRunning.getFlag()) {
510     		getAct().act(new SendMessage().setGlobal(true).setText("[TAG] " + message));
511     	}
512     }
513     
514     private void resetTagGame() {
515     	gameRunning.setFlag(false);	
516     	tagGameStartUT = -1;
517    	    tagGameLengthUT = -1;
518     	tagGameTimeLeftUT = -1;
519     	
520     	utTimeRunningNext = GAME_RUNNING_PERIOD_SECS;
521     	
522     	tagged.clear();
523     	
524     	Iterator<BotTagRecord<PlayerMessage>> recordIter = records.values().iterator();
525     	while (recordIter.hasNext()) {
526     		BotTagRecord<PlayerMessage> record = recordIter.next();
527     		if (!record.isInGame()) {
528     			recordIter.remove();
529     			continue;
530     		}
531     		record.reset();
532     		record.setInGame(true);
533     	}
534     }    
535 
536 }