Skip to content
This repository has been archived by the owner on Apr 18, 2019. It is now read-only.

[RFR] Setup the multiplayer game #15

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion app/config/routes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,56 @@ index:
path: /
defaults: { _controller: 'App\Controller\MainController::start' }

## GAME

game_new:
path: /game
path: /game/new/{mode}
defaults: { _controller: 'App\Controller\GameController::new' }

game:
path: /game/{id}
defaults: { _controller: 'App\Controller\GameController::play' }

game_join:
path: /game/{id}/join
defaults: { _controller: 'App\Controller\GameController::join' }

game_cancel:
path: /game/{id}/cancel
defaults: { _controller: 'App\Controller\GameController::cancel' }

game_move:
path: /game/{id}/move/{tile}
defaults: { _controller: 'App\Controller\GameController::move' }

## API

api_game_new:
path: /api/game
defaults: { _controller: 'App\Controller\ApiController::new' }
methods: [POST]

api_game:
path: /api/game/{id}
defaults: { _controller: 'App\Controller\ApiController::game' }
methods: [GET]

api_game_cancel:
path: /api/game/{id}
defaults: { _controller: 'App\Controller\ApiController::cancel' }
methods: [DELETE]

api_game_join:
path: /api/game/{id}/join
defaults: { _controller: 'App\Controller\ApiController::join' }
methods: [POST]

api_game_move:
path: /api/game/{id}/move/{tile}
defaults: { _controller: 'App\Controller\ApiController::move' }
methods: [PUT]

api_games:
path: /api/games/open
defaults: { _controller: 'App\Controller\ApiController::games' }
methods: [GET]
29 changes: 18 additions & 11 deletions app/src/Api/GameApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use App\Entity\Game;
use App\Entity\Player;
use App\Api\TokenGenerator;
use App\Api\GameResponse;
use App\Api\MoveResponse;
Expand All @@ -22,38 +23,44 @@ public function __construct(Client $gameApiClient, Serializer $serializer, Token
$this->tokenGenerator = $tokenGenerator;
}

public function new(int $size) : Game {
public function new(int $size, bool $isMultiplayer) : Array {
$response = $this->client->get('/new', [
'query' => 'size=' . $size
]);
$gameResponse = $this->serializer->deserialize($response->getBody(), GameResponse::class, 'json');
return new Game($this->tokenGenerator->generate(), $gameResponse->getInitialGrid(), $gameResponse->getGrid());

$player = new Player($this->tokenGenerator->generate(), $gameResponse->getGrid());
$game = new Game($gameResponse->getInitialGrid(), $isMultiplayer);
$game->setPlayer1($player);
return ['game' => $game, 'player' => $player];
}

public function move(Game $game, int $tile) : Game {
public function move(Game $game, Player $player, int $tile) : Array {
try {
$response = $this->client->post('/move-tile', [
'body' => json_encode([
'InitialGrid' => $game->getResolvedGrid(),
'Grid' => $game->getCurrentGrid(),
'Grid' => $player->getCurrentGrid(),
'TileNumber' => $tile
])
]);
} catch (ConnectException $e) {
return $game;
return ['game' => $game, 'player' => $player];
}

$moveResponse = $this->serializer->deserialize($response->getBody(), MoveResponse::class, 'json');
$game->setCurrentGrid($moveResponse->getGrid());
$game->addTurn();
$game->setIsVictory($moveResponse->getIsVictory());
return $game;
$player->setCurrentGrid($moveResponse->getGrid());
$player->addTurn();
if ($moveResponse->getIsVictory()) {
$game->setWinner($player);
}
return ['game' => $game, 'player' => $player];
}

public function suggest(Game $game) : int {
public function suggest(Game $game, Player $player) : int {
$response = $this->client->get('/suggest', [
'query' => [
'Grid' => json_encode($game->getCurrentGrid()),
'Grid' => json_encode($player->getCurrentGrid()),
'InitialGrid' => json_encode($game->getResolvedGrid())
]]);
$suggestResponse = $this->serializer->deserialize($response->getBody(), SuggestResponse::class, 'json');
Expand Down
22 changes: 16 additions & 6 deletions app/src/ArgumentResolver/GameContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
namespace App\ArgumentResolver;

use App\Entity\Game;
use App\Entity\Player;

class GameContext {

private $game;
private $isOwner;
private $player;
private $isPlayer;

public function __construct() {}

Expand All @@ -17,17 +19,25 @@ public function setGame(Game $game) {
$this->game = $game;
}

public function setIsOwner(bool $isOwner) {
$this->isOwner = $isOwner;
public function setPlayer(Player $player) {
$this->player = $player;
}

public function setIsPlayer(bool $isPlayer) {
$this->isPlayer = $isPlayer;
}

// Getters

public function getGame() : Game {
public function getGame() : ?Game {
return $this->game;
}

public function getIsOwner() : bool {
return $this->isOwner;
public function getPlayer() : ?Player {
return $this->player;
}

public function getIsPlayer() : bool {
return $this->isPlayer;
}
}
13 changes: 10 additions & 3 deletions app/src/ArgumentResolver/GameContextResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
use App\ArgumentResolver\GameContext;
use App\Authentication\CookieAuthManager;
use App\Authentication\TokenAuthManager;
use App\Repository\GameRepository;


class GameContextResolver implements ArgumentValueResolverInterface {
private $gameRepository;

public function __construct(GameRepository $gameRepository) {
$this->gameRepository = $gameRepository;
$this->gameRepository = $gameRepository;
}

public function supports(Request $request, ArgumentMetadata $argument) {
Expand All @@ -22,11 +24,16 @@ public function supports(Request $request, ArgumentMetadata $argument) {

public function resolve(Request $request, ArgumentMetadata $argument) {
$game = $this->gameRepository->findGameById($request->get('id'));
$isOwner = CookieAuthManager::isOwner($request, $game);
$token = $request->headers->get('Authorization');

$isPlayer = $token ? TokenAuthManager::isPlayer($request, $game, $token) : CookieAuthManager::isPlayer($request, $game);
$gameContext = new GameContext();
$gameContext->setGame($game);
$gameContext->setIsOwner($isOwner);
$gameContext->setIsPlayer($isPlayer);
if ($isPlayer) {
$player = CookieAuthManager::getPlayer($request, $game);
$gameContext->setPlayer($player);
}
yield $gameContext;
}
}
18 changes: 12 additions & 6 deletions app/src/Authentication/CookieAuthManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,26 @@
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use App\Authentication\TokenAuthManager;
use App\Entity\Game;
use App\Entity\Player;

class CookieAuthManager {
public const COOKIE_NAME = 'current-puzzle';

public static function isOwner($request, $game) {
return $request->cookies->get(self::COOKIE_NAME) == $game->getToken();

public static function isPlayer(Request $request, Game $game) : bool {
return TokenAuthManager::isPlayer($request, $game, $request->cookies->get(self::COOKIE_NAME));
}

public static function getPlayer(Request $request, Game $game) {
return TokenAuthManager::getPlayer($request, $game, $request->cookies->get(self::COOKIE_NAME));
}

public static function setOwner($response, $game) {
$response->headers->setCookie(new Cookie(self::COOKIE_NAME, $game->getToken()));
public static function setPlayer(Response $response, Player $player) {
$response->headers->setCookie(new Cookie(self::COOKIE_NAME, $player->getToken()));
}

public static function removeOwner($response) {
public static function removePlayer(Response $response) {
$response->headers->clearCookie(self::COOKIE_NAME);
}
}
23 changes: 23 additions & 0 deletions app/src/Authentication/TokenAuthManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace App\Authentication;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use App\Entity\Game;
use App\Entity\Player;

class TokenAuthManager {
public static function isPlayer(Request $request, Game $game, string $token) : bool {
$player1 = $game->getPlayer1();
$player2 = $game->getPlayer2();
return $player1->getToken() == $token || !$game->isFull() && $player2->getToken() == $token;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for === (checks type too)

}

public static function getPlayer(Request $request, Game $game, string $token) {
if ($game->getIsMultiplayer() && $game->getPlayer2() && $game->getPlayer2()->getToken() == $token) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

===

return $game->getPlayer2();
}
return $game->getPlayer1();
}
}
Loading