Private
Public Access
1
0

Compare commits

..

1 Commits

65 changed files with 6573 additions and 3524 deletions

View File

@@ -1,3 +1,3 @@
{
"presets": ["es2015", "es2016", "es2017", "react"]
"presets": ["es2015", "react"]
}

3
.gitignore vendored
View File

@@ -35,5 +35,6 @@ phpunit-report/*
/src/Mine/SeekerBundle/Resources/public/js/src/
nohup.out
src/Mine/SeekerBundle/Resources/public/js/build/
src/Mine/SeekerBundle/Resources/public/js/index.js
src/Mine/SeekerBundle/Resources/public/js/index.min.js
npm-debug.log

View File

@@ -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 php bin/console gos:websocket:server --env=prod &
$ nohup bin/console gos:websocket:server &
2.) React JS WebPack watch generator w/ babel presets: es2015, react
@@ -32,9 +32,4 @@ 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
4.) Stunnel config
$ sudo nano /etc/stunnel/stunnel.conf
ssh xxsvci@laszlolang.com -i ~/.ssh/id_rsa_laszlolang

View File

@@ -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(),

View File

@@ -10,8 +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
mailer_user: 7system7@gmail.com
mailer_name: Lang
framework:
#esi: ~
@@ -29,11 +29,11 @@ framework:
default_locale: "%locale%"
trusted_hosts: ~
trusted_proxies: ~
session:
session: ~
# http://symfony.com/doc/current/reference/configuration/framework.html#handler-id
# handler_id: session.handler.native_file
# save_path: "%kernel.root_dir%/../var/sessions/%kernel.environment%"
handler_id: session.handler.pdo
# handler_id: session.handler.pdo
fragments: ~
http_method_override: true
assets: ~
@@ -43,7 +43,7 @@ twig:
debug: "%kernel.debug%"
strict_variables: "%kernel.debug%"
globals:
version: "0.42.21 (beta8)"
version: "0.37.18 (beta7)"
facebook_api: "%facebook.api%"
facebook_scope: "%facebook.scope%"
facebook_api_version: "%facebook.version%"
@@ -90,8 +90,8 @@ fos_user:
firewall_name: secured_area
user_class: Jotunheimr\UserBundle\Entity\User
from_email:
address: "%fos.email%"
sender_name: "%fos.name%"
address: "%mailer_user%"
sender_name: "%mailer_name%"
# Facebook OAuth
hwi_oauth:
@@ -108,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

View File

@@ -4,7 +4,7 @@ parameters:
database_port: null
database_name: mine
database_user: root
database_password: bazmeg
database_password: ~
mailer_transport: smtp
mailer_host: 127.0.0.1
mailer_user: null

View File

@@ -2,12 +2,9 @@
parameters:
database_host: 127.0.0.1
database_port: null
# database_name: xxsvci_mineseeker
# database_user: xxsvci_mine
# database_password: "XTw#8qC$faa*"
database_name: mine
database_user: root
database_password: "bazmeg"
database_password: 'bazmeg'
mailer_transport: smtp
mailer_host: 127.0.0.1
mailer_user: null

View File

@@ -17,9 +17,9 @@
}
},
"require": {
"php": ">=5.5.9",
"symfony/symfony": "3.1.*",
"doctrine/orm": "^2.5",
"php": ">=7.3",
"symfony/symfony": "3.*",
"doctrine/orm": "^2.6",
"doctrine/doctrine-bundle": "^1.6",
"doctrine/doctrine-cache-bundle": "^1.3",
"symfony/swiftmailer-bundle": "^2.3",
@@ -36,9 +36,10 @@
"snc/redis-bundle": "^2.0",
"hwi/oauth-bundle": "^0.5.1",
"cleentfaar/slack-bundle": "^0.20.1",
"symfony/translation": "^3.2"
"ext-json": "*"
},
"require-dev": {
"roave/security-advisories": "dev-master",
"sensio/generator-bundle": "^3.0",
"symfony/phpunit-bridge": "^3.0"
},

3470
composer.lock generated

File diff suppressed because it is too large Load Diff

1
node_modules Symbolic link
View File

@@ -0,0 +1 @@
src/Mine/SeekerBundle/Resources/public/js/node

3543
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -9,8 +9,6 @@
"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",
@@ -18,7 +16,7 @@
"react-dom": "^15.3.2",
"uglify-js": "^2.7.4",
"uglifycss": "0.0.25",
"webpack": "^1.13.3"
"webpack": "^1.15.0"
},
"devDependencies": {},
"scripts": {
@@ -35,6 +33,6 @@
"multiplayer",
"websocket"
],
"author": "Laszlo Lang <system7> www.laszlolang.com",
"author": "Laszlo Lang <system7>",
"license": "ISC"
}

View File

@@ -26,9 +26,6 @@ class User extends BaseUser
*/
private $facebookId;
/**
* @ORM\Column(name="facebook_access_token", type="string", length=255, nullable=true)
*/
private $facebookAccessToken;
/**

View File

@@ -251,11 +251,6 @@ 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;
}
@@ -281,6 +276,10 @@ main .technologies img {
margin: 20px;
}
main .technologies h1 {
font-weight: bold;
}
footer {
background: #414040;
width: 100%;
@@ -321,7 +320,7 @@ footer nav ul li a:hover {
color: #FFFFFF;
}
@media screen and (max-width: 1200px) {
@media screen and (max-width: 1100px) {
header section #id_welcome {
align-items: center;
justify-content: center;

View File

@@ -2,7 +2,7 @@
{% trans_default_domain 'FOSUserBundle' %}
{% block title %} - Registration{% endblock %}
{% block title %} - Sign up{% endblock %}
{% block fos_user_content %}
<section class="header-content">
@@ -12,7 +12,7 @@
border="0"/>
</a>
</div>
<h1> Registration </h1>
<h1> Sign up </h1>
<div class="db">
<form action="{{ path('fos_user_registration_register') }}"
method="post">

View File

@@ -2,7 +2,7 @@
{% trans_default_domain 'FOSUserBundle' %}
{% block title %} - Login{% endblock %}
{% block title %} - Sign in{% endblock %}
{% block fos_user_content %}
{% if error %}
@@ -16,7 +16,7 @@
border="0"/>
</a>
</div>
<h1>Login</h1>
<h1>Sign in</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" autofocus />
placeholder="Username or Email"/>
<input type="password" id="password" name="_password"
class="form-input"

View File

@@ -35,7 +35,6 @@
<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});

View File

@@ -72,7 +72,6 @@ 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()));

View File

@@ -5,64 +5,21 @@ namespace Mine\SeekerBundle\Controller;
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(Request $request)
public function indexAction()
{
// $apiClient = new ApiClient('xoxp-107639806167-107029084564-115427085733-cccaa4f96c89c87ce680c7f22acfd001');
//
// $usersListPayload = new UsersListPayload();
// $usersList = $apiClient->send($usersListPayload);
//
// dump($usersList);
//
// $payload = new ChatPostMessagePayload();
// $payload->setChannel('#general');
// $payload->setAsUser(true);
// $payload->setText('Hello world!');
// $response = $apiClient->send($payload);
// if ($response->isOk()) {
// dump('bazmeg');
// } else {
// dump($response->getError());
// dump($response->getErrorExplanation());
// }
return $this->render('MineSeekerBundle:Game:index.html.twig', array(
'env' => $this->container->getParameter('kernel.environment'),
'ssl' => $request->isSecure() ? 'true' : 'false',
));
return $this->render('MineSeekerBundle:Game:index.html.twig');
}
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',
));
}
public function slackAction(Request $request)
{
}
public function privacyAction()
{
return $this->render('MineSeekerBundle:Official:privacy.html.twig');

View File

@@ -5,10 +5,7 @@ namespace Mine\SeekerBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Class Gamer
*
* @package Mine\SeekerBundle\Entity
* @author system7 <https://www.laszlolang.com>
* Gamer
*
* @ORM\Table(name="gamer")
* @ORM\Entity(repositoryClass="Mine\SeekerBundle\Repository\GamerRepository")

View File

@@ -7,10 +7,7 @@ use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* Class Grid
*
* @package Mine\SeekerBundle\Entity
* @author system7 <https://www.laszlolang.com>
* Grid
*
* @ORM\Table(name="grid")
* @ORM\Entity(repositoryClass="Mine\SeekerBundle\Repository\GridRepository")

View File

@@ -5,10 +5,7 @@ namespace Mine\SeekerBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Class GridRow
*
* @package Mine\SeekerBundle\Entity
* @author system7 <https://www.laszlolang.com>
* GridRow
*
* @ORM\Table(name="grid_row")
* @ORM\Entity(repositoryClass="Mine\SeekerBundle\Repository\GridRowRepository")

View File

@@ -8,10 +8,7 @@ use Doctrine\ORM\Mapping as ORM;
use Jotunheimr\UserBundle\Entity\User;
/**
* Class PlayedGame
*
* @package Mine\SeekerBundle\Entity
* @author system7 <https://www.laszlolang.com>
* PlayedGame
*
* @ORM\Table(name="played_game")
* @ORM\Entity(repositoryClass="Mine\SeekerBundle\Repository\PlayedGameRepository")

View File

@@ -443,52 +443,4 @@ 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;
}
}

View File

@@ -5,10 +5,7 @@ namespace Mine\SeekerBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Class Step
*
* @package Mine\SeekerBundle\Entity
* @author system7 <https://www.laszlolang.com>
* Step
*
* @ORM\Table(name="step")
* @ORM\Entity(repositoryClass="Mine\SeekerBundle\Repository\StepRepository")

View File

@@ -162,28 +162,4 @@ 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;
}
}

View File

@@ -7,12 +7,6 @@ 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
{
/**

View File

@@ -5,12 +5,6 @@ namespace Mine\SeekerBundle\Periodic;
use Gos\Bundle\WebSocketBundle\Periodic\PdoPeriodicPing;
use Gos\Bundle\WebSocketBundle\Periodic\PeriodicInterface;
/**
* Class MinePeriodic
*
* @package Mine\SeekerBundle\Periodic
* @author system7 <https://www.laszlolang.com>
*/
class MinePeriodic implements PeriodicInterface
{
/** @var PdoPeriodicPing */
@@ -24,8 +18,7 @@ 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()
{

View File

@@ -3,10 +3,10 @@
namespace Mine\SeekerBundle\Repository;
/**
* Class GamerRepository
* GamerRepository
*
* @package Mine\SeekerBundle\Repository
* @author system7 <https://www.laszlolang.com>
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class GamerRepository extends \Doctrine\ORM\EntityRepository
{

View File

@@ -3,10 +3,10 @@
namespace Mine\SeekerBundle\Repository;
/**
* Class GridColRepository
* GridColRepository
*
* @package Mine\SeekerBundle\Repository
* @author system7 <https://www.laszlolang.com>
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class GridColRepository extends \Doctrine\ORM\EntityRepository
{

View File

@@ -3,10 +3,10 @@
namespace Mine\SeekerBundle\Repository;
/**
* Class GridRepository
* GridRepository
*
* @package Mine\SeekerBundle\Repository
* @author system7 <https://www.laszlolang.com>
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class GridRepository extends \Doctrine\ORM\EntityRepository
{

View File

@@ -4,10 +4,10 @@ namespace Mine\SeekerBundle\Repository;
use Doctrine\ORM\EntityRepository;
/**
* Class GridRowRepository
* GridRowRepository
*
* @package Mine\SeekerBundle\Repository
* @author system7 <https://www.laszlolang.com>
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class GridRowRepository extends EntityRepository
{

View File

@@ -3,10 +3,10 @@
namespace Mine\SeekerBundle\Repository;
/**
* Class PlayedGameRepository
* PlayedGameRepository
*
* @package Mine\SeekerBundle\Repository
* @author system7 <https://www.laszlolang.com>
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class PlayedGameRepository extends \Doctrine\ORM\EntityRepository
{

View File

@@ -3,10 +3,10 @@
namespace Mine\SeekerBundle\Repository;
/**
* Class StepRepository
* StepRepository
*
* @package Mine\SeekerBundle\Repository
* @author system7 <https://www.laszlolang.com>
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class StepRepository extends \Doctrine\ORM\EntityRepository
{

View File

@@ -10,18 +10,18 @@ doctrine_cache:
type: redis
alias: gos_web_socket.client_storage.driver.redis
# SNC
snc_redis:
clients:
cache:
type: predis
alias: cache
dsn: redis://localhost/2
logging: "%kernel.debug%"
options:
profile: 2.2
connection_timeout: 10
read_write_timeout: 30
## SNC
#snc_redis:
# clients:
# cache:
# type: predis
# alias: cache
# dsn: redis://localhost/2
# logging: "%kernel.debug%"
# options:
# profile: 2.2
# connection_timeout: 10
# read_write_timeout: 30
# Web Socket Configuration
gos_web_socket:
@@ -33,10 +33,10 @@ gos_web_socket:
- "@MineSeekerBundle/Resources/config/pubsub/routing.yml"
client:
firewall: secured_area
session_handler: "@session.handler.pdo"
storage:
driver: "@gos_web_socket.client_storage.driver.predis"
ttl: 28800 #(optionally) time to live if you use redis driver
prefix: client #(optionally) prefix if you use redis driver, create key "client:1" instead key "1"
# session_handler: "@session.handler.pdo"
# storage:
# driver: "@gos_web_socket.client_storage.driver.predis"
# ttl: 28800 #(optionally) time to live if you use redis driver
# prefix: client #(optionally) prefix if you use redis driver, create key "client:1" instead key "1"
periodic:
- "@mineseeker.periodic"

View File

@@ -1,4 +1,4 @@
# MineSeeker Topic Configuration
# Topic Configuration
mineseeker_topic:
channel: mineseeker/channel/{game}
handler:
@@ -7,12 +7,6 @@ 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}

View File

@@ -13,11 +13,6 @@ 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 }

View File

@@ -1,55 +1,33 @@
services:
pdo:
class: PDO
arguments:
dsn: "mysql:host=%database_host%;dbname=%database_name%"
user: "%database_user%"
passwd: "%database_password%"
calls:
- [ setAttribute, [3, 2] ] # \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION
session.handler.pdo:
class: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
public: false
arguments: ["@pdo", {lock_mode: 0}]
# config.yml --> gos_web_socket > storage > client > driver
gos_web_socket.client_storage.driver.predis:
class: Gos\Bundle\WebSocketBundle\Client\Driver\PredisDriver
arguments:
- "@snc_redis.cache"
# gos_web_socket.client_storage.driver.predis:
# class: Gos\Bundle\WebSocketBundle\Client\Driver\PredisDriver
# arguments:
# - "@snc_redis.cache"
mineseeker.periodic:
class: Mine\SeekerBundle\Periodic\MinePeriodic
tags:
- { name: gos_web_socket.periodic }
arguments:
ping: '@gos_web_socket.pdo.periodic_ping'
- '@gos_web_socket.pdo.periodic_ping'
mineseeker.game_service:
mineseeker.topic_sample_service:
class: Mine\SeekerBundle\Topic\MineseekerTopic
tags:
- { name: gos_web_socket.topic }
arguments:
clientManipulator: "@gos_web_socket.websocket.client_manipulator"
doctrine: '@doctrine.orm.entity_manager'
requestStack: '@request_stack'
- "@gos_web_socket.websocket.client_manipulator"
- '@doctrine.orm.entity_manager'
- '@request_stack'
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:
mineseeker.rpc_sample_service:
class: Mine\SeekerBundle\Rpc\MineseekerRpc
tags:
- { name: gos_web_socket.rpc }
arguments:
doctrine: '@doctrine.orm.entity_manager'
- '@doctrine.orm.entity_manager'
- '@logger'
gos_web_socket_server.client_event.listener:
class: Mine\SeekerBundle\EventListener\MineseekerClientEventListener

View File

@@ -141,162 +141,7 @@ header section div.buttons > a.small:hover {
transition: all 250ms ease-in-out;
}
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) {
@media screen and (max-width: 1100px) {
header section {
align-items: center;
justify-content: center;
@@ -321,6 +166,11 @@ main .user-request-container button:hover {
}
@media screen and (max-width: 500px) {
/*header {*/
/*min-height: 100%;*/
/*height: auto;*/
/*}*/
header section {
width: auto;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -2,12 +2,9 @@ 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={mineWrapper.dataset.env}
gameId={mineWrapper.dataset.gameId}
webPlayerName={mineWrapper.dataset.webPlayerName}
ssl={mineWrapper.dataset.ssl}/>,
mineWrapper
<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')
);

View File

@@ -1,39 +1,49 @@
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 services = new MineServices();
let gameAssoc = props.gameId !== '' ? props.gameId : services.randomString(50);
let gameAssoc = props.gameId !== '' ? props.gameId : this.makeGameAssoc(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,
replay: false
end: 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
*
@@ -61,21 +71,338 @@ class MineSeeker extends React.Component {
}
/**
* Unregistered
* http://mine.dev/re-play/I1Bx9UHZP5CWDnTZHpJqGTlkzehblfsbfz4A4xYaH9HFhBK2aN
* START
*
* Registered - Admin -> Lang
* http://mine.dev/re-play/bazmegdflgkjndfgkhjn
* @param payload
*/
makeGameStart(payload) {
/** every time the blue starts */
this.refs.gridControl.refs.userControl.setState({activePlayer: 1});
/** Set up player names w/ server data */
this.refs.gridControl.refs.userControl.refs.red.setState({
name: payload.users.red !== '' ? payload.users.red : payload.users.redAnon,
});
this.refs.gridControl.refs.userControl.refs.blue.setState({
name: payload.users.blue !== '' ? payload.users.blue : payload.users.blueAnon,
desc: this.refs.gridControl.state.webPlayer === 'blue'
? this.refs.gridControl.state.desc.you
: this.refs.gridControl.state.desc.buddy,
active: true,
});
this.refs.gridControl.setState({overlay: false});
}
/**
* THE END
*
* @param bluePoints
* @param redPoints
* @param resign
*/
makeGameEndIfItEnds(bluePoints, redPoints, resign = false) {
let redWins = redPoints > 25,
blueWins = bluePoints > 25;
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://localhost: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() {
/** is it a REPLAY */
window.location.pathname.indexOf('re-play') > 0
? this.setState({replay: true})
: this.setState({replay: false});
this.state.wsProcess.connectWithWebsocket(this);
this.connectWithWebsocket();
}
/**
@@ -100,7 +427,7 @@ class MineSeeker extends React.Component {
let points = this.makePointsCalcAndStep(coords);
/** THE END */
this.state.endProcess.makeGameEndIfItEnds(this, points.blue, points.red);
this.makeGameEndIfItEnds(points.blue, points.red);
let dataPack = {
'coords': coords,
@@ -125,7 +452,7 @@ class MineSeeker extends React.Component {
return (
<GridControl ref="gridControl"
env={this.props.env === 'dev'}
resign={this.state.endProcess.resign.bind(this.state.endProcess, this)}
resign={this.resign.bind(this)}
onClick={this.onClick.bind(this)}/>
);
}

View File

@@ -1,87 +0,0 @@
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;

View File

@@ -1,66 +0,0 @@
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;

View File

@@ -1,10 +0,0 @@
import React from 'react';
class MineStep extends React.Component {
constructor() {
super();
}
}
export default MineStep;

View File

@@ -1,266 +0,0 @@
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;

View File

@@ -1,33 +0,0 @@
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;

View File

@@ -1,12 +0,0 @@
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
);

View File

@@ -1,194 +0,0 @@
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;

View File

@@ -48,8 +48,8 @@
{{ 'layout.logout'|trans({}, 'FOSUserBundle') }}
</a>
{% else %}
<a href="{{ path('fos_user_registration_register') }}" class="small">Registration</a> &middot;
<a href="{{ path('fos_user_security_login') }}" class="small">Login</a>
<a href="{{ path('fos_user_registration_register') }}" class="small">Sign up</a> &middot;
<a href="{{ path('fos_user_security_login') }}" class="small">Sign in</a>
{% endif %}
</div>
<h3>
@@ -63,14 +63,6 @@
{% 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"
@@ -101,7 +93,6 @@
{% 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&amp;subset=latin-ext"
rel="stylesheet">
@@ -116,31 +107,11 @@
{% block javascripts %}
{{ parent() }}
{% 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 %}
<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>
{% endblock %}

View File

@@ -10,8 +10,7 @@
<div id="mine-wrapper"
data-env="{{ env }}"
data-ssl="{{ ssl }}"
data-game-id="{{ app.request.get('gameAssoc') }}"
data-web-player-name="{{ app.user.username is defined ? app.user.username : '' }}">
data-game-id="{{ app.request.get('gameAssoc') }}">
</div>
</div>
{% endblock %}
@@ -21,8 +20,7 @@
<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 %}
@@ -48,7 +46,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/build/mine-seeker/index.js' %}
'@MineSeekerBundle/Resources/public/js/index.js' %}
<script type="text/javascript" src="{{ asset_url }}"></script>
{% endjavascripts %}
{% else %}
@@ -57,7 +55,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/build/mine-seeker/index.min.js' %}
'@MineSeekerBundle/Resources/public/js/index.min.js' %}
<script type="text/javascript" src="{{ asset_url }}"></script>
{% endjavascripts %}
{% endif %}

View File

@@ -1,21 +1,14 @@
<script type="text/javascript">
$(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
// });
};
});
window.fbAsyncInit = function () {
FB.init({
appId: '{{ facebook_api }}',
xfbml: true,
cookie: true,
status: true,
oauth: true,
version: '{{ facebook_api_version }}'
});
};
(function (d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];

View File

@@ -4,35 +4,31 @@ 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 Psr\Log\LoggerInterface;
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;
private $logger;
/**
* MineseekerRpc constructor.
*
* @param EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager)
public function __construct(EntityManager $entityManager, LoggerInterface $logger)
{
$this->em = $entityManager;
$this->logger = $logger;
}
/**
@@ -47,53 +43,94 @@ class MineseekerRpc implements RpcInterface
/**
* @param ConnectionInterface $connection
* @param WampRequest $request
* @param array $params
*
* @param WampRequest $request
* @param array $params
* @return boolean
*/
public function startGame(ConnectionInterface $connection, WampRequest $request, array $params)
{
$this->reConnect($params);
return $this->saveGrid($params);
}
/**
* @param ConnectionInterface $connection
* @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);
return base64_encode(json_encode($grid));
$users = null !== $grid ? $this->getUsers($params) : null;
return base64_encode(json_encode(
array(
'grid' => $grid,
'users' => $users
)
));
}
/**
* @param $data
*
* @return array|bool
* @throws OptimisticLockException
* @param $gameAssoc
* @return array
*/
private function getGrid($data)
private function getGrid($gameAssoc)
{
$this->reConnect();
$getsee = array();
$this->em->clear();
$grid = array();
/** @var PlayedGame $game */
$game = $this->em
$grid = $this->em
->getRepository('MineSeekerBundle:PlayedGame')
->findOneByGameAssoc($data[1]);
->findOneByGameAssoc($gameAssoc);
if (null !== $game) {
/** @var GridRow $row */
foreach ($game->getGrid()->getGridRow()->toArray() as $row) {
$grid[] = $row->getGridCol();
if (null !== $grid) {
foreach ($grid->getGrid()->getGridRow()->toArray() as $row) {
$getsee[] = $row->getGridCol();
}
return $grid;
} else {
$this->saveGrid($data);
return $this->getGrid($data);
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() : ''
);
}
/**
* @param $data
*
* @return boolean
* @throws OptimisticLockException
*/
private function saveGrid($data)
{
@@ -137,8 +174,8 @@ class MineseekerRpc implements RpcInterface
$connection->close();
$connection->connect();
}
} catch (PDOException $ex) {
throw new NotFoundHttpException();
} catch(PDOException $ex) {
$this->logger->error($ex->getMessage());
}
}
}

View File

@@ -4,25 +4,15 @@ 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 */
@@ -37,9 +27,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)
{
@@ -52,11 +42,9 @@ 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)
{
@@ -70,14 +58,13 @@ class MineseekerTopic implements TopicInterface
} else {
$users = $this->controlUsers($topic, $userName, $user);
$topic->broadcast(array(
$topic->broadcast([
'userTopicId' => $connection->resourceId,
'channel' => $topic->getId(),
'user' => $userName,
'userCnt' => $topic->count(),
'users' => $users,
'steps' => $this->getSteps($topic),
));
'users' => $users
]);
}
}
@@ -85,9 +72,8 @@ 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)
@@ -100,16 +86,14 @@ 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)
{
@@ -147,15 +131,12 @@ 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);
@@ -168,17 +149,14 @@ class MineseekerTopic implements TopicInterface
/**
* Save steps and point information to database
*
* @param Topic $topic
* @param $event
*
* @throws OptimisticLockException
* @param $topic
* @param $event
*/
private function saveStepToDb($topic, $event)
{
$this->reConnect();
$gameAssoc = explode('/', $topic->getId())[2];
/** @var PlayedGame $playedGame */
$playedGame = $this->em
->getRepository('MineSeekerBundle:PlayedGame')
->findOneByGameAssoc($gameAssoc);
@@ -202,44 +180,19 @@ 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);
@@ -255,7 +208,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($playedGame, $userName, $user, $topic->count());
$users = $this->saveUserToDb($topic, $userName, $user, $topic->count());
}
return $users;
@@ -264,17 +217,20 @@ class MineseekerTopic implements TopicInterface
/**
* Save user data to database
*
* @param PlayedGame $playedGame
* @param FOSUser $userName
* @param $user
* @param $count
*
* @param $topic
* @param $userName
* @param $user
* @param $count
* @return array
* @throws OptimisticLockException
*/
private function saveUserToDb($playedGame, $userName, $user, $count)
private function saveUserToDb($topic, $userName, $user, $count)
{
$this->reConnect();
$gameAssoc = explode('/', $topic->getId())[2];
$playedGame = $this->em
->getRepository('MineSeekerBundle:PlayedGame')
->findOneByGameAssoc($gameAssoc);
/** the user is not anonym */
if (!is_string($user)) {
@@ -319,8 +275,7 @@ class MineseekerTopic implements TopicInterface
/**
* Get user collection from PlayedGame entity
*
* @param PlayedGame $playedGame
*
* @param $playedGame
* @return array
*/
private function getUserCollection($playedGame)
@@ -345,8 +300,8 @@ class MineseekerTopic implements TopicInterface
$connection->close();
$connection->connect();
}
} catch (PDOException $ex) {
throw new NotFoundHttpException();
} catch(PDOException $ex) {
throw PDOException::class;
}
}
}

View File

@@ -1,283 +0,0 @@
<?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();
}
}
}

View File

@@ -1,823 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/*
* Users of PHP 5.2 should be able to run the requirements checks.
* This is why the file and all classes must be compatible with PHP 5.2+
* (e.g. not using namespaces and closures).
*
* ************** CAUTION **************
*
* DO NOT EDIT THIS FILE as it will be overridden by Composer as part of
* the installation/update process. The original file resides in the
* SensioDistributionBundle.
*
* ************** CAUTION **************
*/
/**
* Represents a single PHP requirement, e.g. an installed extension.
* It can be a mandatory requirement or an optional recommendation.
* There is a special subclass, named PhpIniRequirement, to check a php.ini configuration.
*
* @author Tobias Schultze <http://tobion.de>
*/
class Requirement
{
private $fulfilled;
private $testMessage;
private $helpText;
private $helpHtml;
private $optional;
/**
* Constructor that initializes the requirement.
*
* @param bool $fulfilled Whether the requirement is fulfilled
* @param string $testMessage The message for testing the requirement
* @param string $helpHtml The help text formatted in HTML for resolving the problem
* @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
* @param bool $optional Whether this is only an optional recommendation not a mandatory requirement
*/
public function __construct($fulfilled, $testMessage, $helpHtml, $helpText = null, $optional = false)
{
$this->fulfilled = (bool) $fulfilled;
$this->testMessage = (string) $testMessage;
$this->helpHtml = (string) $helpHtml;
$this->helpText = null === $helpText ? strip_tags($this->helpHtml) : (string) $helpText;
$this->optional = (bool) $optional;
}
/**
* Returns whether the requirement is fulfilled.
*
* @return bool true if fulfilled, otherwise false
*/
public function isFulfilled()
{
return $this->fulfilled;
}
/**
* Returns the message for testing the requirement.
*
* @return string The test message
*/
public function getTestMessage()
{
return $this->testMessage;
}
/**
* Returns the help text for resolving the problem.
*
* @return string The help text
*/
public function getHelpText()
{
return $this->helpText;
}
/**
* Returns the help text formatted in HTML.
*
* @return string The HTML help
*/
public function getHelpHtml()
{
return $this->helpHtml;
}
/**
* Returns whether this is only an optional recommendation and not a mandatory requirement.
*
* @return bool true if optional, false if mandatory
*/
public function isOptional()
{
return $this->optional;
}
}
/**
* Represents a PHP requirement in form of a php.ini configuration.
*
* @author Tobias Schultze <http://tobion.de>
*/
class PhpIniRequirement extends Requirement
{
/**
* Constructor that initializes the requirement.
*
* @param string $cfgName The configuration name used for ini_get()
* @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false,
* or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement
* @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false.
* This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin.
* Example: You require a config to be true but PHP later removes this config and defaults it to true internally.
* @param string|null $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived)
* @param string|null $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived)
* @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
* @param bool $optional Whether this is only an optional recommendation not a mandatory requirement
*/
public function __construct($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null, $optional = false)
{
$cfgValue = ini_get($cfgName);
if (is_callable($evaluation)) {
if (null === $testMessage || null === $helpHtml) {
throw new InvalidArgumentException('You must provide the parameters testMessage and helpHtml for a callback evaluation.');
}
$fulfilled = call_user_func($evaluation, $cfgValue);
} else {
if (null === $testMessage) {
$testMessage = sprintf('%s %s be %s in php.ini',
$cfgName,
$optional ? 'should' : 'must',
$evaluation ? 'enabled' : 'disabled'
);
}
if (null === $helpHtml) {
$helpHtml = sprintf('Set <strong>%s</strong> to <strong>%s</strong> in php.ini<a href="#phpini">*</a>.',
$cfgName,
$evaluation ? 'on' : 'off'
);
}
$fulfilled = $evaluation == $cfgValue;
}
parent::__construct($fulfilled || ($approveCfgAbsence && false === $cfgValue), $testMessage, $helpHtml, $helpText, $optional);
}
}
/**
* A RequirementCollection represents a set of Requirement instances.
*
* @author Tobias Schultze <http://tobion.de>
*/
class RequirementCollection implements IteratorAggregate
{
/**
* @var Requirement[]
*/
private $requirements = array();
/**
* Gets the current RequirementCollection as an Iterator.
*
* @return Traversable A Traversable interface
*/
public function getIterator()
{
return new ArrayIterator($this->requirements);
}
/**
* Adds a Requirement.
*
* @param Requirement $requirement A Requirement instance
*/
public function add(Requirement $requirement)
{
$this->requirements[] = $requirement;
}
/**
* Adds a mandatory requirement.
*
* @param bool $fulfilled Whether the requirement is fulfilled
* @param string $testMessage The message for testing the requirement
* @param string $helpHtml The help text formatted in HTML for resolving the problem
* @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
*/
public function addRequirement($fulfilled, $testMessage, $helpHtml, $helpText = null)
{
$this->add(new Requirement($fulfilled, $testMessage, $helpHtml, $helpText, false));
}
/**
* Adds an optional recommendation.
*
* @param bool $fulfilled Whether the recommendation is fulfilled
* @param string $testMessage The message for testing the recommendation
* @param string $helpHtml The help text formatted in HTML for resolving the problem
* @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
*/
public function addRecommendation($fulfilled, $testMessage, $helpHtml, $helpText = null)
{
$this->add(new Requirement($fulfilled, $testMessage, $helpHtml, $helpText, true));
}
/**
* Adds a mandatory requirement in form of a php.ini configuration.
*
* @param string $cfgName The configuration name used for ini_get()
* @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false,
* or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement
* @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false.
* This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin.
* Example: You require a config to be true but PHP later removes this config and defaults it to true internally.
* @param string $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived)
* @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived)
* @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
*/
public function addPhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null)
{
$this->add(new PhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence, $testMessage, $helpHtml, $helpText, false));
}
/**
* Adds an optional recommendation in form of a php.ini configuration.
*
* @param string $cfgName The configuration name used for ini_get()
* @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false,
* or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement
* @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false.
* This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin.
* Example: You require a config to be true but PHP later removes this config and defaults it to true internally.
* @param string $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived)
* @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived)
* @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags)
*/
public function addPhpIniRecommendation($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null)
{
$this->add(new PhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence, $testMessage, $helpHtml, $helpText, true));
}
/**
* Adds a requirement collection to the current set of requirements.
*
* @param RequirementCollection $collection A RequirementCollection instance
*/
public function addCollection(RequirementCollection $collection)
{
$this->requirements = array_merge($this->requirements, $collection->all());
}
/**
* Returns both requirements and recommendations.
*
* @return Requirement[]
*/
public function all()
{
return $this->requirements;
}
/**
* Returns all mandatory requirements.
*
* @return Requirement[]
*/
public function getRequirements()
{
$array = array();
foreach ($this->requirements as $req) {
if (!$req->isOptional()) {
$array[] = $req;
}
}
return $array;
}
/**
* Returns the mandatory requirements that were not met.
*
* @return Requirement[]
*/
public function getFailedRequirements()
{
$array = array();
foreach ($this->requirements as $req) {
if (!$req->isFulfilled() && !$req->isOptional()) {
$array[] = $req;
}
}
return $array;
}
/**
* Returns all optional recommendations.
*
* @return Requirement[]
*/
public function getRecommendations()
{
$array = array();
foreach ($this->requirements as $req) {
if ($req->isOptional()) {
$array[] = $req;
}
}
return $array;
}
/**
* Returns the recommendations that were not met.
*
* @return Requirement[]
*/
public function getFailedRecommendations()
{
$array = array();
foreach ($this->requirements as $req) {
if (!$req->isFulfilled() && $req->isOptional()) {
$array[] = $req;
}
}
return $array;
}
/**
* Returns whether a php.ini configuration is not correct.
*
* @return bool php.ini configuration problem?
*/
public function hasPhpIniConfigIssue()
{
foreach ($this->requirements as $req) {
if (!$req->isFulfilled() && $req instanceof PhpIniRequirement) {
return true;
}
}
return false;
}
/**
* Returns the PHP configuration file (php.ini) path.
*
* @return string|false php.ini file path
*/
public function getPhpIniConfigPath()
{
return get_cfg_var('cfg_file_path');
}
}
/**
* This class specifies all requirements and optional recommendations that
* are necessary to run the Symfony Standard Edition.
*
* @author Tobias Schultze <http://tobion.de>
* @author Fabien Potencier <fabien@symfony.com>
*/
class SymfonyRequirements extends RequirementCollection
{
const LEGACY_REQUIRED_PHP_VERSION = '5.3.3';
const REQUIRED_PHP_VERSION = '5.5.9';
/**
* Constructor that initializes the requirements.
*/
public function __construct()
{
/* mandatory requirements follow */
$installedPhpVersion = phpversion();
$requiredPhpVersion = $this->getPhpRequiredVersion();
$this->addRecommendation(
$requiredPhpVersion,
'Vendors should be installed in order to check all requirements.',
'Run the <code>composer install</code> command.',
'Run the "composer install" command.'
);
if (false !== $requiredPhpVersion) {
$this->addRequirement(
version_compare($installedPhpVersion, $requiredPhpVersion, '>='),
sprintf('PHP version must be at least %s (%s installed)', $requiredPhpVersion, $installedPhpVersion),
sprintf('You are running PHP version "<strong>%s</strong>", but Symfony needs at least PHP "<strong>%s</strong>" to run.
Before using Symfony, upgrade your PHP installation, preferably to the latest version.',
$installedPhpVersion, $requiredPhpVersion),
sprintf('Install PHP %s or newer (installed version is %s)', $requiredPhpVersion, $installedPhpVersion)
);
}
$this->addRequirement(
version_compare($installedPhpVersion, '5.3.16', '!='),
'PHP version must not be 5.3.16 as Symfony won\'t work properly with it',
'Install PHP 5.3.17 or newer (or downgrade to an earlier PHP version)'
);
$this->addRequirement(
is_dir(__DIR__.'/../vendor/composer'),
'Vendor libraries must be installed',
'Vendor libraries are missing. Install composer following instructions from <a href="http://getcomposer.org/">http://getcomposer.org/</a>. '.
'Then run "<strong>php composer.phar install</strong>" to install them.'
);
$cacheDir = is_dir(__DIR__.'/../var/cache') ? __DIR__.'/../var/cache' : __DIR__.'/cache';
$this->addRequirement(
is_writable($cacheDir),
'app/cache/ or var/cache/ directory must be writable',
'Change the permissions of either "<strong>app/cache/</strong>" or "<strong>var/cache/</strong>" directory so that the web server can write into it.'
);
$logsDir = is_dir(__DIR__.'/../var/logs') ? __DIR__.'/../var/logs' : __DIR__.'/logs';
$this->addRequirement(
is_writable($logsDir),
'app/logs/ or var/logs/ directory must be writable',
'Change the permissions of either "<strong>app/logs/</strong>" or "<strong>var/logs/</strong>" directory so that the web server can write into it.'
);
if (version_compare($installedPhpVersion, '7.0.0', '<')) {
$this->addPhpIniRequirement(
'date.timezone', true, false,
'date.timezone setting must be set',
'Set the "<strong>date.timezone</strong>" setting in php.ini<a href="#phpini">*</a> (like Europe/Paris).'
);
}
if (false !== $requiredPhpVersion && version_compare($installedPhpVersion, $requiredPhpVersion, '>=')) {
$timezones = array();
foreach (DateTimeZone::listAbbreviations() as $abbreviations) {
foreach ($abbreviations as $abbreviation) {
$timezones[$abbreviation['timezone_id']] = true;
}
}
$this->addRequirement(
isset($timezones[@date_default_timezone_get()]),
sprintf('Configured default timezone "%s" must be supported by your installation of PHP', @date_default_timezone_get()),
'Your default timezone is not supported by PHP. Check for typos in your <strong>php.ini</strong> file and have a look at the list of deprecated timezones at <a href="http://php.net/manual/en/timezones.others.php">http://php.net/manual/en/timezones.others.php</a>.'
);
}
$this->addRequirement(
function_exists('iconv'),
'iconv() must be available',
'Install and enable the <strong>iconv</strong> extension.'
);
$this->addRequirement(
function_exists('json_encode'),
'json_encode() must be available',
'Install and enable the <strong>JSON</strong> extension.'
);
$this->addRequirement(
function_exists('session_start'),
'session_start() must be available',
'Install and enable the <strong>session</strong> extension.'
);
$this->addRequirement(
function_exists('ctype_alpha'),
'ctype_alpha() must be available',
'Install and enable the <strong>ctype</strong> extension.'
);
$this->addRequirement(
function_exists('token_get_all'),
'token_get_all() must be available',
'Install and enable the <strong>Tokenizer</strong> extension.'
);
$this->addRequirement(
function_exists('simplexml_import_dom'),
'simplexml_import_dom() must be available',
'Install and enable the <strong>SimpleXML</strong> extension.'
);
if (function_exists('apc_store') && ini_get('apc.enabled')) {
if (version_compare($installedPhpVersion, '5.4.0', '>=')) {
$this->addRequirement(
version_compare(phpversion('apc'), '3.1.13', '>='),
'APC version must be at least 3.1.13 when using PHP 5.4',
'Upgrade your <strong>APC</strong> extension (3.1.13+).'
);
} else {
$this->addRequirement(
version_compare(phpversion('apc'), '3.0.17', '>='),
'APC version must be at least 3.0.17',
'Upgrade your <strong>APC</strong> extension (3.0.17+).'
);
}
}
$this->addPhpIniRequirement('detect_unicode', false);
if (extension_loaded('suhosin')) {
$this->addPhpIniRequirement(
'suhosin.executor.include.whitelist',
create_function('$cfgValue', 'return false !== stripos($cfgValue, "phar");'),
false,
'suhosin.executor.include.whitelist must be configured correctly in php.ini',
'Add "<strong>phar</strong>" to <strong>suhosin.executor.include.whitelist</strong> in php.ini<a href="#phpini">*</a>.'
);
}
if (extension_loaded('xdebug')) {
$this->addPhpIniRequirement(
'xdebug.show_exception_trace', false, true
);
$this->addPhpIniRequirement(
'xdebug.scream', false, true
);
$this->addPhpIniRecommendation(
'xdebug.max_nesting_level',
create_function('$cfgValue', 'return $cfgValue > 100;'),
true,
'xdebug.max_nesting_level should be above 100 in php.ini',
'Set "<strong>xdebug.max_nesting_level</strong>" to e.g. "<strong>250</strong>" in php.ini<a href="#phpini">*</a> to stop Xdebug\'s infinite recursion protection erroneously throwing a fatal error in your project.'
);
}
$pcreVersion = defined('PCRE_VERSION') ? (float) PCRE_VERSION : null;
$this->addRequirement(
null !== $pcreVersion,
'PCRE extension must be available',
'Install the <strong>PCRE</strong> extension (version 8.0+).'
);
if (extension_loaded('mbstring')) {
$this->addPhpIniRequirement(
'mbstring.func_overload',
create_function('$cfgValue', 'return (int) $cfgValue === 0;'),
true,
'string functions should not be overloaded',
'Set "<strong>mbstring.func_overload</strong>" to <strong>0</strong> in php.ini<a href="#phpini">*</a> to disable function overloading by the mbstring extension.'
);
}
/* optional recommendations follow */
if (file_exists(__DIR__.'/../vendor/composer')) {
require_once __DIR__.'/../vendor/autoload.php';
try {
$r = new ReflectionClass('Sensio\Bundle\DistributionBundle\SensioDistributionBundle');
$contents = file_get_contents(dirname($r->getFileName()).'/Resources/skeleton/app/SymfonyRequirements.php');
} catch (ReflectionException $e) {
$contents = '';
}
$this->addRecommendation(
file_get_contents(__FILE__) === $contents,
'Requirements file should be up-to-date',
'Your requirements file is outdated. Run composer install and re-check your configuration.'
);
}
$this->addRecommendation(
version_compare($installedPhpVersion, '5.3.4', '>='),
'You should use at least PHP 5.3.4 due to PHP bug #52083 in earlier versions',
'Your project might malfunction randomly due to PHP bug #52083 ("Notice: Trying to get property of non-object"). Install PHP 5.3.4 or newer.'
);
$this->addRecommendation(
version_compare($installedPhpVersion, '5.3.8', '>='),
'When using annotations you should have at least PHP 5.3.8 due to PHP bug #55156',
'Install PHP 5.3.8 or newer if your project uses annotations.'
);
$this->addRecommendation(
version_compare($installedPhpVersion, '5.4.0', '!='),
'You should not use PHP 5.4.0 due to the PHP bug #61453',
'Your project might not work properly due to the PHP bug #61453 ("Cannot dump definitions which have method calls"). Install PHP 5.4.1 or newer.'
);
$this->addRecommendation(
version_compare($installedPhpVersion, '5.4.11', '>='),
'When using the logout handler from the Symfony Security Component, you should have at least PHP 5.4.11 due to PHP bug #63379 (as a workaround, you can also set invalidate_session to false in the security logout handler configuration)',
'Install PHP 5.4.11 or newer if your project uses the logout handler from the Symfony Security Component.'
);
$this->addRecommendation(
(version_compare($installedPhpVersion, '5.3.18', '>=') && version_compare($installedPhpVersion, '5.4.0', '<'))
||
version_compare($installedPhpVersion, '5.4.8', '>='),
'You should use PHP 5.3.18+ or PHP 5.4.8+ to always get nice error messages for fatal errors in the development environment due to PHP bug #61767/#60909',
'Install PHP 5.3.18+ or PHP 5.4.8+ if you want nice error messages for all fatal errors in the development environment.'
);
if (null !== $pcreVersion) {
$this->addRecommendation(
$pcreVersion >= 8.0,
sprintf('PCRE extension should be at least version 8.0 (%s installed)', $pcreVersion),
'<strong>PCRE 8.0+</strong> is preconfigured in PHP since 5.3.2 but you are using an outdated version of it. Symfony probably works anyway but it is recommended to upgrade your PCRE extension.'
);
}
$this->addRecommendation(
class_exists('DomDocument'),
'PHP-DOM and PHP-XML modules should be installed',
'Install and enable the <strong>PHP-DOM</strong> and the <strong>PHP-XML</strong> modules.'
);
$this->addRecommendation(
function_exists('mb_strlen'),
'mb_strlen() should be available',
'Install and enable the <strong>mbstring</strong> extension.'
);
$this->addRecommendation(
function_exists('iconv'),
'iconv() should be available',
'Install and enable the <strong>iconv</strong> extension.'
);
$this->addRecommendation(
function_exists('utf8_decode'),
'utf8_decode() should be available',
'Install and enable the <strong>XML</strong> extension.'
);
$this->addRecommendation(
function_exists('filter_var'),
'filter_var() should be available',
'Install and enable the <strong>filter</strong> extension.'
);
if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
$this->addRecommendation(
function_exists('posix_isatty'),
'posix_isatty() should be available',
'Install and enable the <strong>php_posix</strong> extension (used to colorize the CLI output).'
);
}
$this->addRecommendation(
extension_loaded('intl'),
'intl extension should be available',
'Install and enable the <strong>intl</strong> extension (used for validators).'
);
if (extension_loaded('intl')) {
// in some WAMP server installations, new Collator() returns null
$this->addRecommendation(
null !== new Collator('fr_FR'),
'intl extension should be correctly configured',
'The intl extension does not behave properly. This problem is typical on PHP 5.3.X x64 WIN builds.'
);
// check for compatible ICU versions (only done when you have the intl extension)
if (defined('INTL_ICU_VERSION')) {
$version = INTL_ICU_VERSION;
} else {
$reflector = new ReflectionExtension('intl');
ob_start();
$reflector->info();
$output = strip_tags(ob_get_clean());
preg_match('/^ICU version +(?:=> )?(.*)$/m', $output, $matches);
$version = $matches[1];
}
$this->addRecommendation(
version_compare($version, '4.0', '>='),
'intl ICU version should be at least 4+',
'Upgrade your <strong>intl</strong> extension with a newer ICU version (4+).'
);
if (class_exists('Symfony\Component\Intl\Intl')) {
$this->addRecommendation(
\Symfony\Component\Intl\Intl::getIcuDataVersion() <= \Symfony\Component\Intl\Intl::getIcuVersion(),
sprintf('intl ICU version installed on your system is outdated (%s) and does not match the ICU data bundled with Symfony (%s)', \Symfony\Component\Intl\Intl::getIcuVersion(), \Symfony\Component\Intl\Intl::getIcuDataVersion()),
'To get the latest internationalization data upgrade the ICU system package and the intl PHP extension.'
);
if (\Symfony\Component\Intl\Intl::getIcuDataVersion() <= \Symfony\Component\Intl\Intl::getIcuVersion()) {
$this->addRecommendation(
\Symfony\Component\Intl\Intl::getIcuDataVersion() === \Symfony\Component\Intl\Intl::getIcuVersion(),
sprintf('intl ICU version installed on your system (%s) does not match the ICU data bundled with Symfony (%s)', \Symfony\Component\Intl\Intl::getIcuVersion(), \Symfony\Component\Intl\Intl::getIcuDataVersion()),
'To avoid internationalization data inconsistencies upgrade the symfony/intl component.'
);
}
}
$this->addPhpIniRecommendation(
'intl.error_level',
create_function('$cfgValue', 'return (int) $cfgValue === 0;'),
true,
'intl.error_level should be 0 in php.ini',
'Set "<strong>intl.error_level</strong>" to "<strong>0</strong>" in php.ini<a href="#phpini">*</a> to inhibit the messages when an error occurs in ICU functions.'
);
}
$accelerator =
(extension_loaded('eaccelerator') && ini_get('eaccelerator.enable'))
||
(extension_loaded('apc') && ini_get('apc.enabled'))
||
(extension_loaded('Zend Optimizer+') && ini_get('zend_optimizerplus.enable'))
||
(extension_loaded('Zend OPcache') && ini_get('opcache.enable'))
||
(extension_loaded('xcache') && ini_get('xcache.cacher'))
||
(extension_loaded('wincache') && ini_get('wincache.ocenabled'))
;
$this->addRecommendation(
$accelerator,
'a PHP accelerator should be installed',
'Install and/or enable a <strong>PHP accelerator</strong> (highly recommended).'
);
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
$this->addRecommendation(
$this->getRealpathCacheSize() >= 5 * 1024 * 1024,
'realpath_cache_size should be at least 5M in php.ini',
'Setting "<strong>realpath_cache_size</strong>" to e.g. "<strong>5242880</strong>" or "<strong>5M</strong>" in php.ini<a href="#phpini">*</a> may improve performance on Windows significantly in some cases.'
);
}
$this->addPhpIniRecommendation('short_open_tag', false);
$this->addPhpIniRecommendation('magic_quotes_gpc', false, true);
$this->addPhpIniRecommendation('register_globals', false, true);
$this->addPhpIniRecommendation('session.auto_start', false);
$this->addRecommendation(
class_exists('PDO'),
'PDO should be installed',
'Install <strong>PDO</strong> (mandatory for Doctrine).'
);
if (class_exists('PDO')) {
$drivers = PDO::getAvailableDrivers();
$this->addRecommendation(
count($drivers) > 0,
sprintf('PDO should have some drivers installed (currently available: %s)', count($drivers) ? implode(', ', $drivers) : 'none'),
'Install <strong>PDO drivers</strong> (mandatory for Doctrine).'
);
}
}
/**
* Loads realpath_cache_size from php.ini and converts it to int.
*
* (e.g. 16k is converted to 16384 int)
*
* @return int
*/
protected function getRealpathCacheSize()
{
$size = ini_get('realpath_cache_size');
$size = trim($size);
$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;
case 'm':
return $size * 1024 * 1024;
case 'k':
return $size * 1024;
default:
return (int) $size;
}
}
/**
* Defines PHP required version from Symfony version.
*
* @return string|false The PHP required version or false if it could not be guessed
*/
protected function getPhpRequiredVersion()
{
if (!file_exists($path = __DIR__.'/../composer.lock')) {
return false;
}
$composerLock = json_decode(file_get_contents($path), true);
foreach ($composerLock['packages'] as $package) {
$name = $package['name'];
if ('symfony/symfony' !== $name && 'symfony/http-kernel' !== $name) {
continue;
}
return (int) $package['version'][1] > 2 ? self::REQUIRED_PHP_VERSION : self::LEGACY_REQUIRED_PHP_VERSION;
}
return false;
}
}

View File

@@ -270,7 +270,7 @@ $hasMinorProblems = (bool) count($minorProblems);
}
.sf-reset ul a,
.sf-reset ul a:hover {
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAICAYAAAAx8TU7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAFdJREFUeNpiYACBjjOhDEiACSggCKTLgXQ5TJARqhIkcReIKxgqTGYxwvV0nDEGkmeAOIwJySiQ4HsgvseIpGo3ELsCtZ9lRDIvDCiwhwHJPEFkJwEEGACq6hdnax8y1AAAAABJRU5ErkJggg==) no-repeat right 7px;
background: url(../images/blue-arrow.png) no-repeat right 6px;
padding-right: 10px;
}
.sf-reset ul, ol {

View File

@@ -1,8 +1,8 @@
'use strict';
let webpack = require("webpack");
var webpack = require("webpack");
process.env.NODE_ENV = 'production';
let isProd = (process.env.NODE_ENV === 'production');
var isProd = (process.env.NODE_ENV === 'production');
/**
* Conditionally return a list of plugins to use based on the current environment.
@@ -10,7 +10,7 @@ let isProd = (process.env.NODE_ENV === 'production');
* @returns {Array}
*/
function getPlugins() {
let plugins = [];
var plugins = [];
/**
* Always expose NODE_ENV to webpack, you can now use `process.env.NODE_ENV`
@@ -33,14 +33,10 @@ function getPlugins() {
return plugins;
}
const config = {
module: {}
};
let mineSeekerConfig = Object.assign({}, config, {
const config = module.exports = {
entry: './web/bundles/mineseeker/js/mine-seeker.js',
output: {
path: './src/Mine/SeekerBundle/Resources/public/js/build/mine-seeker/',
path: './src/Mine/SeekerBundle/Resources/public/js',
filename: 'index.min.js'
},
module: {
@@ -50,34 +46,12 @@ let mineSeekerConfig = Object.assign({}, config, {
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['es2015', 'es2016', 'es2017', 'react']
presets: ['es2015', 'react']
}
}
]
}
});
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']
}
}
]
}
});
plugins: getPlugins()
};
module.exports = [
mineSeekerConfig,
mineUserListConfig,
];
module.exports = config;

View File

@@ -1,13 +1,9 @@
'use strict';
const config = {
module: {}
};
let mineSeekerConfig = Object.assign({}, config, {
const config = module.exports = {
entry: './web/bundles/mineseeker/js/mine-seeker.js',
output: {
path: './src/Mine/SeekerBundle/Resources/public/js/build/mine-seeker/',
path: './src/Mine/SeekerBundle/Resources/public/js',
filename: 'index.js'
},
module: {
@@ -17,34 +13,11 @@ let mineSeekerConfig = Object.assign({}, config, {
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['es2015', 'es2016', 'es2017', 'react']
presets: ['es2015', 'react']
}
}
]
}
});
};
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,
];
module.exports = config;