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
60
61 private IWorldEventListener<BeginMessage> myBeginMessageListener = new IWorldEventListener<BeginMessage>() {
62 public void notify(BeginMessage event) {
63 timeUpdate(event);
64 }
65 };
66
67
68
69
70 private IWorldEventListener<EndMessage> myEndMessageListener = new IWorldEventListener<EndMessage>() {
71 public void notify(EndMessage event) {
72 batchEnd(event);
73 }
74 };
75
76
77
78
79 private IWorldEventListener<PlayerJoinsGame> myPlayerJoinsGameMessageListener = new IWorldEventListener<PlayerJoinsGame>() {
80 public void notify(PlayerJoinsGame event) {
81 playerJoinsGame(event);
82 }
83 };
84
85
86
87
88 private IWorldEventListener<PlayerLeft> myPlayerLeftMessageListener = new IWorldEventListener<PlayerLeft>() {
89 public void notify(PlayerLeft event) {
90 playerLeft(event);
91 }
92 };
93
94
95
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
118
119
120 @Override
121 protected void init() {
122 super.init();
123
124
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
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
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
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 ||
306 otherBot.isHasTag() ||
307 otherBot.getImmunity() == botHasTag.getBotId()
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
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
380
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
404
405
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
435
436
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 }