Private
Public Access
1
0
Files
MineSeeker/src/Mine/SeekerBundle/Topic/MineseekerTopic.php

308 lines
9.5 KiB
PHP

<?php
namespace Mine\SeekerBundle\Topic;
use Doctrine\DBAL\Driver\PDOException;
use Doctrine\ORM\EntityManager;
use Gos\Bundle\WebSocketBundle\Client\ClientManipulatorInterface;
use Gos\Bundle\WebSocketBundle\Topic\TopicInterface;
use Gos\Bundle\WebSocketBundle\Router\WampRequest;
use Mine\SeekerBundle\Entity\Gamer;
use Mine\SeekerBundle\Entity\Step;
use Ratchet\ConnectionInterface;
use Ratchet\Wamp\Topic;
use Symfony\Component\HttpFoundation\RequestStack;
class MineseekerTopic implements TopicInterface
{
/** @var ClientManipulatorInterface */
protected $clientManipulator;
/** @var EntityManager */
protected $em;
/** @var RequestStack */
protected $requestStack;
/**
* MineseekerTopic constructor.
*
* @param $clientManipulator ClientManipulatorInterface
* @param EntityManager $entityManager
* @param RequestStack $requestStack
*/
public function __construct(ClientManipulatorInterface $clientManipulator, EntityManager $entityManager, RequestStack $requestStack)
{
$this->clientManipulator = $clientManipulator;
$this->em = $entityManager;
$this->requestStack = $requestStack;
}
/**
* This will receive any Subscription requests for this topic.
*
* @param ConnectionInterface $connection
* @param Topic $topic
* @param WampRequest $request
* @return void
*/
public function onSubscribe(ConnectionInterface $connection, Topic $topic, WampRequest $request)
{
/** this will broadcast the message to ALL subscribers of this topic. */
$user = $this->clientManipulator->getClient($connection);
$userName = is_string($user) ? $user : $user->getUsername();
/** if more user wants to connect than 2 to one channel */
if ($topic->count() > 2) {
$topic->remove($connection);
} else {
$users = $this->controlUsers($topic, $userName, $user);
$topic->broadcast([
'userTopicId' => $connection->resourceId,
'channel' => $topic->getId(),
'user' => $userName,
'userCnt' => $topic->count(),
'users' => $users
]);
}
}
/**
* This will receive any UnSubscription requests for this topic.
*
* @param ConnectionInterface $connection
* @param Topic $topic
* @param WampRequest $request
* @return void
*/
public function onUnSubscribe(ConnectionInterface $connection, Topic $topic, WampRequest $request)
{
/** this will broadcast the message to ALL subscribers of this topic. */
$topic->broadcast(['msg' => $connection->resourceId . " has left " . $topic->getId()]);
}
/**
* This will receive any Publish requests for this topic.
*
* @param ConnectionInterface $connection
* @param Topic $topic
* @param WampRequest $request
* @param $event
* @param array $exclude
* @param array $eligible
* @return mixed|void
* @internal param Topic $Topic
* @internal param array $eligibles
*/
public function onPublish(ConnectionInterface $connection, Topic $topic, WampRequest $request, $event, array $exclude, array $eligible)
{
$user = $this->clientManipulator->getClient($connection);
$userName = is_string($user) ? $user : $user->getUsername();
/** Save every step by user to db */
if (null === $event['resign']) {
$this->saveStepToDb($topic, $event);
} else {
$this->saveResignToDb($topic, $event['resign']);
}
$topic->broadcast([
'userTopicId' => $connection->resourceId,
'channel' => $topic->getId(),
'user' => $userName,
'userCnt' => $topic->count(),
'data' => $event
]);
}
/**
* Like RPC is will use to prefix the channel
*
* @return string
*/
public function getName()
{
return 'mineseeker.topic';
}
/**
* Save Resign event to database
*
* @param $topic
* @param $color
*/
private function saveResignToDb($topic, $color)
{
$this->reConnect();
$gameAssoc = explode('/', $topic->getId())[2];
$playedGame = $this->em
->getRepository('MineSeekerBundle:PlayedGame')
->findOneByGameAssoc($gameAssoc);
$playedGame->setResign($color);
$this->em->persist($playedGame);
$this->em->flush();
}
/**
* Save steps and point information to database
*
* @param $topic
* @param $event
*/
private function saveStepToDb($topic, $event)
{
$this->reConnect();
$gameAssoc = explode('/', $topic->getId())[2];
$playedGame = $this->em
->getRepository('MineSeekerBundle:PlayedGame')
->findOneByGameAssoc($gameAssoc);
$step = new Step();
$step->setRow($event['coords'][0]);
$step->setCol($event['coords'][1]);
$step->setWBomb($event['bomb']);
$step->setPlayedGame($playedGame);
$step->setCreated(new \DateTime());
$this->em->persist($step);
$playedGame->setBluePoints($event['bluePoints']);
$playedGame->setRedPoints($event['redPoints']);
$playedGame->setBlueExplodedBomb($event['blueExplodedBomb'] ? true : null);
$playedGame->setRedExplodedBomb($event['redExplodedBomb'] ? true : null);
$playedGame->setUpdated(new \DateTime());
$this->em->persist($playedGame);
$this->em->flush();
}
/**
* Control all users in a channel
*
* @param $topic
* @param $userName
* @param $user
* @return array
*/
private function controlUsers($topic, $userName, $user)
{
$this->reConnect();
$gameAssoc = explode('/', $topic->getId())[2];
$playedGame = $this->em
->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 (($one && ($red + $blue === 0)) || ($two && ($red + $blue === 1))) {
/** @var $users {array} w/ save users to database */
$users = $this->saveUserToDb($topic, $userName, $user, $topic->count());
}
return $users;
}
/**
* Save user data to database
*
* @param $topic
* @param $userName
* @param $user
* @param $count
* @return array
*/
private function saveUserToDb($topic, $userName, $user, $count)
{
$this->reConnect();
$gameAssoc = explode('/', $topic->getId())[2];
$playedGame = $this->em
->getRepository('MineSeekerBundle:PlayedGame')
->findOneByGameAssoc($gameAssoc);
/** the user is not anonym */
if (!is_string($user)) {
$FOSUser = $this->em
->getRepository('JotunheimrUserBundle:User')
->findOneByUsername($userName);
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()
? $playedGame->setRed($FOSUser)
: $playedGame->setBlue($FOSUser);
}
} else {
// $request = $this->requestStack->getCurrentRequest(); // TODO nem megy...
$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()
? $playedGame->setRedAnon($anon)
: $playedGame->setBlueAnon($anon);
}
}
$this->em->persist($playedGame);
$this->em->flush();
return $this->getUserCollection($playedGame);
}
/**
* 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() : ''
);
}
/**
* Handle prod MySQL timeout
*/
private function reConnect()
{
try {
$connection = $this->em->getConnection();
if (false === $connection->ping()) {
$connection->close();
$connection->connect();
}
} catch(PDOException $ex) {
throw PDOException::class;
}
}
}