From 50b3663889ba7fef535fe1efd5cd8dcfee2876fe Mon Sep 17 00:00:00 2001 From: Lang Date: Wed, 30 Nov 2016 20:15:56 +0100 Subject: [PATCH] ssl handling #22 && reconnection issues #20, #21 --- .../Controller/GameController.php | 6 +- src/Mine/SeekerBundle/Entity/Gamer.php | 31 +++++ src/Mine/SeekerBundle/Entity/Gamer.php~ | 7 + src/Mine/SeekerBundle/Entity/PlayedGame.php~ | 24 ++++ .../Resources/public/js/mine-seeker.js | 3 +- .../Resources/public/js/mine-seeker/app.js | 128 ++++++++++++------ .../Resources/views/Game/index.html.twig | 2 +- .../Resources/views/Game/play.html.twig | 1 + src/Mine/SeekerBundle/Rpc/MineseekerRpc.php | 37 ++++- .../SeekerBundle/Topic/MineseekerTopic.php | 17 ++- 10 files changed, 199 insertions(+), 57 deletions(-) diff --git a/src/Mine/SeekerBundle/Controller/GameController.php b/src/Mine/SeekerBundle/Controller/GameController.php index 9ef5fd2..6542c5f 100644 --- a/src/Mine/SeekerBundle/Controller/GameController.php +++ b/src/Mine/SeekerBundle/Controller/GameController.php @@ -3,6 +3,7 @@ namespace Mine\SeekerBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Component\HttpFoundation\Request; class GameController extends Controller { @@ -11,10 +12,11 @@ class GameController extends Controller return $this->render('MineSeekerBundle:Game:index.html.twig'); } - public function playAction() + public function playAction(Request $request) { return $this->render('MineSeekerBundle:Game:play.html.twig', array( - 'env' => $this->container->getParameter('kernel.environment') + 'env' => $this->container->getParameter('kernel.environment'), + 'ssl' => $request->isSecure() ? 'true' : 'false' )); } diff --git a/src/Mine/SeekerBundle/Entity/Gamer.php b/src/Mine/SeekerBundle/Entity/Gamer.php index 0ddc464..720249e 100644 --- a/src/Mine/SeekerBundle/Entity/Gamer.php +++ b/src/Mine/SeekerBundle/Entity/Gamer.php @@ -74,6 +74,13 @@ class Gamer */ private $userAgent; + /** + * @var \DateTime + * + * @ORM\Column(name="conn_timestamp", type="datetime", nullable=false) + */ + private $connTimestamp; + /** * Get id @@ -180,4 +187,28 @@ class Gamer { return $this->userAgent; } + + /** + * Set connTimestamp + * + * @param \DateTime $connTimestamp + * + * @return Gamer + */ + public function setConnTimestamp($connTimestamp) + { + $this->connTimestamp = $connTimestamp; + + return $this; + } + + /** + * Get connTimestamp + * + * @return \DateTime + */ + public function getConnTimestamp() + { + return $this->connTimestamp; + } } diff --git a/src/Mine/SeekerBundle/Entity/Gamer.php~ b/src/Mine/SeekerBundle/Entity/Gamer.php~ index 0ddc464..19b587a 100644 --- a/src/Mine/SeekerBundle/Entity/Gamer.php~ +++ b/src/Mine/SeekerBundle/Entity/Gamer.php~ @@ -74,6 +74,13 @@ class Gamer */ private $userAgent; + /** + * @var \DateTime + * + * @ORM\Column(name="conn_timestamp", type="datetime", nullable=false) + */ + private $connTimestamp; + /** * Get id diff --git a/src/Mine/SeekerBundle/Entity/PlayedGame.php~ b/src/Mine/SeekerBundle/Entity/PlayedGame.php~ index e987d2f..97193f7 100644 --- a/src/Mine/SeekerBundle/Entity/PlayedGame.php~ +++ b/src/Mine/SeekerBundle/Entity/PlayedGame.php~ @@ -405,4 +405,28 @@ class PlayedGame { return $this->step; } + + /** + * Set resign + * + * @param string $resign + * + * @return PlayedGame + */ + public function setResign($resign) + { + $this->resign = $resign; + + return $this; + } + + /** + * Get resign + * + * @return string + */ + public function getResign() + { + return $this->resign; + } } diff --git a/src/Mine/SeekerBundle/Resources/public/js/mine-seeker.js b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker.js index 8101b6a..3d47443 100644 --- a/src/Mine/SeekerBundle/Resources/public/js/mine-seeker.js +++ b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker.js @@ -4,6 +4,7 @@ import MineSeeker from './mine-seeker/app'; ReactDOM.render( , + gameId={document.getElementById('mine-wrapper').dataset.gameId} + ssl={document.getElementById('mine-wrapper').dataset.ssl}/>, document.getElementById('mine-wrapper') ); diff --git a/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/app.js b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/app.js index 483f67f..102d7bf 100644 --- a/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/app.js +++ b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/app.js @@ -11,13 +11,15 @@ class MineSeeker extends React.Component { this.state = { env: props.env, + ssl: props.ssl, gameInherited: props.gameId !== '', gameAssoc: gameAssoc, channel: channel, session: null, createGrid: false, stepCache: [], - disconnect: false + connectionLost: false, + end: false } } @@ -30,7 +32,12 @@ class MineSeeker extends React.Component { return text; } - /** STEP */ + /** + * STEP + * + * @param coords + * @returns {{red: *, blue: *}} + */ makePointsCalcAndStep(coords) { let users = this.refs.gridControl.refs.userControl, activePlayer = users.state.activePlayer ? 'blue' : 'red', @@ -51,7 +58,38 @@ class MineSeeker extends React.Component { return {red: redPoints, blue: bluePoints}; } - /** THE END */ + /** + * START + * + * @param payload + */ + makeGameStart(payload) { + /** every time the blue starts */ + this.refs.gridControl.refs.userControl.setState({activePlayer: 1}); + + /** Set up player names w/ server data */ + this.refs.gridControl.refs.userControl.refs.red.setState({ + name: payload.users.red !== '' ? payload.users.red : payload.users.redAnon, + }); + + this.refs.gridControl.refs.userControl.refs.blue.setState({ + name: payload.users.blue !== '' ? payload.users.blue : payload.users.blueAnon, + desc: this.refs.gridControl.state.webPlayer === 'blue' + ? this.refs.gridControl.state.desc.you + : this.refs.gridControl.state.desc.buddy, + active: true, + }); + + this.refs.gridControl.setState({overlay: false}); + } + + /** + * THE END + * + * @param bluePoints + * @param redPoints + * @param resign + */ makeGameEndIfItEnds(bluePoints, redPoints, resign = false) { let redWins = redPoints > 25, blueWins = bluePoints > 25; @@ -86,10 +124,13 @@ class MineSeeker extends React.Component { : "You WIN!" }); + this.setState({end: true}); + this.makeGameEndIfItEnds(0, 0, true); } clickResign() { + /** PUBLISH */ this.state.session.publish(this.state.channel, { 'resign': this.refs.gridControl.refs.userControl.state.activePlayer ? 'blue' : 'red' }); @@ -119,13 +160,13 @@ class MineSeeker extends React.Component { } } - wInit(session, gridServer, gridClient) { + wInit(session, data, gridClient) { this.setState({session: session}); /** save session to GridControl */ /** render grid fields - #12 */ this.refs.gridControl.setState({ - grid: this.state.gameInherited ? JSON.parse(Base64.decode(gridServer)) : gridClient, + grid: this.state.gameInherited ? JSON.parse(Base64.decode(data))['grid'] : gridClient, channel: this.state.channel, desc: { buddy:
@@ -157,39 +198,31 @@ class MineSeeker extends React.Component { }); } - wSubscribe(payload) { + wSubscribe(payload, rpcUsers = null) { this.state.env === 'dev' && console.info( (typeof payload.user !== 'undefined' ? payload.user : 'user') + " has been subscribed to the channel!" ); - if (!this.state.disconnect) { - /** setup the web player */ - null === this.refs.gridControl.state.webPlayer && this.refs.gridControl.setState({ - webPlayer: payload.user === payload.users.blue || payload.user === payload.users.blueAnon - ? 'blue' - : 'red' - }); + let firstUser = !rpcUsers; - /** every user has been came */ - if (payload.userCnt === 2) { - /** every time the blue starts */ - this.refs.gridControl.refs.userControl.setState({activePlayer: 1}); + this.refs.gridControl.state.webPlayer === null && this.refs.gridControl.setState({ + webPlayer: payload.user === payload.users.blue || + ( + firstUser && payload.users.blueAnon !== '' || + !firstUser && (rpcUsers.blueAnon === '' && rpcUsers.blue === '') + ) + ? 'blue' : 'red' + }); - /** Set up player names w/ server data */ - this.refs.gridControl.refs.userControl.refs.red.setState({ - name: payload.users.red !== '' ? payload.users.red : payload.users.redAnon, - }); - - this.refs.gridControl.refs.userControl.refs.blue.setState({ - name: payload.users.blue !== '' ? payload.users.blue : payload.users.blueAnon, - desc: this.refs.gridControl.state.webPlayer === 'blue' - ? this.refs.gridControl.state.desc.you - : this.refs.gridControl.state.desc.buddy, - active: true, - }); - - this.refs.gridControl.setState({overlay: false}); - } + /** every user has been came */ + if ( + payload.userCnt === 2 && + ( + !this.state.connectionLost || + this.state.connectionLost && false === this.refs.gridControl.refs.userControl.state.activePlayer && !this.state.end + ) + ) { + this.makeGameStart(payload); } } @@ -226,7 +259,7 @@ class MineSeeker extends React.Component { } /** Connect - Subscribe */ - subscribe() { + subscribe(rpcUsers = null) { this.state.session.subscribe( this.state.channel, (uri, payload, log) => { @@ -238,30 +271,31 @@ class MineSeeker extends React.Component { this.wTopic(payload); } else { if (isNotUnsubscribe) { - this.wSubscribe(payload); + this.wSubscribe(payload, rpcUsers); } else { this.wUnsubscribe(payload); } } /** RECONNECTION */ - if (payload.userCnt === 2 && this.state.disconnect) { + if (payload.userCnt === 2 && this.state.connectionLost) { this.state.env === 'dev' && console.info('Reconnection process'); + /** PUBLISH */ let cache = this.state.stepCache; cache.forEach((item) => this.state.session.publish(this.state.channel, item)); - this.setState({disconnect: false, stepCache: []}); + this.setState({connectionLost: false, stepCache: []}); } }); } - /** after rendering */ + /** After rendering */ componentDidMount() { /** Create Websocket w/ Bahnhof.js */ let websocket = WS.connect( this.state.env === 'dev' ? "ws://mine.dev:6450" - : "ws://www.mineseeker.ninja:6450" + : (this.state.ssl === 'true' ? "wss" : "ws") + "://www.mineseeker.ninja:6450" ); /** @@ -271,7 +305,7 @@ class MineSeeker extends React.Component { websocket.on("socket/connect", (session) => { this.state.env === 'dev' && console.info("Successfully connected to the Server!"); - if (!this.state.disconnect) { + if (!this.state.connectionLost) { let gridClient = this.state.gameInherited || new Grid().state.grid; /** @@ -284,11 +318,11 @@ class MineSeeker extends React.Component { this.state.gameInherited ? this.state.gameAssoc : [Base64.encode(JSON.stringify(gridClient)), this.state.gameAssoc] ) .then( - (gridServer) => { - this.state.env === 'dev' && console.info("Grid has been created! Return w/ gameAssoc."); + (data) => { + this.state.env === 'dev' && console.info('RPC has been called'); - this.wInit(session, gridServer, gridClient); - this.subscribe(); + this.wInit(session, data, gridClient); + this.subscribe(this.state.gameInherited && JSON.parse(Base64.decode(data))['users']); }, (error, desc) => this.state.env === 'dev' && console.error(["RPC Error", error, desc]) ); @@ -304,7 +338,11 @@ class MineSeeker extends React.Component { */ websocket.on("socket/disconnect", (error) => { this.state.env === 'dev' && console.error("Disconnected for " + error.reason + " with code " + error.code); - this.setState({disconnect: true}); + + error.code === 6 && this.setState({connectionLost: true}); + error.code === 3 && setTimeout(function () { + this.componentDidMount(); + }.bind(this), 500); }); } @@ -344,7 +382,7 @@ class MineSeeker extends React.Component { }; /** PUBLISH */ - !this.state.disconnect + !this.state.connectionLost ? this.state.session.publish(this.state.channel, dataPack) : this.cachePublish(dataPack); } diff --git a/src/Mine/SeekerBundle/Resources/views/Game/index.html.twig b/src/Mine/SeekerBundle/Resources/views/Game/index.html.twig index 7d4da67..46a11ee 100644 --- a/src/Mine/SeekerBundle/Resources/views/Game/index.html.twig +++ b/src/Mine/SeekerBundle/Resources/views/Game/index.html.twig @@ -2,7 +2,7 @@ {% block header %}

MineSeeker

-

version 1.0a

+

version 1.1.3a

Play now {% endblock %} diff --git a/src/Mine/SeekerBundle/Resources/views/Game/play.html.twig b/src/Mine/SeekerBundle/Resources/views/Game/play.html.twig index 9335756..f99498f 100644 --- a/src/Mine/SeekerBundle/Resources/views/Game/play.html.twig +++ b/src/Mine/SeekerBundle/Resources/views/Game/play.html.twig @@ -4,6 +4,7 @@
diff --git a/src/Mine/SeekerBundle/Rpc/MineseekerRpc.php b/src/Mine/SeekerBundle/Rpc/MineseekerRpc.php index 5987288..9a3a50b 100644 --- a/src/Mine/SeekerBundle/Rpc/MineseekerRpc.php +++ b/src/Mine/SeekerBundle/Rpc/MineseekerRpc.php @@ -56,7 +56,12 @@ class MineseekerRpc implements RpcInterface */ public function connectGame(ConnectionInterface $connection, WampRequest $request, array $params) { - return base64_encode(json_encode($this->getGrid($params))); + return base64_encode(json_encode( + array( + 'grid' => $this->getGrid($params), + 'users' => $this->getUsers($params) + ) + )); } /** @@ -82,6 +87,36 @@ class MineseekerRpc implements RpcInterface return $getsee; } + /** + * @param $gameAssoc + * @return array + */ + private function getUsers($gameAssoc) + { + $this->reConnect(); + return $this->getUserCollection( + $this->em + ->getRepository('MineSeekerBundle:PlayedGame') + ->findOneByGameAssoc($gameAssoc) + ); + } + + /** + * Get user collection from PlayedGame entity + * + * @param $playedGame + * @return array + */ + private function getUserCollection($playedGame) + { + return array( + 'red' => null !== $playedGame->getRed() ? $playedGame->getRed()->getUsername() : '', + 'blue' => null !== $playedGame->getBlue() ? $playedGame->getBlue()->getUsername() : '', + 'redAnon' => null !== $playedGame->getRedAnon() ? $playedGame->getRedAnon()->getUserName() : '', + 'blueAnon' => null !== $playedGame->getBlueAnon() ? $playedGame->getBlueAnon()->getUserName() : '' + ); + } + /** * @param $data * @return boolean diff --git a/src/Mine/SeekerBundle/Topic/MineseekerTopic.php b/src/Mine/SeekerBundle/Topic/MineseekerTopic.php index 08bb61e..bbb4d25 100644 --- a/src/Mine/SeekerBundle/Topic/MineseekerTopic.php +++ b/src/Mine/SeekerBundle/Topic/MineseekerTopic.php @@ -194,14 +194,17 @@ class MineseekerTopic implements TopicInterface ->getRepository('MineSeekerBundle:PlayedGame') ->findOneByGameAssoc($gameAssoc); + /** @var $users {array} */ $users = $this->getUserCollection($playedGame); + $red = "" !== $users['red'] || "" !== $users['redAnon'] ? 1 : 0; + $blue = "" !== $users['blue'] || "" !== $users['blueAnon'] ? 1 : 0; + $one = $topic->count() === 1; + $two = $topic->count() === 2; + /** This checks it is a reconnection */ - if ( - (null !== $users['red'] || null !== $users['redAnon']) && - (null !== $users['blue'] || null !== $users['blueAnon']) - ) { - /** @var $users array Save users to database */ + if (($one && ($red + $blue === 0)) || ($two && ($red + $blue === 1))) { + /** @var $users {array} w/ save users to database */ $users = $this->saveUserToDb($topic, $userName, $user, $topic->count()); } @@ -226,6 +229,7 @@ class MineseekerTopic implements TopicInterface ->getRepository('MineSeekerBundle:PlayedGame') ->findOneByGameAssoc($gameAssoc); + /** the user is not anonym */ if (!is_string($user)) { $FOSUser = $this->em ->getRepository('JotunheimrUserBundle:User') @@ -234,7 +238,6 @@ class MineseekerTopic implements TopicInterface if ($count == 1) { /** @var $random {integer} Active player: red: 0, blue: 1 */ $random = rand(0, 1); - !$random ? $playedGame->setRed($FOSUser) : $playedGame->setBlue($FOSUser); } else { null === $playedGame->getRed() && null === $playedGame->getRedAnon() @@ -246,12 +249,12 @@ class MineseekerTopic implements TopicInterface $anon = new Gamer(); $anon->setUserName($userName); + $anon->setConnTimestamp(new \DateTime()); $this->em->persist($anon); if ($count == 1) { /** @var $random {integer} Active player: red: 0, blue: 1 */ $random = rand(0, 1); - !$random ? $playedGame->setRedAnon($anon) : $playedGame->setBlueAnon($anon); } else { null === $playedGame->getRed() && null === $playedGame->getRedAnon()