Compare commits
12 Commits
v2026.2.5-
...
userlist
| Author | SHA1 | Date | |
|---|---|---|---|
| ae8794f564 | |||
| 2f3062bf33 | |||
| 0e86f7d4aa | |||
| 8851a6d68b | |||
| 94ebe9a428 | |||
| 4a94855be9 | |||
| a8ac13850b | |||
| fe5378f69b | |||
| e8691f29bb | |||
| fcd490ef4e | |||
| a3465f6cf9 | |||
| a91172ef7a |
2
.babelrc
2
.babelrc
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"presets": ["es2015", "react"]
|
||||
"presets": ["es2015", "es2016", "es2017", "react"]
|
||||
}
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -35,6 +35,5 @@ phpunit-report/*
|
||||
/src/Mine/SeekerBundle/Resources/public/js/src/
|
||||
|
||||
nohup.out
|
||||
src/Mine/SeekerBundle/Resources/public/js/index.js
|
||||
src/Mine/SeekerBundle/Resources/public/js/index.min.js
|
||||
src/Mine/SeekerBundle/Resources/public/js/build/
|
||||
npm-debug.log
|
||||
|
||||
@@ -15,7 +15,7 @@ This is a Symfony 3 project w/ React JS in standalone mode and w/ WebSocket.
|
||||
|
||||
1.) Backend WebSocket server start as daemon - GeniusesOfSymfony/WebSocketBundle
|
||||
|
||||
$ nohup bin/console gos:websocket:server &
|
||||
$ nohup php bin/console gos:websocket:server --env=prod &
|
||||
|
||||
2.) React JS WebPack watch generator w/ babel presets: es2015, react
|
||||
|
||||
@@ -32,4 +32,9 @@ This is a Symfony 3 project w/ React JS in standalone mode and w/ WebSocket.
|
||||
|
||||
3.) Connect to Prod
|
||||
|
||||
ssh xxsvci@laszlolang.com -i ~/.ssh/id_rsa_laszlolang
|
||||
$ ssh xxsvci@laszlolang.com -i ~/.ssh/id_rsa_laszlolang
|
||||
|
||||
4.) Stunnel config
|
||||
|
||||
$ sudo nano /etc/stunnel/stunnel.conf
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class AppKernel extends Kernel
|
||||
new Gos\Bundle\PubSubRouterBundle\GosPubSubRouterBundle(),
|
||||
new Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle(),
|
||||
new Snc\RedisBundle\SncRedisBundle(),
|
||||
new CL\Bundle\SlackBundle\CLSlackBundle(),
|
||||
// new CL\Bundle\SlackBundle\CLSlackBundle(),
|
||||
|
||||
new Jotunheimr\AdminBundle\JotunheimrAdminBundle(),
|
||||
new Jotunheimr\UserBundle\JotunheimrUserBundle(),
|
||||
|
||||
@@ -10,6 +10,8 @@ imports:
|
||||
# http://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
|
||||
parameters:
|
||||
locale: en
|
||||
fos.email: 7system7@gmail.com
|
||||
fos.name: system7
|
||||
|
||||
framework:
|
||||
#esi: ~
|
||||
@@ -41,7 +43,7 @@ twig:
|
||||
debug: "%kernel.debug%"
|
||||
strict_variables: "%kernel.debug%"
|
||||
globals:
|
||||
version: "0.37.18 (beta7)"
|
||||
version: "0.42.21 (beta8)"
|
||||
facebook_api: "%facebook.api%"
|
||||
facebook_scope: "%facebook.scope%"
|
||||
facebook_api_version: "%facebook.version%"
|
||||
@@ -87,6 +89,9 @@ fos_user:
|
||||
db_driver: orm # other valid values are 'mongodb', 'couchdb' and 'propel'
|
||||
firewall_name: secured_area
|
||||
user_class: Jotunheimr\UserBundle\Entity\User
|
||||
from_email:
|
||||
address: "%fos.email%"
|
||||
sender_name: "%fos.name%"
|
||||
|
||||
# Facebook OAuth
|
||||
hwi_oauth:
|
||||
@@ -103,5 +108,5 @@ hwi_oauth:
|
||||
csrf: true
|
||||
|
||||
# Slack integration
|
||||
cl_slack:
|
||||
api_token: xoxp-107639806167-107029084564-115427085733-cccaa4f96c89c87ce680c7f22acfd001
|
||||
#cl_slack:
|
||||
# api_token: xoxp-107639806167-107029084564-115427085733-cccaa4f96c89c87ce680c7f22acfd001
|
||||
|
||||
@@ -35,7 +35,8 @@
|
||||
"predis/predis": "^1.0",
|
||||
"snc/redis-bundle": "^2.0",
|
||||
"hwi/oauth-bundle": "^0.5.1",
|
||||
"cleentfaar/slack-bundle": "^0.20.1"
|
||||
"cleentfaar/slack-bundle": "^0.20.1",
|
||||
"symfony/translation": "^3.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"sensio/generator-bundle": "^3.0",
|
||||
|
||||
811
composer.lock
generated
811
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
src/Mine/SeekerBundle/Resources/public/js/node
|
||||
@@ -9,6 +9,8 @@
|
||||
"babel-core": "^6.14.0",
|
||||
"babel-loader": "^6.2.5",
|
||||
"babel-preset-es2015": "^6.14.0",
|
||||
"babel-preset-es2016": "^6.22.0",
|
||||
"babel-preset-es2017": "^6.22.0",
|
||||
"babel-preset-react": "^6.11.1",
|
||||
"howler": "^2.0.1",
|
||||
"js-base64": "^2.1.9",
|
||||
@@ -33,6 +35,6 @@
|
||||
"multiplayer",
|
||||
"websocket"
|
||||
],
|
||||
"author": "Laszlo Lang <system7>",
|
||||
"author": "Laszlo Lang <system7> www.laszlolang.com",
|
||||
"license": "ISC"
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@ class User extends BaseUser
|
||||
*/
|
||||
private $facebookId;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="facebook_access_token", type="string", length=255, nullable=true)
|
||||
*/
|
||||
private $facebookAccessToken;
|
||||
|
||||
/**
|
||||
|
||||
@@ -251,6 +251,11 @@ main div.txt {
|
||||
margin: 50px auto 0 auto;
|
||||
}
|
||||
|
||||
main .txt h1 {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
main div.txt h2 {
|
||||
margin: 0 0 50px 0;
|
||||
}
|
||||
@@ -276,10 +281,6 @@ main .technologies img {
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
main .technologies h1 {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
footer {
|
||||
background: #414040;
|
||||
width: 100%;
|
||||
@@ -320,7 +321,7 @@ footer nav ul li a:hover {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1100px) {
|
||||
@media screen and (max-width: 1200px) {
|
||||
header section #id_welcome {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
{% trans_default_domain 'FOSUserBundle' %}
|
||||
|
||||
{% block title %} - Sign up{% endblock %}
|
||||
{% block title %} - Registration{% endblock %}
|
||||
|
||||
{% block fos_user_content %}
|
||||
<section class="header-content">
|
||||
@@ -12,7 +12,7 @@
|
||||
border="0"/>
|
||||
</a>
|
||||
</div>
|
||||
<h1> Sign up </h1>
|
||||
<h1> Registration </h1>
|
||||
<div class="db">
|
||||
<form action="{{ path('fos_user_registration_register') }}"
|
||||
method="post">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
{% trans_default_domain 'FOSUserBundle' %}
|
||||
|
||||
{% block title %} - Sign in{% endblock %}
|
||||
{% block title %} - Login{% endblock %}
|
||||
|
||||
{% block fos_user_content %}
|
||||
{% if error %}
|
||||
@@ -16,7 +16,7 @@
|
||||
border="0"/>
|
||||
</a>
|
||||
</div>
|
||||
<h1>Sign in</h1>
|
||||
<h1>Login</h1>
|
||||
<form action="{{ path("fos_user_security_check") }}" method="post"
|
||||
class="ac-custom ac-checkbox ac-boxfill">
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<input type="text" id="username" name="_username" value="{{ last_username }}"
|
||||
class="form-input form-username"
|
||||
placeholder="Username or Email"/>
|
||||
placeholder="Username or Email" autofocus />
|
||||
|
||||
<input type="password" id="password" name="_password"
|
||||
class="form-input"
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/JQuery-Snowfall/1.7.4/snowfall.jquery.min.js"
|
||||
type="text/javascript"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$(document).snowfall({deviceorientation: true, round: true, minSize: 5, maxSize: 8});
|
||||
|
||||
@@ -72,6 +72,7 @@ class MyFOSUBUserProvider extends BaseFOSUBProvider
|
||||
}
|
||||
|
||||
$user->setFacebookId($response->getUsername());
|
||||
$user->setFacebookAccessToken($response->getAccessToken());
|
||||
$user->setEmail($response->getEmail());
|
||||
$user->setEmailCanonical($response->getEmail());
|
||||
$user->setUsername($this->slug($response->getRealName()));
|
||||
|
||||
@@ -2,15 +2,18 @@
|
||||
|
||||
namespace Mine\SeekerBundle\Controller;
|
||||
|
||||
//use CL\Slack\Payload\ChatPostMessagePayload;
|
||||
//use CL\Slack\Payload\UsersListPayload;
|
||||
//use CL\Slack\Transport\ApiClient;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Class GameController
|
||||
*
|
||||
* @package Mine\SeekerBundle\Controller
|
||||
* @author system7 <https://www.laszlolang.com>
|
||||
*/
|
||||
class GameController extends Controller
|
||||
{
|
||||
public function indexAction()
|
||||
public function indexAction(Request $request)
|
||||
{
|
||||
// $apiClient = new ApiClient('xoxp-107639806167-107029084564-115427085733-cccaa4f96c89c87ce680c7f22acfd001');
|
||||
//
|
||||
@@ -33,14 +36,25 @@ class GameController extends Controller
|
||||
// dump($response->getErrorExplanation());
|
||||
// }
|
||||
|
||||
return $this->render('MineSeekerBundle:Game:index.html.twig');
|
||||
return $this->render('MineSeekerBundle:Game:index.html.twig', array(
|
||||
'env' => $this->container->getParameter('kernel.environment'),
|
||||
'ssl' => $request->isSecure() ? 'true' : 'false',
|
||||
));
|
||||
}
|
||||
|
||||
public function playAction(Request $request)
|
||||
{
|
||||
return $this->render('MineSeekerBundle:Game:play.html.twig', array(
|
||||
'env' => $this->container->getParameter('kernel.environment'),
|
||||
'ssl' => $request->isSecure() ? 'true' : 'false'
|
||||
'ssl' => $request->isSecure() ? 'true' : 'false',
|
||||
));
|
||||
}
|
||||
|
||||
public function rePlayAction(Request $request)
|
||||
{
|
||||
return $this->render('MineSeekerBundle:Game:play.html.twig', array(
|
||||
'env' => $this->container->getParameter('kernel.environment'),
|
||||
'ssl' => $request->isSecure() ? 'true' : 'false',
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,10 @@ namespace Mine\SeekerBundle\Entity;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* Gamer
|
||||
* Class Gamer
|
||||
*
|
||||
* @package Mine\SeekerBundle\Entity
|
||||
* @author system7 <https://www.laszlolang.com>
|
||||
*
|
||||
* @ORM\Table(name="gamer")
|
||||
* @ORM\Entity(repositoryClass="Mine\SeekerBundle\Repository\GamerRepository")
|
||||
|
||||
@@ -7,7 +7,10 @@ use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* Grid
|
||||
* Class Grid
|
||||
*
|
||||
* @package Mine\SeekerBundle\Entity
|
||||
* @author system7 <https://www.laszlolang.com>
|
||||
*
|
||||
* @ORM\Table(name="grid")
|
||||
* @ORM\Entity(repositoryClass="Mine\SeekerBundle\Repository\GridRepository")
|
||||
|
||||
@@ -5,7 +5,10 @@ namespace Mine\SeekerBundle\Entity;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* GridRow
|
||||
* Class GridRow
|
||||
*
|
||||
* @package Mine\SeekerBundle\Entity
|
||||
* @author system7 <https://www.laszlolang.com>
|
||||
*
|
||||
* @ORM\Table(name="grid_row")
|
||||
* @ORM\Entity(repositoryClass="Mine\SeekerBundle\Repository\GridRowRepository")
|
||||
|
||||
@@ -8,7 +8,10 @@ use Doctrine\ORM\Mapping as ORM;
|
||||
use Jotunheimr\UserBundle\Entity\User;
|
||||
|
||||
/**
|
||||
* PlayedGame
|
||||
* Class PlayedGame
|
||||
*
|
||||
* @package Mine\SeekerBundle\Entity
|
||||
* @author system7 <https://www.laszlolang.com>
|
||||
*
|
||||
* @ORM\Table(name="played_game")
|
||||
* @ORM\Entity(repositoryClass="Mine\SeekerBundle\Repository\PlayedGameRepository")
|
||||
|
||||
@@ -443,4 +443,52 @@ class PlayedGame
|
||||
{
|
||||
return $this->resign;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set created
|
||||
*
|
||||
* @param \DateTime $created
|
||||
*
|
||||
* @return PlayedGame
|
||||
*/
|
||||
public function setCreated($created)
|
||||
{
|
||||
$this->created = $created;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get created
|
||||
*
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getCreated()
|
||||
{
|
||||
return $this->created;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set updated
|
||||
*
|
||||
* @param \DateTime $updated
|
||||
*
|
||||
* @return PlayedGame
|
||||
*/
|
||||
public function setUpdated($updated)
|
||||
{
|
||||
$this->updated = $updated;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get updated
|
||||
*
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getUpdated()
|
||||
{
|
||||
return $this->updated;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,10 @@ namespace Mine\SeekerBundle\Entity;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* Step
|
||||
* Class Step
|
||||
*
|
||||
* @package Mine\SeekerBundle\Entity
|
||||
* @author system7 <https://www.laszlolang.com>
|
||||
*
|
||||
* @ORM\Table(name="step")
|
||||
* @ORM\Entity(repositoryClass="Mine\SeekerBundle\Repository\StepRepository")
|
||||
|
||||
@@ -162,4 +162,28 @@ class Step
|
||||
{
|
||||
return $this->wBomb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set created
|
||||
*
|
||||
* @param \DateTime $created
|
||||
*
|
||||
* @return Step
|
||||
*/
|
||||
public function setCreated($created)
|
||||
{
|
||||
$this->created = $created;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get created
|
||||
*
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getCreated()
|
||||
{
|
||||
return $this->created;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,12 @@ use Gos\Bundle\WebSocketBundle\Event\ClientErrorEvent;
|
||||
use Gos\Bundle\WebSocketBundle\Event\ServerEvent;
|
||||
use Gos\Bundle\WebSocketBundle\Event\ClientRejectedEvent;
|
||||
|
||||
/**
|
||||
* Class MineseekerClientEventListener
|
||||
*
|
||||
* @package Mine\SeekerBundle\EventListener
|
||||
* @author system7 <https://www.laszlolang.com>
|
||||
*/
|
||||
class MineseekerClientEventListener
|
||||
{
|
||||
/**
|
||||
@@ -69,4 +75,4 @@ class MineseekerClientEventListener
|
||||
|
||||
echo 'connection rejected from ' . $origin . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,13 @@ namespace Mine\SeekerBundle\Periodic;
|
||||
|
||||
use Gos\Bundle\WebSocketBundle\Periodic\PdoPeriodicPing;
|
||||
use Gos\Bundle\WebSocketBundle\Periodic\PeriodicInterface;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* Class MinePeriodic
|
||||
*
|
||||
* @package Mine\SeekerBundle\Periodic
|
||||
* @author system7 <https://www.laszlolang.com>
|
||||
*/
|
||||
class MinePeriodic implements PeriodicInterface
|
||||
{
|
||||
/** @var PdoPeriodicPing */
|
||||
@@ -19,7 +24,8 @@ class MinePeriodic implements PeriodicInterface
|
||||
/**
|
||||
* This function is executed every 5 seconds.
|
||||
*
|
||||
* For more advanced functionality, try injecting a Topic Service to perform actions on your connections every x seconds.
|
||||
* For more advanced functionality, try injecting a Topic Service to perform actions on your connections every x
|
||||
* seconds.
|
||||
*/
|
||||
public function tick()
|
||||
{
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
namespace Mine\SeekerBundle\Repository;
|
||||
|
||||
/**
|
||||
* GamerRepository
|
||||
* Class GamerRepository
|
||||
*
|
||||
* This class was generated by the Doctrine ORM. Add your own custom
|
||||
* repository methods below.
|
||||
* @package Mine\SeekerBundle\Repository
|
||||
* @author system7 <https://www.laszlolang.com>
|
||||
*/
|
||||
class GamerRepository extends \Doctrine\ORM\EntityRepository
|
||||
{
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
namespace Mine\SeekerBundle\Repository;
|
||||
|
||||
/**
|
||||
* GridColRepository
|
||||
* Class GridColRepository
|
||||
*
|
||||
* This class was generated by the Doctrine ORM. Add your own custom
|
||||
* repository methods below.
|
||||
* @package Mine\SeekerBundle\Repository
|
||||
* @author system7 <https://www.laszlolang.com>
|
||||
*/
|
||||
class GridColRepository extends \Doctrine\ORM\EntityRepository
|
||||
{
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
namespace Mine\SeekerBundle\Repository;
|
||||
|
||||
/**
|
||||
* GridRepository
|
||||
* Class GridRepository
|
||||
*
|
||||
* This class was generated by the Doctrine ORM. Add your own custom
|
||||
* repository methods below.
|
||||
* @package Mine\SeekerBundle\Repository
|
||||
* @author system7 <https://www.laszlolang.com>
|
||||
*/
|
||||
class GridRepository extends \Doctrine\ORM\EntityRepository
|
||||
{
|
||||
|
||||
@@ -4,10 +4,10 @@ namespace Mine\SeekerBundle\Repository;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
|
||||
/**
|
||||
* GridRowRepository
|
||||
* Class GridRowRepository
|
||||
*
|
||||
* This class was generated by the Doctrine ORM. Add your own custom
|
||||
* repository methods below.
|
||||
* @package Mine\SeekerBundle\Repository
|
||||
* @author system7 <https://www.laszlolang.com>
|
||||
*/
|
||||
class GridRowRepository extends EntityRepository
|
||||
{
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
namespace Mine\SeekerBundle\Repository;
|
||||
|
||||
/**
|
||||
* PlayedGameRepository
|
||||
* Class PlayedGameRepository
|
||||
*
|
||||
* This class was generated by the Doctrine ORM. Add your own custom
|
||||
* repository methods below.
|
||||
* @package Mine\SeekerBundle\Repository
|
||||
* @author system7 <https://www.laszlolang.com>
|
||||
*/
|
||||
class PlayedGameRepository extends \Doctrine\ORM\EntityRepository
|
||||
{
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
namespace Mine\SeekerBundle\Repository;
|
||||
|
||||
/**
|
||||
* StepRepository
|
||||
* Class StepRepository
|
||||
*
|
||||
* This class was generated by the Doctrine ORM. Add your own custom
|
||||
* repository methods below.
|
||||
* @package Mine\SeekerBundle\Repository
|
||||
* @author system7 <https://www.laszlolang.com>
|
||||
*/
|
||||
class StepRepository extends \Doctrine\ORM\EntityRepository
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Topic Configuration
|
||||
# MineSeeker Topic Configuration
|
||||
mineseeker_topic:
|
||||
channel: mineseeker/channel/{game}
|
||||
handler:
|
||||
@@ -7,6 +7,12 @@ mineseeker_topic:
|
||||
# method:
|
||||
# path: '[a-z1-9A-Z]+'
|
||||
|
||||
# UserList Topic Configuration
|
||||
userList_topic:
|
||||
channel: mineseeker/userList
|
||||
handler:
|
||||
callback: 'userlist.topic'
|
||||
|
||||
# Remote Procedure Call Configuration
|
||||
mineseeker_rpc:
|
||||
channel: mineseeker-rpc/{method}
|
||||
|
||||
@@ -13,6 +13,11 @@ MineSeekerBundle_gamePlayWId:
|
||||
defaults: { _controller: MineSeekerBundle:Game:play }
|
||||
schemes: [https]
|
||||
|
||||
MineSeekerBundle_gameReplay:
|
||||
path: /re-play/{gameAssoc}
|
||||
defaults: { _controller: MineSeekerBundle:Game:rePlay }
|
||||
schemes: [https]
|
||||
|
||||
MineSeekerBundle_slack:
|
||||
path: /slack
|
||||
defaults: { _controller: MineSeekerBundle:Game:slack }
|
||||
|
||||
@@ -26,7 +26,7 @@ services:
|
||||
arguments:
|
||||
ping: '@gos_web_socket.pdo.periodic_ping'
|
||||
|
||||
mineseeker.topic_sample_service:
|
||||
mineseeker.game_service:
|
||||
class: Mine\SeekerBundle\Topic\MineseekerTopic
|
||||
tags:
|
||||
- { name: gos_web_socket.topic }
|
||||
@@ -35,7 +35,16 @@ services:
|
||||
doctrine: '@doctrine.orm.entity_manager'
|
||||
requestStack: '@request_stack'
|
||||
|
||||
mineseeker.rpc_sample_service:
|
||||
mineseeker.user_list_service:
|
||||
class: Mine\SeekerBundle\Topic\UserListTopic
|
||||
tags:
|
||||
- { name: gos_web_socket.topic }
|
||||
arguments:
|
||||
clientManipulator: "@gos_web_socket.websocket.client_manipulator"
|
||||
doctrine: '@doctrine.orm.entity_manager'
|
||||
requestStack: '@request_stack'
|
||||
|
||||
mineseeker.game_rpc_service:
|
||||
class: Mine\SeekerBundle\Rpc\MineseekerRpc
|
||||
tags:
|
||||
- { name: gos_web_socket.rpc }
|
||||
|
||||
@@ -141,7 +141,162 @@ header section div.buttons > a.small:hover {
|
||||
transition: all 250ms ease-in-out;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1100px) {
|
||||
main .user-list-container {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
main .user-list-container .user-friend {
|
||||
background: #ccc;
|
||||
width: 15%;
|
||||
min-width: 145px;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
margin: 5px;
|
||||
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
main .user-list-container .user-friend h1 {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
main .user-list-container .user-friend .img,
|
||||
main .user-list-container .user-friend .img img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
main .user-list-container .user-friend button {
|
||||
background: #83aed9;
|
||||
display: inline-block;
|
||||
font: bold 22px 'Rajdhani', sans-serif;
|
||||
border: 1px solid #6890ba;
|
||||
color: #FFFFFF;
|
||||
padding: 10px;
|
||||
|
||||
-webkit-transition: all 250ms ease-in-out;
|
||||
-moz-transition: all 250ms ease-in-out;
|
||||
-o-transition: all 250ms ease-in-out;
|
||||
transition: all 250ms ease-in-out;
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
main .user-list-container .user-friend button:hover {
|
||||
background: #86b5e1;
|
||||
border: 1px solid #658fb8;
|
||||
color: #FFFFFF;
|
||||
|
||||
-webkit-transition: all 250ms ease-in-out;
|
||||
-moz-transition: all 250ms ease-in-out;
|
||||
-o-transition: all 250ms ease-in-out;
|
||||
transition: all 250ms ease-in-out;
|
||||
}
|
||||
|
||||
main .user-list-container .user-friend button.button-offline {
|
||||
background: #a1a1a1;
|
||||
border: 1px solid #929292;
|
||||
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
main .user-list-container .user-friend button.button-offline:hover {
|
||||
background: #a1a1a1;
|
||||
border: 1px solid #929292;
|
||||
}
|
||||
|
||||
main .user-list-filter form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
main .user-list-filter input {
|
||||
border: 1px solid #ccc;
|
||||
border-right: 0;
|
||||
min-width: 300px;
|
||||
padding: 10px;
|
||||
|
||||
-webkit-border-top-left-radius: 3px;
|
||||
border-top-left-radius: 3px;
|
||||
-webkit-border-bottom-left-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
}
|
||||
|
||||
main .user-list-filter button {
|
||||
background: #ccc;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border: 0;
|
||||
color: #fff;
|
||||
|
||||
-webkit-border-top-right-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
-webkit-border-bottom-right-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
|
||||
main .user-request-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
main .user-request-container .user-friend {
|
||||
background: #ff0000;
|
||||
width: 150px;
|
||||
color: #403f3f;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
main .user-request-container h1 {
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
main .user-request-container .img,
|
||||
main .user-request-container .img img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
main .user-request-container button {
|
||||
background: #fff;
|
||||
width: 100%;
|
||||
font-weight: bold;
|
||||
color: #ff0000;
|
||||
border: 0;
|
||||
padding: 15px 5px;
|
||||
margin: 0 auto;
|
||||
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
-webkit-transition: all 250ms ease-in-out;
|
||||
-moz-transition: all 250ms ease-in-out;
|
||||
-o-transition: all 250ms ease-in-out;
|
||||
transition: all 250ms ease-in-out;
|
||||
}
|
||||
|
||||
main .user-request-container button:hover {
|
||||
background: #ffc38b;
|
||||
|
||||
-webkit-transition: all 250ms ease-in-out;
|
||||
-moz-transition: all 250ms ease-in-out;
|
||||
-o-transition: all 250ms ease-in-out;
|
||||
transition: all 250ms ease-in-out;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
header section {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -166,11 +321,6 @@ header section div.buttons > a.small:hover {
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
/*header {*/
|
||||
/*min-height: 100%;*/
|
||||
/*height: auto;*/
|
||||
/*}*/
|
||||
|
||||
header section {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
BIN
src/Mine/SeekerBundle/Resources/public/images/avatar/bg-avatar-default-1.png
Executable file
BIN
src/Mine/SeekerBundle/Resources/public/images/avatar/bg-avatar-default-1.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
BIN
src/Mine/SeekerBundle/Resources/public/images/avatar/bg-avatar-default-2.png
Executable file
BIN
src/Mine/SeekerBundle/Resources/public/images/avatar/bg-avatar-default-2.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
BIN
src/Mine/SeekerBundle/Resources/public/images/avatar/bg-avatar-default-3.png
Executable file
BIN
src/Mine/SeekerBundle/Resources/public/images/avatar/bg-avatar-default-3.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
BIN
src/Mine/SeekerBundle/Resources/public/images/avatar/bg-avatar-default-4.png
Executable file
BIN
src/Mine/SeekerBundle/Resources/public/images/avatar/bg-avatar-default-4.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
BIN
src/Mine/SeekerBundle/Resources/public/images/avatar/bg-avatar-default-5.png
Executable file
BIN
src/Mine/SeekerBundle/Resources/public/images/avatar/bg-avatar-default-5.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
@@ -2,9 +2,12 @@ import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import MineSeeker from './mine-seeker/app';
|
||||
|
||||
let mineWrapper = document.getElementById('mine-wrapper');
|
||||
|
||||
ReactDOM.render(
|
||||
<MineSeeker env={document.getElementById('mine-wrapper').dataset.env}
|
||||
gameId={document.getElementById('mine-wrapper').dataset.gameId}
|
||||
ssl={document.getElementById('mine-wrapper').dataset.ssl}/>,
|
||||
document.getElementById('mine-wrapper')
|
||||
<MineSeeker env={mineWrapper.dataset.env}
|
||||
gameId={mineWrapper.dataset.gameId}
|
||||
webPlayerName={mineWrapper.dataset.webPlayerName}
|
||||
ssl={mineWrapper.dataset.ssl}/>,
|
||||
mineWrapper
|
||||
);
|
||||
|
||||
@@ -1,49 +1,39 @@
|
||||
import React from 'react';
|
||||
import Grid from './grid/grid';
|
||||
import GridControl from './grid/grid-control';
|
||||
import MineServices from '../mine-system/mine-services';
|
||||
import MineStart from './game/start'
|
||||
import MineEnd from './game/end'
|
||||
import MineWebsocket from './game/websocket'
|
||||
|
||||
class MineSeeker extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
let gameAssoc = props.gameId !== '' ? props.gameId : this.makeGameAssoc(50);
|
||||
let services = new MineServices();
|
||||
let gameAssoc = props.gameId !== '' ? props.gameId : services.randomString(50);
|
||||
let channel = "mineseeker/channel/" + gameAssoc;
|
||||
|
||||
this.state = {
|
||||
startProcess: new MineStart(),
|
||||
endProcess: new MineEnd(),
|
||||
wsProcess: new MineWebsocket(),
|
||||
services: services,
|
||||
|
||||
env: props.env,
|
||||
ssl: props.ssl,
|
||||
gameInherited: props.gameId !== '',
|
||||
webPlayerName: props.webPlayerName === '' ? null : props.webPlayerName,
|
||||
gameAssoc: gameAssoc,
|
||||
channel: channel,
|
||||
session: null,
|
||||
createGrid: false,
|
||||
stepCache: [],
|
||||
connectionLost: false,
|
||||
end: false
|
||||
end: false,
|
||||
replay: false
|
||||
}
|
||||
}
|
||||
|
||||
makeGameAssoc(len) {
|
||||
let text = "";
|
||||
let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
for (let i = 0; i < len; i++) {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
currectGridSize() {
|
||||
let $field = $('#mine-wrapper .grid');
|
||||
$field.height($field.width());
|
||||
|
||||
$field = $('#mine-wrapper .grid .field-wrapper');
|
||||
$field.height($field.width());
|
||||
|
||||
$('#mine-wrapper .grid .field-wrapper .field')
|
||||
.height($field.width())
|
||||
.css('line-height', ($field.width() - 2) + 'px');
|
||||
}
|
||||
|
||||
/**
|
||||
* STEP
|
||||
*
|
||||
@@ -71,338 +61,21 @@ class MineSeeker extends React.Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* START
|
||||
* Unregistered
|
||||
* http://mine.dev/re-play/I1Bx9UHZP5CWDnTZHpJqGTlkzehblfsbfz4A4xYaH9HFhBK2aN
|
||||
*
|
||||
* @param payload
|
||||
* Registered - Admin -> Lang
|
||||
* http://mine.dev/re-play/bazmegdflgkjndfgkhjn
|
||||
*/
|
||||
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;
|
||||
|
||||
if (redWins || blueWins || resign) {
|
||||
this.refs.gridControl.state.sound.won.play();
|
||||
|
||||
if (false === resign) {
|
||||
this.refs.gridControl.setState({
|
||||
overlay: true,
|
||||
overlayTitle: (redWins ? 'Red' : 'Blue') + " wins the game!",
|
||||
overlaySubTitle: "Play again!"
|
||||
});
|
||||
}
|
||||
|
||||
this.refs.gridControl.showLeftMines();
|
||||
|
||||
this.refs.gridControl.refs.userControl.setState({activePlayer: false});
|
||||
this.refs.gridControl.refs.userControl.refs.red.setState({desc: ""});
|
||||
this.refs.gridControl.refs.userControl.refs.blue.setState({desc: ""});
|
||||
}
|
||||
}
|
||||
|
||||
resignProcess(color) {
|
||||
this.refs.gridControl.setState({
|
||||
overlay: true,
|
||||
overlayTitle: color === this.refs.gridControl.state.webPlayer
|
||||
? "You have been give up"
|
||||
: "Your opponent has been resigned",
|
||||
overlaySubTitle: color === this.refs.gridControl.state.webPlayer
|
||||
? "You LOSE!"
|
||||
: "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'
|
||||
});
|
||||
this.resignProcess(this.refs.gridControl.state.webPlayer);
|
||||
}
|
||||
|
||||
clickResignCancel() {
|
||||
this.refs.gridControl.setState({
|
||||
overlay: false
|
||||
});
|
||||
}
|
||||
|
||||
/** RESIGN */
|
||||
resign() {
|
||||
let users = this.refs.gridControl.refs.userControl,
|
||||
activePlayer = users.state.activePlayer ? 'blue' : 'red';
|
||||
|
||||
if (this.refs.gridControl.state.webPlayer === activePlayer) {
|
||||
this.refs.gridControl.setState({
|
||||
overlay: true,
|
||||
overlayTitle: "Are u sure u want to resign?!",
|
||||
overlaySubTitle: <div className="resign">
|
||||
<a onClick={this.clickResign.bind(this)}>Yes</a>
|
||||
<a onClick={this.clickResignCancel.bind(this)}>No!</a>
|
||||
</div>
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus
|
||||
* @see https://developers.facebook.com/docs/sharing/reference/send-dialog
|
||||
* @see https://developers.facebook.com/docs/plugins/share-button/
|
||||
*/
|
||||
clickFBShare() {
|
||||
let display = 'popup';
|
||||
|
||||
FB.getLoginStatus(function (response) {
|
||||
display = response.status === 'connected'
|
||||
? 'dialog'
|
||||
: 'popup';
|
||||
});
|
||||
|
||||
FB.ui({
|
||||
method: 'send',
|
||||
display: display,
|
||||
link: window.location.href + '/' + this.state.gameAssoc,
|
||||
});
|
||||
}
|
||||
|
||||
wInit(session, gridServer, gridClient) {
|
||||
this.setState({session: session});
|
||||
|
||||
/** save session to GridControl */
|
||||
/** render grid fields - @see #12 */
|
||||
this.refs.gridControl.setState({
|
||||
grid: this.state.gameInherited ? gridServer : gridClient,
|
||||
channel: this.state.channel,
|
||||
desc: {
|
||||
buddy: <div>
|
||||
Your buddy is <br/>
|
||||
making a <br/>
|
||||
move.
|
||||
</div>,
|
||||
you: <div>
|
||||
It is your turn! <br/>
|
||||
Make a move.
|
||||
</div>
|
||||
},
|
||||
overlay: true,
|
||||
overlayTitle: "We are waiting for your opponent...",
|
||||
overlaySubTitle: this.state.gameAssoc
|
||||
?
|
||||
<div>
|
||||
<h3>Share this unique link w/ your opponent</h3>
|
||||
<div className="clippy">
|
||||
<input id="foo"
|
||||
defaultValue={window.location.href + '/' + this.state.gameAssoc}/>
|
||||
</div>
|
||||
{this.state.env !== 'dev' &&
|
||||
<a onClick={this.clickFBShare.bind(this)}>Share in Facebook message</a>
|
||||
}
|
||||
{this.state.env === 'dev' &&
|
||||
<a href={"/play/" + this.state.gameAssoc} target="_blank">Play w/ me!</a>
|
||||
}
|
||||
</div>
|
||||
: '',
|
||||
renderGridFields: this.state.gameAssoc
|
||||
});
|
||||
}
|
||||
|
||||
wSubscribe(payload, rpcUsers = null) {
|
||||
this.state.env === 'dev' && console.info(
|
||||
(typeof payload.user !== 'undefined' ? payload.user : 'user') + " has been subscribed to the channel!"
|
||||
);
|
||||
|
||||
let firstUser = !rpcUsers;
|
||||
|
||||
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'
|
||||
});
|
||||
|
||||
/** rwd */
|
||||
(900 > $(document).width()) && this.currectGridSize();
|
||||
|
||||
/** 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);
|
||||
}
|
||||
}
|
||||
|
||||
wUnsubscribe(payload) {
|
||||
this.state.env === 'dev' && console.info(payload.msg);
|
||||
|
||||
this.refs.gridControl.setState({
|
||||
overlay: true,
|
||||
overlayTitle: "The connection has been lost w/ your friend...",
|
||||
overlaySubTitle: "Please, restart the game!"
|
||||
});
|
||||
}
|
||||
|
||||
wTopic(payload) {
|
||||
/** Auto-Step if this player is not the current user */
|
||||
if (this.refs.gridControl.state.webPlayer !== payload.data.player) {
|
||||
if (null === payload.data.resign) {
|
||||
this.state.env === 'dev' && console.warn(payload.user + " has been stepped to coords: " + payload.data.coords[0] + ', ' + payload.data.coords[1]);
|
||||
this.state.env === 'dev' && console.warn('Opponent stepped: Auto-Step process');
|
||||
|
||||
this.refs.gridControl.refs.userControl.setState({bombSelected: payload.data.bomb});
|
||||
|
||||
/** STEP */
|
||||
let points = this.makePointsCalcAndStep(payload.data.coords);
|
||||
|
||||
/** THE END */
|
||||
this.makeGameEndIfItEnds(points.blue, points.red);
|
||||
} else {
|
||||
/** RESIGN */
|
||||
/** THE END */
|
||||
this.resignProcess(payload.data.resign);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Connect - Subscribe */
|
||||
subscribe(rpcUsers = null) {
|
||||
this.state.session.subscribe(
|
||||
this.state.channel,
|
||||
(uri, payload, log) => {
|
||||
let isTopicEvent = typeof payload.data !== 'undefined',
|
||||
isNotUnsubscribe = typeof payload.msg === 'undefined';
|
||||
|
||||
/** CONNECTION */
|
||||
if (isTopicEvent) {
|
||||
this.wTopic(payload);
|
||||
} else {
|
||||
if (isNotUnsubscribe) {
|
||||
this.wSubscribe(payload, rpcUsers);
|
||||
} else {
|
||||
this.wUnsubscribe(payload);
|
||||
}
|
||||
}
|
||||
|
||||
/** RECONNECTION */
|
||||
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({connectionLost: false, stepCache: []});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
connectWithWebsocket() {
|
||||
/** Create Websocket w/ Bahnhof.js */
|
||||
let websocket = WS.connect(
|
||||
this.state.env === 'dev'
|
||||
? "ws://mine.dev:6450"
|
||||
// : (this.state.ssl === 'true' ? "wss" : "ws") + "://" + window.location.hostname + ":6450/"
|
||||
: (this.state.ssl === 'true' ? "wss" : "ws") + "://www.mineseeker.ninja:6450/"
|
||||
);
|
||||
|
||||
/**
|
||||
* Connect
|
||||
* Session is an Autobahn JS WAMP session.
|
||||
*/
|
||||
websocket.on("socket/connect", (session) => {
|
||||
this.state.env === 'dev' && console.info("Successfully connected to the Server!");
|
||||
|
||||
if (!this.state.connectionLost) {
|
||||
let gridClient = this.state.gameInherited || new Grid().state.grid;
|
||||
|
||||
/**
|
||||
* Connect - RPC
|
||||
* Send grid information to the server
|
||||
*/
|
||||
session
|
||||
.call(
|
||||
this.state.gameInherited ? "mineseeker-rpc/connectGame" : "mineseeker-rpc/startGame",
|
||||
this.state.gameInherited ? this.state.gameAssoc : [Base64.encode(JSON.stringify(gridClient)), this.state.gameAssoc]
|
||||
)
|
||||
.then(
|
||||
(data) => {
|
||||
this.state.env === 'dev' && console.info('RPC has been called');
|
||||
|
||||
let serverData = data[0] !== true
|
||||
? JSON.parse(Base64.decode(data))
|
||||
: data;
|
||||
|
||||
/** Check the grid if the user is inherited @see #30 */
|
||||
if ((this.state.gameInherited && null !== serverData.grid) || !this.state.gameInherited) {
|
||||
this.wInit(session, serverData.grid, gridClient);
|
||||
this.subscribe(this.state.gameInherited && serverData.users);
|
||||
} else {
|
||||
this.refs.gridControl.setState({
|
||||
overlay: true,
|
||||
overlayTitle: "This channel does not exists!",
|
||||
overlaySubTitle: <a href={"/play"} target="_self">Restart game!</a>
|
||||
});
|
||||
|
||||
console.error("This channel does not exists!");
|
||||
}
|
||||
},
|
||||
(error, desc) => this.state.env === 'dev' && console.error(["RPC Error", error, desc])
|
||||
);
|
||||
} else {
|
||||
this.setState({session: session});
|
||||
this.subscribe();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* DisConnect
|
||||
* Error provides us with some insight into the disconnection: error.reason and error.code
|
||||
*/
|
||||
websocket.on("socket/disconnect", (error) => {
|
||||
this.state.env === 'dev' && console.error("Disconnected for " + error.reason + " with code " + error.code);
|
||||
|
||||
error.code === 6 && this.setState({connectionLost: true});
|
||||
error.code === 3 && setTimeout(function () {
|
||||
this.componentDidMount();
|
||||
}.bind(this), 500);
|
||||
});
|
||||
}
|
||||
|
||||
/** After rendering */
|
||||
componentDidMount() {
|
||||
this.connectWithWebsocket();
|
||||
/** is it a REPLAY */
|
||||
window.location.pathname.indexOf('re-play') > 0
|
||||
? this.setState({replay: true})
|
||||
: this.setState({replay: false});
|
||||
|
||||
this.state.wsProcess.connectWithWebsocket(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -427,7 +100,7 @@ class MineSeeker extends React.Component {
|
||||
let points = this.makePointsCalcAndStep(coords);
|
||||
|
||||
/** THE END */
|
||||
this.makeGameEndIfItEnds(points.blue, points.red);
|
||||
this.state.endProcess.makeGameEndIfItEnds(this, points.blue, points.red);
|
||||
|
||||
let dataPack = {
|
||||
'coords': coords,
|
||||
@@ -452,7 +125,7 @@ class MineSeeker extends React.Component {
|
||||
return (
|
||||
<GridControl ref="gridControl"
|
||||
env={this.props.env === 'dev'}
|
||||
resign={this.resign.bind(this)}
|
||||
resign={this.state.endProcess.resign.bind(this.state.endProcess, this)}
|
||||
onClick={this.onClick.bind(this)}/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import React from 'react';
|
||||
|
||||
class MineEnd extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
makeGameEndIfItEnds(app, bluePoints, redPoints, resign = false) {
|
||||
let redWins = redPoints > 25,
|
||||
blueWins = bluePoints > 25,
|
||||
gridControl = app.refs.gridControl,
|
||||
userControl = app.refs.gridControl.refs.userControl,
|
||||
bluePlayer = userControl.refs.blue,
|
||||
redPlayer = userControl.refs.red;
|
||||
|
||||
if (redWins || blueWins || resign) {
|
||||
gridControl.state.sound.won.play();
|
||||
|
||||
/** it is NOT a RESIGN */
|
||||
if (false === resign) {
|
||||
gridControl.setState({
|
||||
overlay: true,
|
||||
overlayTitle: (redWins ? 'Red' : 'Blue') + " wins the game!",
|
||||
overlaySubTitle: "Play again!"
|
||||
});
|
||||
}
|
||||
|
||||
gridControl.showLeftMines();
|
||||
userControl.setState({activePlayer: false});
|
||||
redPlayer.setState({desc: ""});
|
||||
bluePlayer.setState({desc: ""});
|
||||
}
|
||||
}
|
||||
|
||||
resignProcess(app, webPlayer) {
|
||||
let gridControl = app.refs.gridControl;
|
||||
|
||||
gridControl.setState({
|
||||
overlay: true,
|
||||
overlayTitle: webPlayer === gridControl.state.webPlayer
|
||||
? "You have been give up"
|
||||
: "Your opponent has been resigned",
|
||||
overlaySubTitle: webPlayer === gridControl.state.webPlayer
|
||||
? "You LOSE!"
|
||||
: "You WIN!"
|
||||
});
|
||||
|
||||
app.setState({end: true});
|
||||
this.makeGameEndIfItEnds(app, 0, 0, true);
|
||||
}
|
||||
|
||||
clickResign(app) {
|
||||
let gridControl = app.refs.gridControl,
|
||||
userControl = app.refs.gridControl.refs.userControl;
|
||||
|
||||
/** REMOTE */
|
||||
app.state.session.publish(app.state.channel, {
|
||||
'resign': userControl.state.activePlayer ? 'blue' : 'red'
|
||||
});
|
||||
|
||||
/** LOCAL */
|
||||
this.resignProcess(app, gridControl.state.webPlayer);
|
||||
}
|
||||
|
||||
clickResignCancel(gridControl) {
|
||||
gridControl.setState({overlay: false});
|
||||
}
|
||||
|
||||
resign(app) {
|
||||
let gridControl = app.refs.gridControl,
|
||||
userControl = app.refs.gridControl.refs.userControl,
|
||||
activePlayer = userControl.state.activePlayer ? 'blue' : 'red';
|
||||
|
||||
if (gridControl.state.webPlayer === activePlayer) {
|
||||
gridControl.setState({
|
||||
overlay: true,
|
||||
overlayTitle: "Are u sure u want to resign?!",
|
||||
overlaySubTitle: <div className="resign">
|
||||
<a onClick={this.clickResign.bind(this, app)}>Yes</a>
|
||||
<a onClick={this.clickResignCancel.bind(this, gridControl)}>No!</a>
|
||||
</div>
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default MineEnd;
|
||||
@@ -0,0 +1,66 @@
|
||||
import React from 'react';
|
||||
|
||||
class MineStart extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
/** MAIN */
|
||||
makeGameStart(app, payload, gridControl) {
|
||||
let steps = JSON.parse(Base64.decode(payload.steps)),
|
||||
userControl = gridControl.refs.userControl;
|
||||
|
||||
if (steps.length) {
|
||||
this.takeSteps(app, steps, gridControl);
|
||||
} else {
|
||||
/** every time the blue starts when it is not a continued game */
|
||||
userControl.setState({activePlayer: 1});
|
||||
}
|
||||
|
||||
this.setupPlayers(payload, gridControl, userControl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take steps if them exists
|
||||
*
|
||||
* @param app
|
||||
* @param steps
|
||||
* @param userControl
|
||||
*/
|
||||
takeSteps(app, steps, userControl) {
|
||||
steps.forEach((item) => {
|
||||
setTimeout(() => {
|
||||
userControl.setState({bombSelected: item.wBomb});
|
||||
app.makePointsCalcAndStep([item.row, item.col]);
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up player names w/ server data
|
||||
*
|
||||
* @param payload
|
||||
* @param gridControl
|
||||
* @param userControl
|
||||
*/
|
||||
setupPlayers(payload, gridControl, userControl) {
|
||||
let redPlayer = userControl.refs.red,
|
||||
bluePlayer = userControl.refs.blue;
|
||||
|
||||
redPlayer.setState({
|
||||
name: payload.users.red !== '' ? payload.users.red : payload.users.redAnon,
|
||||
});
|
||||
|
||||
bluePlayer.setState({
|
||||
name: payload.users.blue !== '' ? payload.users.blue : payload.users.blueAnon,
|
||||
desc: gridControl.state.webPlayer === 'blue'
|
||||
? gridControl.state.desc.you
|
||||
: gridControl.state.desc.buddy,
|
||||
active: true,
|
||||
});
|
||||
|
||||
gridControl.setState({overlay: false});
|
||||
}
|
||||
}
|
||||
|
||||
export default MineStart;
|
||||
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
|
||||
class MineStep extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default MineStep;
|
||||
@@ -0,0 +1,266 @@
|
||||
import React from 'react';
|
||||
import Grid from '../grid/grid';
|
||||
|
||||
class MineWebsocket extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
handleUnsubscribe(app, payload) {
|
||||
let gridControl = app.refs.gridControl;
|
||||
|
||||
app.state.env === 'dev' && console.info(payload.msg);
|
||||
|
||||
gridControl.setState({
|
||||
overlay: true,
|
||||
overlayTitle: "The connection has been lost w/ your friend...",
|
||||
overlaySubTitle: "Please, restart the game!"
|
||||
});
|
||||
}
|
||||
|
||||
nbrUsersWasInGame(users) {
|
||||
let nbr = 0;
|
||||
|
||||
nbr += users.blueAnon === "" ? 0 : 1;
|
||||
nbr += users.redAnon === "" ? 0 : 1;
|
||||
nbr += users.blue === "" ? 0 : 1;
|
||||
nbr += users.red === "" ? 0 : 1;
|
||||
|
||||
return nbr;
|
||||
}
|
||||
|
||||
wereYouInTheParty(users, user) {
|
||||
return users.blueAnon === user ||
|
||||
users.redAnon === user ||
|
||||
users.blue === user ||
|
||||
users.red === user;
|
||||
}
|
||||
|
||||
whichWereYouInTheParty(users, user) {
|
||||
return users.blue === user ? 'blue' : 'red';
|
||||
}
|
||||
|
||||
handleSubscribe(app, payload) {
|
||||
let gridControl = app.refs.gridControl,
|
||||
user = payload.user,
|
||||
usersInTheParty = payload.users,
|
||||
webPlayerName = app.state.webPlayerName;
|
||||
|
||||
app.state.env === 'dev' && console.info(
|
||||
(typeof payload.user !== 'undefined' ? payload.user : 'user') + " has been subscribed to the channel!"
|
||||
);
|
||||
|
||||
/** set up webPlayer */
|
||||
if (gridControl.state.webPlayer === null) {
|
||||
if (app.state.replay) {
|
||||
if (this.wereYouInTheParty(usersInTheParty, webPlayerName)) {
|
||||
let opponent = user === webPlayerName && webPlayerName === payload.users.red
|
||||
? payload.users.blue
|
||||
: payload.users.red;
|
||||
|
||||
gridControl.setState({
|
||||
webPlayer: this.whichWereYouInTheParty(usersInTheParty, webPlayerName),
|
||||
overlayTitle: "We are waitin' for " + opponent + "!",
|
||||
overlaySubTitle: ""
|
||||
});
|
||||
} else {
|
||||
gridControl.setState({
|
||||
overlayTitle: "It was not your party!",
|
||||
overlaySubTitle: ""
|
||||
});
|
||||
}
|
||||
} else {
|
||||
gridControl.setState({
|
||||
webPlayer: payload.user === payload.users.blue || payload.user === payload.users.blueAnon
|
||||
? 'blue' : 'red',
|
||||
overlaySubTitle: app.state.gameAssoc
|
||||
?
|
||||
<div>
|
||||
<h3>Share this unique link w/ your opponent</h3>
|
||||
<div className="clippy">
|
||||
<input id="foo"
|
||||
defaultValue={window.location.href + '/' + app.state.gameAssoc}/>
|
||||
</div>
|
||||
{app.state.env !== 'dev' &&
|
||||
<a onClick={this.clickFBShare.bind(this, app)}>Share in Facebook message</a>
|
||||
}
|
||||
{app.state.env === 'dev' &&
|
||||
<a href={"/play/" + app.state.gameAssoc} target="_blank">Play w/ me!</a>
|
||||
}
|
||||
</div>
|
||||
: ''
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (payload.userCnt === 2 && !app.state.end) {
|
||||
app.state.startProcess.makeGameStart(app, payload, gridControl);
|
||||
}
|
||||
|
||||
/** rwd */
|
||||
(900 > $(document).width()) && app.state.services.currectGridSize();
|
||||
}
|
||||
|
||||
handleTopic(app, payload) {
|
||||
let gridControl = app.refs.gridControl,
|
||||
userControl = gridControl.refs.userControl;
|
||||
|
||||
/** Auto-Step if this player is not the current user */
|
||||
if (gridControl.state.webPlayer !== payload.data.player) {
|
||||
if (null === payload.data.resign) {
|
||||
app.state.env === 'dev' && console.warn(payload.user + " has been stepped to coords: " + payload.data.coords[0] + ', ' + payload.data.coords[1]);
|
||||
app.state.env === 'dev' && console.warn('Opponent stepped: Auto-Step process');
|
||||
|
||||
userControl.setState({bombSelected: payload.data.bomb});
|
||||
|
||||
/** STEP */
|
||||
let points = app.makePointsCalcAndStep(payload.data.coords);
|
||||
/** THE END? */
|
||||
app.state.endProcess.makeGameEndIfItEnds(app, points.blue, points.red);
|
||||
} else {
|
||||
/** RESIGN */
|
||||
/** THE END */
|
||||
app.state.endProcess.resignProcess(app, payload.data.resign);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Connect - Subscribe */
|
||||
subscribe(app) {
|
||||
app.state.session.subscribe(
|
||||
app.state.channel,
|
||||
(uri, payload, log) => {
|
||||
let isTopicEvent = typeof payload.data !== 'undefined',
|
||||
isNotUnsubscribe = typeof payload.msg === 'undefined';
|
||||
|
||||
/** CONNECTION */
|
||||
if (isTopicEvent) {
|
||||
this.handleTopic(app, payload);
|
||||
} else {
|
||||
if (isNotUnsubscribe) {
|
||||
this.handleSubscribe(app, payload);
|
||||
} else {
|
||||
this.handleUnsubscribe(app, payload);
|
||||
}
|
||||
}
|
||||
|
||||
/** RECONNECTION */
|
||||
if (payload.userCnt === 2 && app.state.connectionLost) {
|
||||
app.state.env === 'dev' && console.info('Reconnection process');
|
||||
|
||||
let cache = app.state.stepCache;
|
||||
cache.forEach((item) => app.state.session.publish(app.state.channel, item));
|
||||
app.setState({connectionLost: false, stepCache: []});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
connectWithWebsocket(app) {
|
||||
/** Create Websocket w/ Bahnhof.js */
|
||||
let websocket = WS.connect(
|
||||
app.state.env === 'dev'
|
||||
? "ws://mine.dev:6450"
|
||||
: (app.state.ssl === 'true' ? "wss" : "ws") + "://www.mineseeker.ninja:6450/"
|
||||
);
|
||||
|
||||
/**
|
||||
* Connect
|
||||
* Session is an Autobahn JS WAMP session.
|
||||
*/
|
||||
websocket.on("socket/connect", (session) => {
|
||||
app.state.env === 'dev' && console.info("Successfully connected to the Server!");
|
||||
|
||||
if (!app.state.connectionLost) {
|
||||
let gridClient = new Grid().state.grid;
|
||||
|
||||
/**
|
||||
* Connect - RPC
|
||||
* Send grid information to the server
|
||||
*/
|
||||
session
|
||||
.call(
|
||||
"mineseeker-rpc/connectGame",
|
||||
[Base64.encode(JSON.stringify(gridClient)), app.state.gameAssoc]
|
||||
)
|
||||
.then(
|
||||
(data) => {
|
||||
app.state.env === 'dev' && console.info('RPC has been called');
|
||||
|
||||
let grid = JSON.parse(Base64.decode(data));
|
||||
|
||||
this.init(app, session, grid);
|
||||
this.subscribe(app);
|
||||
},
|
||||
(error, desc) => app.state.env === 'dev' && console.error(["RPC Error", error, desc])
|
||||
);
|
||||
} else {
|
||||
app.setState({session: session});
|
||||
this.subscribe(app);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* DisConnect
|
||||
* Error provides us with some insight into the disconnection: error.reason and error.code
|
||||
*/
|
||||
websocket.on("socket/disconnect", (error) => {
|
||||
app.state.env === 'dev' && console.error("Disconnected for " + error.reason + " with code " + error.code);
|
||||
|
||||
error.code === 6 && app.setState({connectionLost: true});
|
||||
error.code === 3 && setTimeout(function () {
|
||||
app.componentDidMount();
|
||||
}.bind(this), 500);
|
||||
});
|
||||
}
|
||||
|
||||
init(app, session, grid) {
|
||||
let gridControl = app.refs.gridControl;
|
||||
|
||||
app.setState({session: session});
|
||||
|
||||
/** save session to GridControl */
|
||||
/** render grid fields - @see #12 */
|
||||
gridControl.setState({
|
||||
grid: grid,
|
||||
channel: app.state.channel,
|
||||
desc: {
|
||||
buddy: <div>
|
||||
Your buddy is <br/>
|
||||
making a <br/>
|
||||
move.
|
||||
</div>,
|
||||
you: <div>
|
||||
It is your turn! <br/>
|
||||
Make a move.
|
||||
</div>
|
||||
},
|
||||
overlay: true,
|
||||
overlayTitle: "We are waiting for your opponent...",
|
||||
overlaySubTitle: "",
|
||||
renderGridFields: app.state.gameAssoc
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus
|
||||
* @see https://developers.facebook.com/docs/sharing/reference/send-dialog
|
||||
* @see https://developers.facebook.com/docs/plugins/share-button/
|
||||
*/
|
||||
clickFBShare(app) {
|
||||
let display = 'popup';
|
||||
|
||||
FB.getLoginStatus(function (response) {
|
||||
display = response.status === 'connected'
|
||||
? 'dialog'
|
||||
: 'popup';
|
||||
});
|
||||
|
||||
FB.ui({
|
||||
method: 'send',
|
||||
display: display,
|
||||
link: window.location.href + '/' + app.state.gameAssoc,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default MineWebsocket;
|
||||
@@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
|
||||
class MineServices extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
randomString(len) {
|
||||
let text = "";
|
||||
let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
for (let i = 0; i < len; i++) {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* RWD
|
||||
*/
|
||||
currectGridSize() {
|
||||
let $field = $('#mine-wrapper .grid');
|
||||
$field.height($field.width());
|
||||
|
||||
$field = $('#mine-wrapper .grid .field-wrapper');
|
||||
$field.height($field.width());
|
||||
|
||||
$('#mine-wrapper .grid .field-wrapper .field')
|
||||
.height($field.width())
|
||||
.css('line-height', ($field.width() - 2) + 'px');
|
||||
}
|
||||
}
|
||||
|
||||
export default MineServices;
|
||||
12
src/Mine/SeekerBundle/Resources/public/js/mine-user-list.js
Normal file
12
src/Mine/SeekerBundle/Resources/public/js/mine-user-list.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import MineUserList from './mine-user-list/app';
|
||||
|
||||
let mineUserList = document.getElementById('mine-user-list');
|
||||
|
||||
ReactDOM.render(
|
||||
<MineUserList env={mineUserList.dataset.env}
|
||||
ssl={mineUserList.dataset.ssl}
|
||||
webPlayerName={mineUserList.dataset.webPlayerName}/>,
|
||||
mineUserList
|
||||
);
|
||||
194
src/Mine/SeekerBundle/Resources/public/js/mine-user-list/app.js
Normal file
194
src/Mine/SeekerBundle/Resources/public/js/mine-user-list/app.js
Normal file
@@ -0,0 +1,194 @@
|
||||
import React from 'react';
|
||||
import MineServices from '../mine-system/mine-services';
|
||||
|
||||
class MineUserList extends React.Component {
|
||||
constructor(props) {
|
||||
super();
|
||||
|
||||
let services = new MineServices();
|
||||
|
||||
this.state = {
|
||||
env: props.env,
|
||||
ssl: props.ssl,
|
||||
webPlayerName: props.webPlayerName,
|
||||
services: services,
|
||||
session: null,
|
||||
users: [],
|
||||
requests: new Map(),
|
||||
search: '',
|
||||
};
|
||||
}
|
||||
|
||||
refreshUserList(payload) {
|
||||
let webUsers = JSON.parse(Base64.decode(payload.users)),
|
||||
users = new Map();
|
||||
|
||||
webUsers.forEach((item) => {
|
||||
if (!users.has(item.email) && item.username !== this.state.webPlayerName) {
|
||||
users.set(item.email, item);
|
||||
}
|
||||
});
|
||||
|
||||
this.setState({users: users});
|
||||
}
|
||||
|
||||
connectWithWebsocket() {
|
||||
let websocket = WS.connect(
|
||||
this.state.env === 'dev'
|
||||
? "ws://mine.dev:6450"
|
||||
: (this.state.ssl === 'true' ? "wss" : "ws") + "://www.mineseeker.ninja:6450/"
|
||||
);
|
||||
|
||||
websocket.on("socket/connect", (session) => {
|
||||
this.state.env === 'dev' && console.info("Successfully connected to the Server!");
|
||||
this.state.session = session;
|
||||
|
||||
session.subscribe(
|
||||
'mineseeker/userList',
|
||||
(uri, payload, log) => {
|
||||
/** refresh list */
|
||||
if (typeof payload.users !== 'undefined') {
|
||||
this.refreshUserList(payload);
|
||||
}
|
||||
|
||||
/** communication */
|
||||
if (typeof payload.gameAssoc !== 'undefined') {
|
||||
switch (payload.type) {
|
||||
case 'REQ':
|
||||
let req = this.state.requests,
|
||||
user = JSON.parse(Base64.decode(payload.user));
|
||||
|
||||
if (!req.has(user.username)) {
|
||||
req.set(user.username, {
|
||||
'gameAssoc': payload.gameAssoc,
|
||||
'user': user,
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({reqests: req});
|
||||
break;
|
||||
case 'RESP':
|
||||
window.location.href = window.location.origin + '/re-play/' + payload.gameAssoc;
|
||||
break;
|
||||
case 'SEARCH':
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
websocket.on("socket/disconnect", (error) => {
|
||||
this.state.env === 'dev' && console.error("Disconnected for " + error.reason + " with code " + error.code);
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.connectWithWebsocket();
|
||||
}
|
||||
|
||||
getProfilePicture(id) {
|
||||
let avatarNbr = 5;
|
||||
|
||||
if (id !== null) {
|
||||
avatarNbr = id.substr(-1) > 5 ? 5 : id.substr(-1);
|
||||
}
|
||||
|
||||
return id !== null ?
|
||||
<img src={"http://graph.facebook.com/" + id + "/picture?type=square&width=130&height=130"}
|
||||
alt="Facebook profile"/> :
|
||||
<img src={"/bundles/mineseeker/images/avatar/bg-avatar-default-" + avatarNbr + ".png"}
|
||||
alt="Default avatar picture"/>;
|
||||
}
|
||||
|
||||
clickChallengeFriend(isOnline, username) {
|
||||
if (isOnline) {
|
||||
this.state.session.publish('mineseeker/userList', {
|
||||
'type': 'REQ',
|
||||
'username': username,
|
||||
'gameAssoc': this.state.services.randomString(50)
|
||||
});
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
clickChallengeAccepted(username, gameAssoc) {
|
||||
this.state.session.publish('mineseeker/userList', {
|
||||
'type': 'RESP',
|
||||
'username': username,
|
||||
'gameAssoc': gameAssoc
|
||||
});
|
||||
|
||||
window.location.href = window.location.origin + '/re-play/' + gameAssoc;
|
||||
}
|
||||
|
||||
onChangeSearch(event) {
|
||||
this.setState({search: event.target.value});
|
||||
}
|
||||
|
||||
handleSubmit(event) {
|
||||
this.state.session.publish('mineseeker/userList', {
|
||||
'type': 'SEARCH',
|
||||
'username': this.state.search
|
||||
});
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
render() {
|
||||
let users = [];
|
||||
let req = [];
|
||||
|
||||
this.state.users.size > 0
|
||||
? this.state.users.forEach((item) => {
|
||||
users.push(<div key={this.state.services.randomString(50)} className="user-friend">
|
||||
<div className="img">
|
||||
{this.getProfilePicture(item.id)}
|
||||
</div>
|
||||
<h1>{item.name}</h1>
|
||||
<button className={item.online ? 'button-online' : 'button-offline'}
|
||||
onClick={this.clickChallengeFriend.bind(this, item.online, item.username)}>
|
||||
{item.online ? 'Challenge' : 'Offline'}
|
||||
</button>
|
||||
</div>)
|
||||
})
|
||||
: '';
|
||||
|
||||
this.state.requests.size > 0
|
||||
? this.state.requests.forEach((item) => {
|
||||
req.push(<div key={this.state.services.randomString(50)} className="user-friend">
|
||||
<div className="img">
|
||||
{this.getProfilePicture(item.user.id)}
|
||||
</div>
|
||||
<h1>{item.user.name} <br/><strong>challenged you</strong>!</h1>
|
||||
<button onClick={this.clickChallengeAccepted.bind(this, item.user.username, item.gameAssoc)}>
|
||||
Accept
|
||||
</button>
|
||||
</div>)
|
||||
})
|
||||
: '';
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="user-request-container">
|
||||
{req}
|
||||
</div>
|
||||
<div className="user-list-filter">
|
||||
<form onSubmit={this.handleSubmit.bind(this)}>
|
||||
<input type="text"
|
||||
onChange={this.onChangeSearch.bind(this)}
|
||||
value={this.state.search}
|
||||
autoFocus="autoFocus"
|
||||
placeholder="Search users..."/>
|
||||
<button type="submit"><i className="fa fa-search"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
<div className="user-list-container">
|
||||
{users.length ? users : 'There is no users w/ this search results..'}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MineUserList;
|
||||
@@ -48,8 +48,8 @@
|
||||
{{ 'layout.logout'|trans({}, 'FOSUserBundle') }}
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{{ path('fos_user_registration_register') }}" class="small">Sign up</a> ·
|
||||
<a href="{{ path('fos_user_security_login') }}" class="small">Sign in</a>
|
||||
<a href="{{ path('fos_user_registration_register') }}" class="small">Registration</a> ·
|
||||
<a href="{{ path('fos_user_security_login') }}" class="small">Login</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<h3>
|
||||
@@ -63,6 +63,14 @@
|
||||
|
||||
{% block body %}
|
||||
<div class="txt">
|
||||
{% if is_granted("IS_AUTHENTICATED_REMEMBERED") %}
|
||||
<h1>Users</h1>
|
||||
<div id="mine-user-list"
|
||||
data-env="{{ env }}"
|
||||
data-ssl="{{ ssl }}"
|
||||
data-web-player-name="{{ app.user.username is defined ? app.user.username : '' }}">
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="technologies">
|
||||
<h1>Used technologies</h1>
|
||||
<img src="{{ asset('bundles/mineseeker/images/technologies/websocket.png') }}" alt="Used Websocket"
|
||||
@@ -93,6 +101,7 @@
|
||||
{% block stylesheets %}
|
||||
{{ parent() }}
|
||||
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Rajdhani:300,400,500,600,700&subset=latin-ext"
|
||||
rel="stylesheet">
|
||||
|
||||
@@ -107,11 +116,31 @@
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/JQuery-Snowfall/1.7.4/snowfall.jquery.min.js"
|
||||
type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$(document).snowfall({deviceorientation: true, round: true, minSize: 5, maxSize: 8});
|
||||
});
|
||||
</script>
|
||||
{% if is_granted("IS_AUTHENTICATED_REMEMBERED") and app.user.facebookAccessToken is defined %}
|
||||
{% if env == 'dev' %}
|
||||
{% javascripts filter='?uglifyjs2'
|
||||
'@GosWebSocketBundle/Resources/public/js/vendor/autobahn.min.js'
|
||||
'@GosWebSocketBundle/Resources/public/js/gos_web_socket_client.js'
|
||||
'@MineSeekerBundle/Resources/public/js/node/js-base64/base64.min.js'
|
||||
'@MineSeekerBundle/Resources/public/js/build/mine-user-list/index.js' %}
|
||||
<script type="text/javascript" src="{{ asset_url }}"></script>
|
||||
{% endjavascripts %}
|
||||
{% else %}
|
||||
{% javascripts filter='?uglifyjs2'
|
||||
'@GosWebSocketBundle/Resources/public/js/vendor/autobahn.min.js'
|
||||
'@GosWebSocketBundle/Resources/public/js/gos_web_socket_client.js'
|
||||
'@MineSeekerBundle/Resources/public/js/node/js-base64/base64.min.js'
|
||||
'@MineSeekerBundle/Resources/public/js/build/mine-user-list/index.min.js' %}
|
||||
<script type="text/javascript" src="{{ asset_url }}"></script>
|
||||
{% endjavascripts %}
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/JQuery-Snowfall/1.7.4/snowfall.jquery.min.js"
|
||||
type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$(document).snowfall({deviceorientation: true, round: true, minSize: 5, maxSize: 8});
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
<div id="mine-wrapper"
|
||||
data-env="{{ env }}"
|
||||
data-ssl="{{ ssl }}"
|
||||
data-game-id="{{ app.request.get('gameAssoc') }}">
|
||||
data-game-id="{{ app.request.get('gameAssoc') }}"
|
||||
data-web-player-name="{{ app.user.username is defined ? app.user.username : '' }}">
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -20,7 +21,8 @@
|
||||
<meta property="og:type" content="website"/>
|
||||
<meta property="og:title" content="Your friend challenges YOU!"/>
|
||||
<meta property="og:description" content="Do you accept the challenge?"/>
|
||||
<meta property="og:image" content="{{ app.request.getSchemeAndHttpHost() }}{{ asset('bundles/mineseeker/images/mine-1600x627.png') }}"/>
|
||||
<meta property="og:image"
|
||||
content="{{ app.request.getSchemeAndHttpHost() }}{{ asset('bundles/mineseeker/images/mine-1600x627.png') }}"/>
|
||||
{% endblock %}
|
||||
|
||||
{% block stylesheets %}
|
||||
@@ -46,7 +48,7 @@
|
||||
'@GosWebSocketBundle/Resources/public/js/gos_web_socket_client.js'
|
||||
'@MineSeekerBundle/Resources/public/js/node/howler/dist/howler.min.js'
|
||||
'@MineSeekerBundle/Resources/public/js/node/js-base64/base64.min.js'
|
||||
'@MineSeekerBundle/Resources/public/js/index.js' %}
|
||||
'@MineSeekerBundle/Resources/public/js/build/mine-seeker/index.js' %}
|
||||
<script type="text/javascript" src="{{ asset_url }}"></script>
|
||||
{% endjavascripts %}
|
||||
{% else %}
|
||||
@@ -55,7 +57,7 @@
|
||||
'@GosWebSocketBundle/Resources/public/js/gos_web_socket_client.js'
|
||||
'@MineSeekerBundle/Resources/public/js/node/howler/dist/howler.min.js'
|
||||
'@MineSeekerBundle/Resources/public/js/node/js-base64/base64.min.js'
|
||||
'@MineSeekerBundle/Resources/public/js/index.min.js' %}
|
||||
'@MineSeekerBundle/Resources/public/js/build/mine-seeker/index.min.js' %}
|
||||
<script type="text/javascript" src="{{ asset_url }}"></script>
|
||||
{% endjavascripts %}
|
||||
{% endif %}
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
<script type="text/javascript">
|
||||
window.fbAsyncInit = function () {
|
||||
FB.init({
|
||||
appId: '{{ facebook_api }}',
|
||||
xfbml: true,
|
||||
cookie: true,
|
||||
status: true,
|
||||
oauth: true,
|
||||
version: '{{ facebook_api_version }}'
|
||||
});
|
||||
};
|
||||
$(function () {
|
||||
window.fbAsyncInit = function () {
|
||||
FB.init({
|
||||
appId: '{{ facebook_api }}',
|
||||
xfbml: true,
|
||||
cookie: true,
|
||||
status: true,
|
||||
version: '{{ facebook_api_version }}',
|
||||
});
|
||||
|
||||
/** trigger jQuery when Facebook SKD loads */
|
||||
// $(document).trigger('fb-load');
|
||||
// $(document).bind('fb-load', function () {
|
||||
// TODO
|
||||
// });
|
||||
};
|
||||
});
|
||||
|
||||
(function (d, s, id) {
|
||||
var js, fjs = d.getElementsByTagName(s)[0];
|
||||
|
||||
@@ -4,22 +4,30 @@ namespace Mine\SeekerBundle\Rpc;
|
||||
|
||||
use Doctrine\DBAL\Driver\PDOException;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\OptimisticLockException;
|
||||
use Mine\SeekerBundle\Entity\Grid;
|
||||
use Mine\SeekerBundle\Entity\GridRow;
|
||||
use Mine\SeekerBundle\Entity\PlayedGame;
|
||||
use Ratchet\ConnectionInterface;
|
||||
use Gos\Bundle\WebSocketBundle\RPC\RpcInterface;
|
||||
use Gos\Bundle\WebSocketBundle\Router\WampRequest;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Class MineseekerRpc
|
||||
*
|
||||
* @package Mine\SeekerBundle\Rpc
|
||||
* @author system7 <https://www.laszlolang.com>
|
||||
*/
|
||||
class MineseekerRpc implements RpcInterface
|
||||
{
|
||||
/** @var EntityManager */
|
||||
protected $em;
|
||||
|
||||
protected $grid;
|
||||
|
||||
/**
|
||||
* MineseekerRpc constructor.
|
||||
*
|
||||
* @param EntityManager $entityManager
|
||||
*/
|
||||
public function __construct(EntityManager $entityManager)
|
||||
@@ -39,93 +47,53 @@ class MineseekerRpc implements RpcInterface
|
||||
|
||||
/**
|
||||
* @param ConnectionInterface $connection
|
||||
* @param WampRequest $request
|
||||
* @param array $params
|
||||
* @return boolean
|
||||
*/
|
||||
public function startGame(ConnectionInterface $connection, WampRequest $request, array $params)
|
||||
{
|
||||
return $this->saveGrid($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ConnectionInterface $connection
|
||||
* @param WampRequest $request
|
||||
* @param array $params
|
||||
* @param WampRequest $request
|
||||
* @param array $params
|
||||
*
|
||||
* @return string Json string for frontend w/ numbering consideration. (=> a number is not string)
|
||||
* @throws OptimisticLockException
|
||||
*/
|
||||
public function connectGame(ConnectionInterface $connection, WampRequest $request, array $params)
|
||||
{
|
||||
$grid = $this->getGrid($params);
|
||||
$users = null !== $grid ? $this->getUsers($params) : null;
|
||||
|
||||
return base64_encode(json_encode(
|
||||
array(
|
||||
'grid' => $grid,
|
||||
'users' => $users
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $gameAssoc
|
||||
* @return array
|
||||
*/
|
||||
private function getGrid($gameAssoc)
|
||||
{
|
||||
$this->reConnect();
|
||||
$getsee = array();
|
||||
|
||||
$this->em->clear();
|
||||
|
||||
$grid = $this->em
|
||||
->getRepository('MineSeekerBundle:PlayedGame')
|
||||
->findOneByGameAssoc($gameAssoc);
|
||||
|
||||
if (null !== $grid) {
|
||||
foreach ($grid->getGrid()->getGridRow()->toArray() as $row) {
|
||||
$getsee[] = $row->getGridCol();
|
||||
}
|
||||
|
||||
return $getsee;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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() : ''
|
||||
);
|
||||
return base64_encode(json_encode($grid));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
*
|
||||
* @return array|bool
|
||||
* @throws OptimisticLockException
|
||||
*/
|
||||
private function getGrid($data)
|
||||
{
|
||||
$this->reConnect();
|
||||
$this->em->clear();
|
||||
|
||||
$grid = array();
|
||||
/** @var PlayedGame $game */
|
||||
$game = $this->em
|
||||
->getRepository('MineSeekerBundle:PlayedGame')
|
||||
->findOneByGameAssoc($data[1]);
|
||||
|
||||
if (null !== $game) {
|
||||
/** @var GridRow $row */
|
||||
foreach ($game->getGrid()->getGridRow()->toArray() as $row) {
|
||||
$grid[] = $row->getGridCol();
|
||||
}
|
||||
|
||||
return $grid;
|
||||
} else {
|
||||
$this->saveGrid($data);
|
||||
return $this->getGrid($data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
*
|
||||
* @return boolean
|
||||
* @throws OptimisticLockException
|
||||
*/
|
||||
private function saveGrid($data)
|
||||
{
|
||||
@@ -169,8 +137,8 @@ class MineseekerRpc implements RpcInterface
|
||||
$connection->close();
|
||||
$connection->connect();
|
||||
}
|
||||
} catch(PDOException $ex) {
|
||||
throw PDOException::class;
|
||||
} catch (PDOException $ex) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,25 @@ namespace Mine\SeekerBundle\Topic;
|
||||
|
||||
use Doctrine\DBAL\Driver\PDOException;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\OptimisticLockException;
|
||||
use Gos\Bundle\WebSocketBundle\Client\ClientManipulatorInterface;
|
||||
use Gos\Bundle\WebSocketBundle\Topic\TopicInterface;
|
||||
use Gos\Bundle\WebSocketBundle\Router\WampRequest;
|
||||
use HWI\Bundle\OAuthBundle\Tests\Fixtures\FOSUser;
|
||||
use Mine\SeekerBundle\Entity\Gamer;
|
||||
use Mine\SeekerBundle\Entity\PlayedGame;
|
||||
use Mine\SeekerBundle\Entity\Step;
|
||||
use Ratchet\ConnectionInterface;
|
||||
use Ratchet\Wamp\Topic;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Class MineseekerTopic
|
||||
*
|
||||
* @package Mine\SeekerBundle\Topic
|
||||
* @author system7 <https://www.laszlolang.com>
|
||||
*/
|
||||
class MineseekerTopic implements TopicInterface
|
||||
{
|
||||
/** @var ClientManipulatorInterface */
|
||||
@@ -27,9 +37,9 @@ class MineseekerTopic implements TopicInterface
|
||||
/**
|
||||
* MineseekerTopic constructor.
|
||||
*
|
||||
* @param $clientManipulator ClientManipulatorInterface
|
||||
* @param $clientManipulator ClientManipulatorInterface
|
||||
* @param EntityManager $entityManager
|
||||
* @param RequestStack $requestStack
|
||||
* @param RequestStack $requestStack
|
||||
*/
|
||||
public function __construct(ClientManipulatorInterface $clientManipulator, EntityManager $entityManager, RequestStack $requestStack)
|
||||
{
|
||||
@@ -42,9 +52,11 @@ class MineseekerTopic implements TopicInterface
|
||||
* This will receive any Subscription requests for this topic.
|
||||
*
|
||||
* @param ConnectionInterface $connection
|
||||
* @param Topic $topic
|
||||
* @param WampRequest $request
|
||||
* @param Topic $topic
|
||||
* @param WampRequest $request
|
||||
*
|
||||
* @return void
|
||||
* @throws OptimisticLockException
|
||||
*/
|
||||
public function onSubscribe(ConnectionInterface $connection, Topic $topic, WampRequest $request)
|
||||
{
|
||||
@@ -58,13 +70,14 @@ class MineseekerTopic implements TopicInterface
|
||||
} else {
|
||||
$users = $this->controlUsers($topic, $userName, $user);
|
||||
|
||||
$topic->broadcast([
|
||||
$topic->broadcast(array(
|
||||
'userTopicId' => $connection->resourceId,
|
||||
'channel' => $topic->getId(),
|
||||
'user' => $userName,
|
||||
'userCnt' => $topic->count(),
|
||||
'users' => $users
|
||||
]);
|
||||
'users' => $users,
|
||||
'steps' => $this->getSteps($topic),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,8 +85,9 @@ class MineseekerTopic implements TopicInterface
|
||||
* This will receive any UnSubscription requests for this topic.
|
||||
*
|
||||
* @param ConnectionInterface $connection
|
||||
* @param Topic $topic
|
||||
* @param WampRequest $request
|
||||
* @param Topic $topic
|
||||
* @param WampRequest $request
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function onUnSubscribe(ConnectionInterface $connection, Topic $topic, WampRequest $request)
|
||||
@@ -86,14 +100,16 @@ class MineseekerTopic implements TopicInterface
|
||||
* 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
|
||||
* @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
|
||||
* @throws OptimisticLockException
|
||||
*/
|
||||
public function onPublish(ConnectionInterface $connection, Topic $topic, WampRequest $request, $event, array $exclude, array $eligible)
|
||||
{
|
||||
@@ -131,12 +147,15 @@ class MineseekerTopic implements TopicInterface
|
||||
*
|
||||
* @param $topic
|
||||
* @param $color
|
||||
*
|
||||
* @throws OptimisticLockException
|
||||
*/
|
||||
private function saveResignToDb($topic, $color)
|
||||
{
|
||||
$this->reConnect();
|
||||
$gameAssoc = explode('/', $topic->getId())[2];
|
||||
|
||||
/** @var PlayedGame $playedGame */
|
||||
$playedGame = $this->em
|
||||
->getRepository('MineSeekerBundle:PlayedGame')
|
||||
->findOneByGameAssoc($gameAssoc);
|
||||
@@ -149,14 +168,17 @@ class MineseekerTopic implements TopicInterface
|
||||
/**
|
||||
* Save steps and point information to database
|
||||
*
|
||||
* @param $topic
|
||||
* @param $event
|
||||
* @param Topic $topic
|
||||
* @param $event
|
||||
*
|
||||
* @throws OptimisticLockException
|
||||
*/
|
||||
private function saveStepToDb($topic, $event)
|
||||
{
|
||||
$this->reConnect();
|
||||
$gameAssoc = explode('/', $topic->getId())[2];
|
||||
|
||||
/** @var PlayedGame $playedGame */
|
||||
$playedGame = $this->em
|
||||
->getRepository('MineSeekerBundle:PlayedGame')
|
||||
->findOneByGameAssoc($gameAssoc);
|
||||
@@ -180,19 +202,44 @@ class MineseekerTopic implements TopicInterface
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
private function getSteps($topic)
|
||||
{
|
||||
$this->reConnect();
|
||||
$gameAssoc = explode('/', $topic->getId())[2];
|
||||
|
||||
$playedGame = $this->em
|
||||
->getRepository('MineSeekerBundle:PlayedGame')
|
||||
->findOneByGameAssoc($gameAssoc);
|
||||
|
||||
$steps = array();
|
||||
|
||||
foreach ($playedGame->getStep()->toArray() as $item) {
|
||||
$steps[] = array(
|
||||
'row' => $item->getRow(),
|
||||
'col' => $item->getCol(),
|
||||
'wBomb' => $item->getWBomb(),
|
||||
);
|
||||
}
|
||||
|
||||
return base64_encode(json_encode($steps));
|
||||
}
|
||||
|
||||
/**
|
||||
* Control all users in a channel
|
||||
*
|
||||
* @param $topic
|
||||
* @param $userName
|
||||
* @param $user
|
||||
*
|
||||
* @return array
|
||||
* @throws OptimisticLockException
|
||||
*/
|
||||
private function controlUsers($topic, $userName, $user)
|
||||
{
|
||||
$this->reConnect();
|
||||
$gameAssoc = explode('/', $topic->getId())[2];
|
||||
|
||||
/** @var PlayedGame $playedGame */
|
||||
$playedGame = $this->em
|
||||
->getRepository('MineSeekerBundle:PlayedGame')
|
||||
->findOneByGameAssoc($gameAssoc);
|
||||
@@ -208,7 +255,7 @@ class MineseekerTopic implements TopicInterface
|
||||
/** 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());
|
||||
$users = $this->saveUserToDb($playedGame, $userName, $user, $topic->count());
|
||||
}
|
||||
|
||||
return $users;
|
||||
@@ -217,20 +264,17 @@ class MineseekerTopic implements TopicInterface
|
||||
/**
|
||||
* Save user data to database
|
||||
*
|
||||
* @param $topic
|
||||
* @param $userName
|
||||
* @param $user
|
||||
* @param $count
|
||||
* @param PlayedGame $playedGame
|
||||
* @param FOSUser $userName
|
||||
* @param $user
|
||||
* @param $count
|
||||
*
|
||||
* @return array
|
||||
* @throws OptimisticLockException
|
||||
*/
|
||||
private function saveUserToDb($topic, $userName, $user, $count)
|
||||
private function saveUserToDb($playedGame, $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)) {
|
||||
@@ -275,7 +319,8 @@ class MineseekerTopic implements TopicInterface
|
||||
/**
|
||||
* Get user collection from PlayedGame entity
|
||||
*
|
||||
* @param $playedGame
|
||||
* @param PlayedGame $playedGame
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getUserCollection($playedGame)
|
||||
@@ -300,8 +345,8 @@ class MineseekerTopic implements TopicInterface
|
||||
$connection->close();
|
||||
$connection->connect();
|
||||
}
|
||||
} catch(PDOException $ex) {
|
||||
throw PDOException::class;
|
||||
} catch (PDOException $ex) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
283
src/Mine/SeekerBundle/Topic/UserListTopic.php
Normal file
283
src/Mine/SeekerBundle/Topic/UserListTopic.php
Normal file
@@ -0,0 +1,283 @@
|
||||
<?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 Ratchet\ConnectionInterface;
|
||||
use Ratchet\Wamp\Topic;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Class UserListTopic
|
||||
*
|
||||
* @package Mine\SeekerBundle\Topic
|
||||
* @author system7 <https://www.laszlolang.com>
|
||||
*/
|
||||
class UserListTopic 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();
|
||||
|
||||
/** @var array Find all users online and offline $users */
|
||||
$users = $this->findAllUsers($topic);
|
||||
|
||||
$topic->broadcast([
|
||||
'userTopicId' => $connection->resourceId,
|
||||
'channel' => $topic->getId(),
|
||||
'user' => $userName,
|
||||
'userCnt' => $topic->count(),
|
||||
'users' => base64_encode(json_encode($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();
|
||||
|
||||
if ($event['type'] === 'REQ' || $event['type'] === 'RESP') {
|
||||
$reqUser = $this->clientManipulator->findByUsername($topic, $event['username']);
|
||||
|
||||
/** user is still online */
|
||||
if (false !== $reqUser) {
|
||||
$topic->broadcast(
|
||||
array(
|
||||
'type' => $event['type'],
|
||||
'user' => $this->findUser($topic, $userName),
|
||||
'gameAssoc' => $event['gameAssoc'],
|
||||
),
|
||||
array(),
|
||||
array(
|
||||
$reqUser['connection']->WAMP->sessionId
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($event['type'] === 'SEARCH') {
|
||||
$reqUser = $this->clientManipulator->findByUsername($topic, $userName);
|
||||
|
||||
/** user is still online */
|
||||
if (false !== $reqUser) {
|
||||
$topic->broadcast(
|
||||
array(
|
||||
'type' => $event['type'],
|
||||
'users' => $this->findUsersForSearch($topic, $event['username']),
|
||||
'gameAssoc' => true,
|
||||
),
|
||||
array(),
|
||||
array(
|
||||
$reqUser['connection']->WAMP->sessionId,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like RPC is will use to prefix the channel
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'userlist.topic';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $topic
|
||||
* @param $username
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function findUser($topic, $username)
|
||||
{
|
||||
$this->reConnect();
|
||||
$userRepo = $this->em->getRepository('JotunheimrUserBundle:User');
|
||||
$user = $userRepo->findOneByUsername($username);
|
||||
$currentOnline = $this->clientManipulator->findByUsername($topic, $username)['client'];
|
||||
|
||||
return base64_encode(json_encode(array(
|
||||
'id' => $user->getFacebookId(),
|
||||
'name' => $user->getRealName(),
|
||||
'email' => $user->getEmail(),
|
||||
'username' => $user->getUsername(),
|
||||
'online' => $currentOnline !== null
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $topic
|
||||
* @param $username
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function findUsersForSearch($topic, $username)
|
||||
{
|
||||
$this->reConnect();
|
||||
$userRepo = $this->em->getRepository('JotunheimrUserBundle:User');
|
||||
$users = $userRepo
|
||||
->createQueryBuilder('o')
|
||||
->where('o.username LIKE :username')
|
||||
->andWhere('o.realName LIKE :realname')
|
||||
->setParameter('username', "%{$username}%")
|
||||
->setParameter('realname', "%{$username}%")
|
||||
->setMaxResults(10)
|
||||
->getQuery()
|
||||
->getResult();
|
||||
$usersAvailable = array();
|
||||
|
||||
foreach ($users as $item) {
|
||||
$usersAvailable[] = array(
|
||||
'id' => $item->getFacebookId(),
|
||||
'name' => $item->getRealName(),
|
||||
'email' => $item->getEmail(),
|
||||
'username' => $item->getUsername(),
|
||||
'online' => $this->clientManipulator->findByUsername($topic, $item->getUsername())['client'] !== null
|
||||
);
|
||||
}
|
||||
|
||||
return base64_encode(json_encode(
|
||||
$usersAvailable
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all users online/offline
|
||||
*
|
||||
* @param $topic
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function findAllUsers($topic)
|
||||
{
|
||||
$usersOnline = array();
|
||||
$usersOffline = array();
|
||||
|
||||
$this->reConnect();
|
||||
$userRepo = $this->em->getRepository('JotunheimrUserBundle:User');
|
||||
|
||||
$usersMax = 10;
|
||||
foreach ($this->clientManipulator->getAll($topic) as $item) {
|
||||
if (!$usersMax) {
|
||||
break;
|
||||
}
|
||||
|
||||
$user = $userRepo->findOneByEmail(
|
||||
$item['client']->getEmail()
|
||||
);
|
||||
|
||||
$usersOnline[] = array(
|
||||
'id' => $user->getFacebookId(),
|
||||
'name' => $user->getRealName(),
|
||||
'email' => $user->getEmail(),
|
||||
'username' => $user->getUsername(),
|
||||
'online' => true
|
||||
);
|
||||
|
||||
$usersMax--;
|
||||
}
|
||||
|
||||
foreach ($userRepo->findBy(array(), array(), 20) as $item) {
|
||||
$exists = is_numeric(array_search($item->getEmail(), array_column($usersOnline, 'email')));
|
||||
|
||||
if (!$exists) {
|
||||
$usersOffline[] = array(
|
||||
'id' => $item->getFacebookId(),
|
||||
'name' => $item->getRealName(),
|
||||
'email' => $item->getEmail(),
|
||||
'username' => $item->getUsername(),
|
||||
'online' => false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return array_merge($usersOnline, $usersOffline);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle prod MySQL timeout
|
||||
*/
|
||||
private function reConnect()
|
||||
{
|
||||
try {
|
||||
$connection = $this->em->getConnection();
|
||||
|
||||
if (false === $connection->ping()) {
|
||||
$connection->close();
|
||||
$connection->connect();
|
||||
}
|
||||
} catch (PDOException $ex) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -780,7 +780,11 @@ class SymfonyRequirements extends RequirementCollection
|
||||
{
|
||||
$size = ini_get('realpath_cache_size');
|
||||
$size = trim($size);
|
||||
$unit = strtolower(substr($size, -1, 1));
|
||||
$unit = '';
|
||||
if (!ctype_digit($size)) {
|
||||
$unit = strtolower(substr($size, -1, 1));
|
||||
$size = (int) substr($size, 0, -1);
|
||||
}
|
||||
switch ($unit) {
|
||||
case 'g':
|
||||
return $size * 1024 * 1024 * 1024;
|
||||
|
||||
@@ -270,7 +270,7 @@ $hasMinorProblems = (bool) count($minorProblems);
|
||||
}
|
||||
.sf-reset ul a,
|
||||
.sf-reset ul a:hover {
|
||||
background: url(../images/blue-arrow.png) no-repeat right 6px;
|
||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAICAYAAAAx8TU7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAFdJREFUeNpiYACBjjOhDEiACSggCKTLgXQ5TJARqhIkcReIKxgqTGYxwvV0nDEGkmeAOIwJySiQ4HsgvseIpGo3ELsCtZ9lRDIvDCiwhwHJPEFkJwEEGACq6hdnax8y1AAAAABJRU5ErkJggg==) no-repeat right 7px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.sf-reset ul, ol {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
var webpack = require("webpack");
|
||||
let webpack = require("webpack");
|
||||
process.env.NODE_ENV = 'production';
|
||||
var isProd = (process.env.NODE_ENV === 'production');
|
||||
let isProd = (process.env.NODE_ENV === 'production');
|
||||
|
||||
/**
|
||||
* Conditionally return a list of plugins to use based on the current environment.
|
||||
@@ -10,7 +10,7 @@ var isProd = (process.env.NODE_ENV === 'production');
|
||||
* @returns {Array}
|
||||
*/
|
||||
function getPlugins() {
|
||||
var plugins = [];
|
||||
let plugins = [];
|
||||
|
||||
/**
|
||||
* Always expose NODE_ENV to webpack, you can now use `process.env.NODE_ENV`
|
||||
@@ -33,10 +33,14 @@ function getPlugins() {
|
||||
return plugins;
|
||||
}
|
||||
|
||||
const config = module.exports = {
|
||||
const config = {
|
||||
module: {}
|
||||
};
|
||||
|
||||
let mineSeekerConfig = Object.assign({}, config, {
|
||||
entry: './web/bundles/mineseeker/js/mine-seeker.js',
|
||||
output: {
|
||||
path: './src/Mine/SeekerBundle/Resources/public/js',
|
||||
path: './src/Mine/SeekerBundle/Resources/public/js/build/mine-seeker/',
|
||||
filename: 'index.min.js'
|
||||
},
|
||||
module: {
|
||||
@@ -46,12 +50,34 @@ const config = module.exports = {
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel',
|
||||
query: {
|
||||
presets: ['es2015', 'react']
|
||||
presets: ['es2015', 'es2016', 'es2017', 'react']
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: getPlugins()
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = config;
|
||||
let mineUserListConfig = Object.assign({}, config, {
|
||||
entry: './web/bundles/mineseeker/js/mine-user-list.js',
|
||||
output: {
|
||||
path: './src/Mine/SeekerBundle/Resources/public/js/build/mine-user-list/',
|
||||
filename: 'index.min.js'
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel',
|
||||
query: {
|
||||
presets: ['es2015', 'es2016', 'es2017', 'react']
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
mineSeekerConfig,
|
||||
mineUserListConfig,
|
||||
];
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const config = module.exports = {
|
||||
const config = {
|
||||
module: {}
|
||||
};
|
||||
|
||||
let mineSeekerConfig = Object.assign({}, config, {
|
||||
entry: './web/bundles/mineseeker/js/mine-seeker.js',
|
||||
output: {
|
||||
path: './src/Mine/SeekerBundle/Resources/public/js',
|
||||
path: './src/Mine/SeekerBundle/Resources/public/js/build/mine-seeker/',
|
||||
filename: 'index.js'
|
||||
},
|
||||
module: {
|
||||
@@ -13,11 +17,34 @@ const config = module.exports = {
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel',
|
||||
query: {
|
||||
presets: ['es2015', 'react']
|
||||
presets: ['es2015', 'es2016', 'es2017', 'react']
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
module.exports = config;
|
||||
let mineUserListConfig = Object.assign({}, config, {
|
||||
entry: './web/bundles/mineseeker/js/mine-user-list.js',
|
||||
output: {
|
||||
path: './src/Mine/SeekerBundle/Resources/public/js/build/mine-user-list/',
|
||||
filename: 'index.js'
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel',
|
||||
query: {
|
||||
presets: ['es2015', 'es2016', 'es2017', 'react']
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
mineSeekerConfig,
|
||||
mineUserListConfig,
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user