1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace ManiaLive\Gui;
13:
14: use ManiaLib\Gui\Elements\Bgs1;
15: use ManiaLive\Event\Dispatcher;
16: use ManiaLive\Application\Listener as AppListener;
17: use ManiaLive\Application\Event as AppEvent;
18: use ManiaLive\Data\Listener as PlayerListener;
19: use ManiaLive\Data\Event as PlayerEvent;
20: use ManiaLive\DedicatedApi\Callback\Listener as ServerListener;
21: use ManiaLive\DedicatedApi\Callback\Event as ServerEvent;
22: use DedicatedApi\Connection;
23: use DedicatedApi\Structures\Status;
24: use ManiaLive\Data\Storage;
25: use ManiaLive\Gui\Windows\Info;
26: use ManiaLive\Gui\Windows\Shortkey;
27: use ManiaLive\Gui\Windows\Thumbnail;
28:
29: 30: 31:
32: final class GuiHandler extends \ManiaLib\Utils\Singleton implements AppListener, PlayerListener, ServerListener
33: {
34: const MAX_THUMBNAILS = 5;
35: const NEXT_IS_MODAL = 0xFA15EADD;
36:
37: 38: 39:
40: private $connection;
41:
42: private $hidingGui = array();
43: private $modals = array();
44: private $modalsRecipients = array();
45: private $modalShown = array();
46: private $managedWindow = array();
47: private $thumbnails = array();
48:
49: private $currentWindows = array();
50: private $nextWindows = array();
51: private $modalBg;
52:
53: private $groupAll;
54: private $groupAllLogin;
55: private $groupPlayers;
56: private $groupSpectators;
57:
58: private $nextLoop;
59:
60:
61: private $sendingTimes = array();
62: private $averageSendingTimes;
63:
64: protected function __construct()
65: {
66: $this->modalBg = new Bgs1(340, 200);
67: $this->modalBg->setSubStyle(Bgs1::BgDialogBlur);
68: $this->modalBg->setAlign('center', 'center');
69: $this->modalBg->setPosZ(Window::Z_MODAL);
70: $this->modalBg->setScriptEvents();
71: $this->nextLoop = microtime(true);
72: Dispatcher::register(AppEvent::getClass(), $this, AppEvent::ALL & ~AppEvent::ON_POST_LOOP);
73: Dispatcher::register(PlayerEvent::getClass(), $this, PlayerEvent::ON_PLAYER_CHANGE_SIDE);
74: Dispatcher::register(ServerEvent::getClass(), $this, ServerEvent::ON_PLAYER_CONNECT | ServerEvent::ON_PLAYER_DISCONNECT);
75:
76: $config = \ManiaLive\DedicatedApi\Config::getInstance();
77: $this->connection = Connection::factory($config->host, $config->port, $config->timeout, $config->user, $config->password);
78: }
79:
80: function getAverageSendingTimes()
81: {
82: return $this->averageSendingTimes;
83: }
84:
85: function toggleGui($login)
86: {
87: $this->hidingGui[$login] = !$this->hidingGui[$login];
88: if($this->hidingGui[$login])
89: {
90: $this->connection->chatSendServerMessage('ManiaLive interface has been deactivated, press F8 to enable...', $login, true);
91: $this->connection->sendHideManialinkPage($login, true);
92: Manialinks::load();
93: $this->drawWindow(Shortkey::Create($login));
94: CustomUI::Create($login)->saveToDefault();
95: $this->connection->sendDisplayManialinkPage($login, Manialinks::getXml(), 0, false, true);
96: $this->connection->executeMulticall();
97: }
98: else
99: {
100: Manialinks::load();
101: foreach($this->currentWindows as $visibilityByLogin)
102: if(isset($visibilityByLogin[$login]))
103: $this->drawWindow($visibilityByLogin[$login]);
104: if($this->modalShown[$login])
105: $this->drawModal($this->modalShown[$login]);
106: $this->drawWindow(Shortkey::Create($login));
107: CustomUI::Create($login)->save();
108: $this->connection->sendDisplayManialinkPage($login, Manialinks::getXml(), 0, false);
109: }
110: }
111:
112: function addtoShow(Window $window, $recipients)
113: {
114: $windowId = $window->getId();
115:
116: if($window instanceof ManagedWindow)
117: {
118: if($this->managedWindow[$recipients[0]] && $this->managedWindow[$recipients[0]] !== $window && !$this->sendToTaskbar($recipients[0]))
119: return;
120: $this->managedWindow[$recipients[0]] = $window;
121: if(($thumbnail = $this->getThumbnail($window)))
122: $thumbnail->hide();
123: }
124:
125: foreach($recipients as $login)
126: {
127: if(isset($this->nextWindows[$windowId]))
128: $this->nextWindows[$windowId][$login] = $window;
129: else
130: $this->nextWindows[$windowId] = array($login => $window);
131: }
132: }
133:
134: function addToHide(Window $window, $recipients)
135: {
136: $windowId = $window->getId();
137:
138: if($window instanceof ManagedWindow && $this->managedWindow[$recipients[0]] === $window)
139: $this->managedWindow[$recipients[0]] = null;
140:
141: if(isset($this->currentWindows[$windowId]))
142: {
143: foreach($recipients as $login)
144: {
145: if(isset($this->currentWindows[$windowId][$login]))
146: {
147: if(isset($this->nextWindows[$windowId]))
148: $this->nextWindows[$windowId][$login] = false;
149: else
150: $this->nextWindows[$windowId] = array($login => false);
151: }
152: else if(isset($this->nextWindows[$windowId]))
153: {
154: unset($this->nextWindows[$windowId][$login]);
155: if(!$this->nextWindows[$windowId])
156: unset($this->nextWindows[$windowId]);
157: }
158: }
159: }
160: else if(isset($this->modalsRecipients[$windowId]))
161: {
162: foreach($recipients as $login)
163: {
164: if(isset($this->modalShown[$login]) && $this->modalShown[$login] === $window)
165: {
166: if(isset($this->nextWindows[$windowId]))
167: $this->nextWindows[$windowId][$login] = false;
168: else
169: $this->nextWindows[$windowId] = array($login => false);
170: }
171: }
172: }
173: else
174: unset($this->nextWindows[$windowId]);
175: }
176:
177: function addToRedraw(Window $window, $recipients)
178: {
179: $windowId = $window->getId();
180:
181: if($window instanceof ManagedWindow && ($thumbnail = $this->getThumbnail($window)))
182: $thumbnail->enableHighlight();
183: else if(isset($this->currentWindows[$windowId]))
184: {
185: foreach($recipients as $login)
186: if(isset($this->currentWindows[$windowId][$login]))
187: {
188: if(isset($this->nextWindows[$windowId]))
189: {
190: if(!isset($this->nextWindows[$windowId][$login]))
191: $this->nextWindows[$windowId][$login] = $window;
192: }
193: else
194: $this->nextWindows[$windowId] = array($login => $window);
195: }
196: }
197: }
198:
199: function sendToTaskbar($login)
200: {
201: $window = $this->managedWindow[$login];
202:
203: $taskbarIndex = 0;
204: $freePlaceFound = false;
205: foreach($this->thumbnails[$login] as $taskbarIndex => $placedThumbnail)
206: if(!$placedThumbnail)
207: {
208: $freePlaceFound = true;
209: break;
210: }
211: if(!$freePlaceFound)
212: {
213: if($taskbarIndex == self::MAX_THUMBNAILS - 1)
214: {
215: $info = Info::Create($login, false);
216: $info->setSize(40, 25);
217: $info->setTitle('Too many Windows!');
218: $info->setText("You are in the process of minimizing another window ...\n".
219: "Due to restricted resources you have reached the limit of allowed concurrent displayable minimized windows.\n".
220: "Please close some old windows in order to be able to open and minimize new ones.");
221: $this->addModal($info);
222: return false;
223: }
224: else
225: $taskbarIndex = count($this->thumbnails[$login]);
226: }
227:
228:
229: $thumbnail = Thumbnail::Create($login, false, $window);
230: $this->thumbnails[$login][$taskbarIndex] = $thumbnail;
231: $thumbnail->setSize(30, 26);
232: $thumbnail->setPosition(80 - 31 * $taskbarIndex, 85);
233: $thumbnail->addCloseCallback(array($this, 'onThumbnailClosed'));
234: $thumbnail->show();
235: $window->hide();
236: $this->managedWindow[$login] = null;
237:
238: return true;
239: }
240:
241: function onThumbnailClosed($login, Thumbnail $thumbnail)
242: {
243: $taskbarIndex = array_search($thumbnail, $this->thumbnails[$login], true);
244: if($taskbarIndex !== false)
245: $this->thumbnails[$login][$taskbarIndex] = false;
246: $thumbnail->destroy();
247: }
248:
249: private function getNextModal($login)
250: {
251: if($this->modalShown[$login])
252: return null;
253: return array_shift($this->modals[$login]);
254: }
255:
256: function addModal(Window $modal, $recipients)
257: {
258: foreach($recipients as $login)
259: {
260: if(isset($this->modals[$login]) && !isset($this->modalsRecipients[$modal->getId()][$login]))
261: {
262: $this->modals[$login][] = $modal;
263: $this->modalsRecipients[$modal->getId()][$login] = true;
264: }
265: }
266:
267: $modal->addCloseCallback(array($this, 'onModalClosed'));
268: }
269:
270: function onModalClosed($login, Window $window)
271: {
272: $windowId = $window->getId();
273: unset($this->modalsRecipients[$windowId][$login]);
274: if(empty($this->modalsRecipients[$windowId]))
275: {
276: $window->destroy();
277: unset($this->modalsRecipients[$windowId]);
278: }
279: $this->modalShown[$login] = null;
280: }
281:
282: function getThumbnail(ManagedWindow $window)
283: {
284: $login = $window->getRecipient();
285: if(isset($this->thumbnails[$login]))
286: foreach($this->thumbnails[$login] as $thumbnail)
287: if($thumbnail && $thumbnail->getWindow() === $window)
288: return $thumbnail;
289: return null;
290: }
291:
292:
293:
294: function onInit()
295: {
296: if(Storage::getInstance()->serverStatus->code > Status::LAUNCHING)
297: $this->connection->sendHideManialinkPage();
298:
299: $this->groupAll = Group::Create('all');
300: $this->groupPlayers = Group::Create('players');
301: $this->groupSpectators = Group::Create('spectators');
302: }
303:
304: function onRun()
305: {
306: foreach(Storage::getInstance()->players as $login => $player)
307: $this->onPlayerConnect($login, false);
308:
309: foreach(Storage::getInstance()->spectators as $login => $spectator)
310: $this->onPlayerConnect($login, true);
311: }
312:
313: function onPreLoop()
314: {
315:
316: if(Storage::getInstance()->serverStatus->code <= Status::LAUNCHING)
317: return;
318:
319: $startTime = microtime(true);
320: if($startTime < $this->nextLoop)
321: return;
322:
323: $stackByPlayer = array();
324: $playersOnServer = array_merge(array_keys(Storage::getInstance()->players), array_keys(Storage::getInstance()->spectators));
325: $playersHidingGui = array_keys(array_filter($this->hidingGui));
326: $playersShowingGui = array_intersect(array_diff(array_keys($this->hidingGui), $playersHidingGui), $playersOnServer);
327:
328: foreach($this->nextWindows as $windowId => $visibilityByLogin)
329: {
330: $showing = array_diff(array_keys(array_filter($visibilityByLogin)), $playersHidingGui);
331: $hiding = array_diff(array_keys($visibilityByLogin), $showing, $playersHidingGui);
332: if(count($showing))
333: {
334: sort($showing);
335: $stackByPlayer[implode(',', $showing)][] = $visibilityByLogin[reset($showing)];
336: }
337: if(count($hiding))
338: {
339: sort($hiding);
340: $stackByPlayer[implode(',', $hiding)][] = $windowId;
341: }
342: }
343:
344: $loginsByDiff = array();
345: $customUIsByDiff = array();
346: foreach($playersShowingGui as $login)
347: {
348: $modal = $this->getNextModal($login);
349: if($modal)
350: {
351: $stackByPlayer[$login][] = self::NEXT_IS_MODAL;
352: $stackByPlayer[$login][] = $modal;
353: $this->modalShown[$login] = $modal;
354: }
355:
356: $customUI = CustomUI::Create($login);
357: $diff = $customUI->getDiff();
358: if($diff)
359: {
360: $loginsByDiff[$diff][] = $login;
361: $customUIsByDiff[$diff][] = $customUI;
362: }
363: }
364:
365: foreach($loginsByDiff as $diff => $logins)
366: $stackByPlayer[implode(',', $logins)][] = $customUIsByDiff[$diff];
367:
368:
369: $nextIsModal = false;
370: foreach($stackByPlayer as $login => $data)
371: {
372: Manialinks::load();
373: foreach($data as $toDraw)
374: {
375: if($nextIsModal)
376: {
377: $this->drawModal($toDraw);
378: $nextIsModal = false;
379: }
380: else if($toDraw === self::NEXT_IS_MODAL)
381: $nextIsModal = true;
382: else if(is_string($toDraw))
383: $this->drawHidden($toDraw);
384: else if(is_array($toDraw))
385: {
386: array_shift($toDraw)->save();
387: foreach($toDraw as $customUI)
388: $customUI->hasBeenSaved();
389: }
390: else
391: {
392: $this->drawWindow($toDraw);
393: }
394: }
395: $this->connection->sendDisplayManialinkPage($login == $this->groupAllLogin ? null : $login, Manialinks::getXml(), 0, false, true);
396: }
397: $this->connection->executeMulticall();
398:
399:
400: foreach($this->nextWindows as $windowId => $visibilityByLogin)
401: {
402: if(isset($this->currentWindows[$windowId]))
403: $newCurrent = array_filter(array_merge($this->currentWindows[$windowId], $visibilityByLogin));
404: else
405: $newCurrent = array_filter($visibilityByLogin);
406:
407: if($newCurrent)
408: $this->currentWindows[$windowId] = $newCurrent;
409: else
410: unset($this->currentWindows[$windowId]);
411: }
412: $this->nextWindows = array();
413:
414:
415: $endTime = microtime(true);
416: do
417: {
418: $this->nextLoop += 0.2;
419: } while($this->nextLoop < $endTime);
420:
421: $this->sendingTimes[] = $endTime - $startTime;
422: if (count($this->sendingTimes) >= 10)
423: {
424: $this->averageSendingTimes = array_sum($this->sendingTimes) / count($this->sendingTimes);
425: $this->sendingTimes = array();
426: }
427: }
428:
429: final private function drawWindow(Window $window)
430: {
431: if($window instanceof ManagedWindow && $window->isMaximized())
432: $window->setPosZ(Window::Z_MAXIMIZED);
433: else
434: $window->setPosZ($window->getMinZ());
435:
436: Manialinks::beginManialink($window->getId());
437: $window->save();
438: Manialinks::endManialink();
439: }
440:
441: final private function drawModal(Window $window)
442: {
443: $window->setPosZ(Window::Z_MODAL + Window::Z_OFFSET);
444:
445: Manialinks::beginManialink($window->getId());
446: $this->modalBg->save();
447: $window->save();
448: Manialinks::endManialink();
449: }
450:
451: final private function drawHidden($windowId)
452: {
453: Manialinks::beginManialink($windowId);
454: Manialinks::endManialink();
455: }
456:
457: function onPostLoop() {}
458:
459: function onTerminate()
460: {
461: if(Storage::getInstance()->serverStatus->code > Status::LAUNCHING)
462: $this->connection->sendHideManialinkPage();
463: }
464:
465:
466:
467: function onPlayerNewBestTime($player, $oldBest, $newBest) {}
468: function onPlayerNewRank($player, $oldRank, $newRank) {}
469: function onPlayerNewBestScore($player, $oldScore, $newScore) {}
470:
471: function onPlayerChangeSide($player, $oldSide)
472: {
473: if($player->spectator)
474: {
475: $this->groupPlayers->remove($player->login);
476: $this->groupSpectators->add($player->login, true);
477: }
478: else
479: {
480: $this->groupSpectators->remove($player->login);
481: $this->groupPlayers->add($player->login, true);
482: }
483: }
484:
485: function onPlayerFinishLap($player, $timeOrScore, $checkpoints, $nbLap) {}
486:
487:
488:
489: function onPlayerConnect($login, $isSpectator)
490: {
491: $this->hidingGui[$login] = false;
492: $this->modals[$login] = array();
493: $this->modalShown[$login] = null;
494: $this->managedWindow[$login] = null;
495: $this->thumbnails[$login] = array();
496:
497: $sk = Shortkey::Create($login);
498: $sk->addCallback(Shortkey::F8, array($this, 'toggleGui'));
499: $sk->show();
500:
501: $this->groupAll->add($login, true);
502: $allLogins = $this->groupAll->toArray();
503: sort($allLogins);
504: $this->groupAllLogin = implode(',', $allLogins);
505: if($isSpectator)
506: $this->groupSpectators->add($login, true);
507: else
508: $this->groupPlayers->add($login, true);
509: }
510:
511: function onPlayerDisconnect($login)
512: {
513: $this->groupAll->remove($login);
514: $this->groupPlayers->remove($login);
515: $this->groupSpectators->remove($login);
516:
517: Window::Erase($login);
518: CustomUI::Erase($login);
519:
520: foreach($this->modals[$login] as $dialog)
521: $this->onModalClosed($login, $dialog);
522: if($this->modalShown[$login])
523: $this->onModalClosed($login, $this->modalShown[$login]);
524:
525: unset($this->hidingGui[$login]);
526: unset($this->modals[$login]);
527: unset($this->modalShown[$login]);
528: unset($this->managedWindow[$login]);
529: unset($this->thumbnails[$login]);
530: }
531:
532: function onBeginMap($map, $warmUp, $matchContinuation) {}
533: function onBeginMatch() {}
534: function onBeginRound() {}
535: function onBillUpdated($billId, $state, $stateName, $transactionId) {}
536: function onMapListModified($curMapIndex, $nextMapIndex, $isListModified) {}
537: function onEcho($internal, $public) {}
538: function onEndMap($rankings, $map, $wasWarmUp, $matchContinuesOnNextMap, $restartMap) {}
539: function onEndMatch($rankings, $winnerTeamOrMap) {}
540: function onEndRound() {}
541: function onManualFlowControlTransition($transition) {}
542: function onPlayerChat($playerUid, $login, $text, $isRegistredCmd) {}
543: function onPlayerCheckpoint($playerUid, $login, $timeOrScore, $curLap, $checkpointIndex) {}
544: function onPlayerFinish($playerUid, $login, $timeOrScore) {}
545: function onPlayerIncoherence($playerUid, $login) {}
546: function onPlayerInfoChanged($playerInfo) {}
547: function onPlayerManialinkPageAnswer($playerUid, $login, $answer, array $entries) {}
548: function onServerStart() {}
549: function onServerStop() {}
550: function onStatusChanged($statusCode, $statusName) {}
551: function onTunnelDataReceived($playerUid, $login, $data) {}
552: function onVoteUpdated($stateName, $login, $cmdName, $cmdParam) {}
553: function onModeScriptCallback($param1, $param2) {}
554: }
555:
556: ?>