303 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			303 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php namespace MapGuesser\Controller;
 | 
						|
 | 
						|
use DateTime;
 | 
						|
use Faker\Factory;
 | 
						|
use SokoWeb\Interfaces\Authentication\IAuthenticationRequired;
 | 
						|
use SokoWeb\Interfaces\Request\IRequest;
 | 
						|
use SokoWeb\Response\HtmlContent;
 | 
						|
use SokoWeb\Response\JsonContent;
 | 
						|
use SokoWeb\Interfaces\Response\IContent;
 | 
						|
use SokoWeb\Interfaces\Response\IRedirect;
 | 
						|
use MapGuesser\Multi\MultiConnector;
 | 
						|
use MapGuesser\PersistentData\Model\Challenge;
 | 
						|
use MapGuesser\PersistentData\Model\MultiRoom;
 | 
						|
use MapGuesser\PersistentData\Model\PlaceInChallenge;
 | 
						|
use MapGuesser\PersistentData\Model\UserInChallenge;
 | 
						|
use SokoWeb\PersistentData\PersistentDataManager;
 | 
						|
use MapGuesser\Repository\ChallengeRepository;
 | 
						|
use MapGuesser\Repository\MapRepository;
 | 
						|
use MapGuesser\Repository\MultiRoomRepository;
 | 
						|
use MapGuesser\Repository\PlaceRepository;
 | 
						|
use MapGuesser\Repository\UserInChallengeRepository;
 | 
						|
use SokoWeb\Response\Redirect;
 | 
						|
 | 
						|
class GameController implements IAuthenticationRequired
 | 
						|
{
 | 
						|
    const NUMBER_OF_ROUNDS = 5;
 | 
						|
 | 
						|
    private IRequest $request;
 | 
						|
 | 
						|
    private PersistentDataManager $pdm;
 | 
						|
 | 
						|
    private MultiConnector $multiConnector;
 | 
						|
 | 
						|
    private MultiRoomRepository $multiRoomRepository;
 | 
						|
 | 
						|
    private MapRepository $mapRepository;
 | 
						|
 | 
						|
    private PlaceRepository $placeRepository;
 | 
						|
 | 
						|
    private ChallengeRepository $challengeRepository;
 | 
						|
 | 
						|
    private UserInChallengeRepository $userInChallengeRepository;
 | 
						|
 | 
						|
    public function __construct(IRequest $request)
 | 
						|
    {
 | 
						|
        $this->request = $request;
 | 
						|
        $this->pdm = new PersistentDataManager();
 | 
						|
        $this->multiConnector = new MultiConnector();
 | 
						|
        $this->multiRoomRepository = new MultiRoomRepository();
 | 
						|
        $this->mapRepository = new MapRepository();
 | 
						|
        $this->placeRepository = new PlaceRepository();
 | 
						|
        $this->challengeRepository = new ChallengeRepository();
 | 
						|
        $this->userInChallengeRepository = new UserInChallengeRepository();
 | 
						|
    }
 | 
						|
 | 
						|
    public function isAuthenticationRequired(): bool
 | 
						|
    {
 | 
						|
        return empty($_ENV['ENABLE_GAME_FOR_GUESTS']);
 | 
						|
    }
 | 
						|
 | 
						|
    public function getGame(): IContent
 | 
						|
    {
 | 
						|
        $mapId = (int) $this->request->query('mapId');
 | 
						|
 | 
						|
        return new HtmlContent('game', ['mapId' => $mapId]);
 | 
						|
    }
 | 
						|
 | 
						|
    public function getNewMultiGame(): IRedirect
 | 
						|
    {
 | 
						|
        $mapId = (int) $this->request->query('mapId');
 | 
						|
        $map = $this->mapRepository->getById($mapId);
 | 
						|
        $roomId = bin2hex(random_bytes(3));
 | 
						|
        $token = $this->getMultiToken($roomId);
 | 
						|
 | 
						|
        $room = new MultiRoom();
 | 
						|
        $room->setRoomId($roomId);
 | 
						|
        $room->setStateArray([
 | 
						|
            'mapId' => $mapId,
 | 
						|
            'area' => $map->getArea(),
 | 
						|
            'rounds' => [],
 | 
						|
            'currentRound' => -1
 | 
						|
        ]);
 | 
						|
        $room->setMembersArray(['owner' => $token, 'all' => []]);
 | 
						|
        $room->setUpdatedDate(new DateTime());
 | 
						|
 | 
						|
        $this->pdm->saveToDb($room);
 | 
						|
 | 
						|
        $this->multiConnector->sendMessage('create_room', ['roomId' => $roomId]);
 | 
						|
 | 
						|
        return new Redirect(
 | 
						|
            \Container::$routeCollection
 | 
						|
                ->getRoute('multiGame')
 | 
						|
                ->generateLink(['roomId' => $roomId]),
 | 
						|
            IRedirect::TEMPORARY
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    public function getMultiGame(): IContent
 | 
						|
    {
 | 
						|
        $roomId = $this->request->query('roomId');
 | 
						|
 | 
						|
        return new HtmlContent('game', ['roomId' => $roomId]);
 | 
						|
    }
 | 
						|
 | 
						|
    public function getChallenge(): IContent
 | 
						|
    {
 | 
						|
        $challengeToken = $this->request->query('challengeToken');
 | 
						|
 | 
						|
        return new HtmlContent('game', ['challengeToken' => $challengeToken]);
 | 
						|
    }
 | 
						|
 | 
						|
    public function createNewChallenge(): IContent
 | 
						|
    {
 | 
						|
        // create Challenge
 | 
						|
        do {
 | 
						|
            // initiliaze or if a challenge with the same token already exists
 | 
						|
            $challengeToken = mt_rand();
 | 
						|
        } while ($this->challengeRepository->getByToken($challengeToken));
 | 
						|
 | 
						|
        $challenge = new Challenge();
 | 
						|
        $challenge->setToken($challengeToken);
 | 
						|
        $challenge->setCreatedDate(new DateTime());
 | 
						|
 | 
						|
        if ($this->request->post('timerEnabled') !== null && $this->request->post('timeLimit') !== null) {
 | 
						|
            $challenge->setTimeLimit($this->request->post('timeLimit'));
 | 
						|
        }
 | 
						|
        if ($this->request->post('timeLimitType') !== null) {
 | 
						|
            $challenge->setTimeLimitType($this->request->post('timeLimitType'));
 | 
						|
        }
 | 
						|
        if ($this->request->post('noMove') !== null) {
 | 
						|
            $challenge->setNoMove(true);
 | 
						|
        }
 | 
						|
        if ($this->request->post('noPan') !== null) {
 | 
						|
            $challenge->setNoPan(true);
 | 
						|
        }
 | 
						|
        if ($this->request->post('noZoom') !== null) {
 | 
						|
            $challenge->setNoZoom(true);
 | 
						|
        }
 | 
						|
 | 
						|
        $this->pdm->saveToDb($challenge);
 | 
						|
 | 
						|
        // save owner/creator
 | 
						|
 | 
						|
        $session = $this->request->session();
 | 
						|
        $userId = $session->get('userId');
 | 
						|
 | 
						|
        $userInChallenge = new UserInChallenge();
 | 
						|
        $userInChallenge->setUserId($userId);
 | 
						|
        $userInChallenge->setChallenge($challenge);
 | 
						|
        $userInChallenge->setTimeLeft($challenge->getTimeLimit());
 | 
						|
        $userInChallenge->setIsOwner(true);
 | 
						|
 | 
						|
        $this->pdm->saveToDb($userInChallenge);
 | 
						|
 | 
						|
        // select places
 | 
						|
 | 
						|
        $mapId = (int) $this->request->post('mapId');
 | 
						|
        // $map = $this->mapRepository->getById($mapId);
 | 
						|
 | 
						|
        $places = $this->placeRepository->getRandomNPlaces($mapId, static::NUMBER_OF_ROUNDS, $userId);
 | 
						|
 | 
						|
        $round = 0;
 | 
						|
        foreach ($places as $place) {
 | 
						|
            $placeInChallenge = new PlaceInChallenge();
 | 
						|
            $placeInChallenge->setPlace($place);
 | 
						|
            $placeInChallenge->setChallenge($challenge);
 | 
						|
            $placeInChallenge->setRound($round++);
 | 
						|
            $this->pdm->saveToDb($placeInChallenge);
 | 
						|
        }
 | 
						|
 | 
						|
        return new JsonContent(['challengeToken' => dechex($challengeToken)]);
 | 
						|
    }
 | 
						|
 | 
						|
    public function prepareGame(): IContent
 | 
						|
    {
 | 
						|
        $mapId = (int) $this->request->query('mapId');
 | 
						|
        $map = $this->mapRepository->getById($mapId);
 | 
						|
        $session = $this->request->session();
 | 
						|
 | 
						|
        if (!($state = $session->get('state')) || $state['mapId'] !== $mapId) {
 | 
						|
            $session->set('state', [
 | 
						|
                'mapId' => $mapId,
 | 
						|
                'area' => $map->getArea(),
 | 
						|
                'rounds' => [],
 | 
						|
                'currentRound' => -1
 | 
						|
            ]);
 | 
						|
        } else { // update the area of the map in the session in any case
 | 
						|
            $state['area'] = $map->getArea();
 | 
						|
            $session->set('state', $state);
 | 
						|
        }
 | 
						|
 | 
						|
        return new JsonContent([
 | 
						|
            'mapId' => $mapId,
 | 
						|
            'mapName' => $map->getName(),
 | 
						|
            'bounds' => $map->getBounds()->toArray()
 | 
						|
        ]);
 | 
						|
    }
 | 
						|
 | 
						|
    public function prepareMultiGame(): IContent
 | 
						|
    {
 | 
						|
        $roomId = $this->request->query('roomId');
 | 
						|
        $userName = $this->request->post('userName');
 | 
						|
        if (empty($userName)) {
 | 
						|
            $faker = Factory::create();
 | 
						|
            $userName = $faker->userName;
 | 
						|
        }
 | 
						|
 | 
						|
        $room = $this->multiRoomRepository->getByRoomId($roomId);
 | 
						|
 | 
						|
        if (!isset($room)) {
 | 
						|
            return new JsonContent(['error' => 'game_not_found']);
 | 
						|
        }
 | 
						|
 | 
						|
        $state = $room->getStateArray();
 | 
						|
        $map = $this->mapRepository->getById($state['mapId']);
 | 
						|
        $token = $this->getMultiToken($roomId);
 | 
						|
 | 
						|
        $members = $room->getMembersArray();
 | 
						|
 | 
						|
        if (!in_array($token, $members['all'])) {
 | 
						|
            if ($state['currentRound'] >= 0) {
 | 
						|
                return new JsonContent(['error' => 'game_already_started']);
 | 
						|
            }
 | 
						|
 | 
						|
            $members['all'][] = $token;
 | 
						|
        }
 | 
						|
 | 
						|
        $room->setMembersArray($members);
 | 
						|
        $room->setUpdatedDate(new DateTime());
 | 
						|
 | 
						|
        $this->pdm->saveToDb($room);
 | 
						|
 | 
						|
        $this->multiConnector->sendMessage('join_room', [
 | 
						|
            'roomId' => $roomId,
 | 
						|
            'token' => $token,
 | 
						|
            'userName' => $userName
 | 
						|
        ]);
 | 
						|
 | 
						|
        return new JsonContent([
 | 
						|
            'roomId' => $roomId,
 | 
						|
            'token' => $token,
 | 
						|
            'owner' => $members['owner'] == $token,
 | 
						|
            'mapId' => $state['mapId'],
 | 
						|
            'mapName' => $map->getName(),
 | 
						|
            'bounds' => $map->getBounds()->toArray()
 | 
						|
        ]);
 | 
						|
    }
 | 
						|
 | 
						|
    public function prepareChallenge(): IContent
 | 
						|
    {
 | 
						|
        $challengeToken_str = $this->request->query('challengeToken');
 | 
						|
        $session = $this->request->session();
 | 
						|
        $userId = $session->get('userId');
 | 
						|
 | 
						|
        if (!isset($userId))
 | 
						|
        {
 | 
						|
            return new JsonContent(['error' => 'anonymous_user']);
 | 
						|
        }
 | 
						|
 | 
						|
        $challenge = $this->challengeRepository->getByTokenStr($challengeToken_str);
 | 
						|
 | 
						|
        if (!isset($challenge))
 | 
						|
        {
 | 
						|
            return new JsonContent(['error' => 'game_not_found']);
 | 
						|
        }
 | 
						|
 | 
						|
        if (!$this->userInChallengeRepository->isUserParticipatingInChallenge($userId, $challenge)) {
 | 
						|
            // new player is joining
 | 
						|
            $userInChallenge = new UserInChallenge();
 | 
						|
            $userInChallenge->setUserId($userId);
 | 
						|
            $userInChallenge->setChallenge($challenge);
 | 
						|
            $userInChallenge->setTimeLeft($challenge->getTimeLimit());
 | 
						|
            $this->pdm->saveToDb($userInChallenge);
 | 
						|
        }
 | 
						|
 | 
						|
        $map = $this->mapRepository->getByChallenge($challenge);
 | 
						|
 | 
						|
        return new JsonContent([
 | 
						|
            'mapId' => $map->getId(),
 | 
						|
            'mapName' => $map->getName(),
 | 
						|
            'bounds' => $map->getBounds()->toArray()
 | 
						|
        ]);
 | 
						|
    }
 | 
						|
 | 
						|
    private function getMultiToken(string $roomId): string
 | 
						|
    {
 | 
						|
        $session = $this->request->session();
 | 
						|
 | 
						|
        if (!($multiState = $session->get('multiState')) || $multiState['roomId'] !== $roomId) {
 | 
						|
            $token = bin2hex(random_bytes(16));
 | 
						|
 | 
						|
            $session->set('multiState', [
 | 
						|
                'roomId' => $roomId,
 | 
						|
                'token' => $token
 | 
						|
            ]);
 | 
						|
        } else {
 | 
						|
            $token = $multiState['token'];
 | 
						|
        }
 | 
						|
 | 
						|
        return $token;
 | 
						|
    }
 | 
						|
}
 |