add new websocket topic - userList - and handle it
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -35,6 +35,5 @@ phpunit-report/*
|
||||
/src/Mine/SeekerBundle/Resources/public/js/src/
|
||||
|
||||
nohup.out
|
||||
src/Mine/SeekerBundle/Resources/public/js/index.js
|
||||
src/Mine/SeekerBundle/Resources/public/js/index.min.js
|
||||
src/Mine/SeekerBundle/Resources/public/js/build/
|
||||
npm-debug.log
|
||||
|
||||
@@ -33,14 +33,24 @@ class GameController extends Controller
|
||||
// dump($response->getErrorExplanation());
|
||||
// }
|
||||
|
||||
return $this->render('MineSeekerBundle:Game:index.html.twig');
|
||||
return $this->render('MineSeekerBundle:Game:index.html.twig', array(
|
||||
'env' => $this->container->getParameter('kernel.environment'),
|
||||
));
|
||||
}
|
||||
|
||||
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',
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Topic Configuration
|
||||
# MineSeeker Topic Configuration
|
||||
mineseeker_topic:
|
||||
channel: mineseeker/channel/{game}
|
||||
handler:
|
||||
@@ -7,6 +7,12 @@ mineseeker_topic:
|
||||
# method:
|
||||
# path: '[a-z1-9A-Z]+'
|
||||
|
||||
# UserList Topic Configuration
|
||||
userList_topic:
|
||||
channel: mineseeker/userList
|
||||
handler:
|
||||
callback: 'userlist.topic'
|
||||
|
||||
# Remote Procedure Call Configuration
|
||||
mineseeker_rpc:
|
||||
channel: mineseeker-rpc/{method}
|
||||
|
||||
@@ -13,6 +13,11 @@ MineSeekerBundle_gamePlayWId:
|
||||
defaults: { _controller: MineSeekerBundle:Game:play }
|
||||
schemes: [https]
|
||||
|
||||
MineSeekerBundle_gameReplay:
|
||||
path: /re-play/{gameAssoc}
|
||||
defaults: { _controller: MineSeekerBundle:Game:rePlay }
|
||||
schemes: [https]
|
||||
|
||||
MineSeekerBundle_slack:
|
||||
path: /slack
|
||||
defaults: { _controller: MineSeekerBundle:Game:slack }
|
||||
|
||||
@@ -26,7 +26,7 @@ services:
|
||||
arguments:
|
||||
ping: '@gos_web_socket.pdo.periodic_ping'
|
||||
|
||||
mineseeker.topic_sample_service:
|
||||
mineseeker.game_service:
|
||||
class: Mine\SeekerBundle\Topic\MineseekerTopic
|
||||
tags:
|
||||
- { name: gos_web_socket.topic }
|
||||
@@ -35,7 +35,16 @@ services:
|
||||
doctrine: '@doctrine.orm.entity_manager'
|
||||
requestStack: '@request_stack'
|
||||
|
||||
mineseeker.rpc_sample_service:
|
||||
mineseeker.user_list_service:
|
||||
class: Mine\SeekerBundle\Topic\UserListTopic
|
||||
tags:
|
||||
- { name: gos_web_socket.topic }
|
||||
arguments:
|
||||
clientManipulator: "@gos_web_socket.websocket.client_manipulator"
|
||||
doctrine: '@doctrine.orm.entity_manager'
|
||||
requestStack: '@request_stack'
|
||||
|
||||
mineseeker.game_rpc_service:
|
||||
class: Mine\SeekerBundle\Rpc\MineseekerRpc
|
||||
tags:
|
||||
- { name: gos_web_socket.rpc }
|
||||
|
||||
@@ -141,6 +141,59 @@ header section div.buttons > a.small:hover {
|
||||
transition: all 250ms ease-in-out;
|
||||
}
|
||||
|
||||
main .user-list-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
main .user-list-container .user-friend {
|
||||
width: 15%;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
main .user-list-container .user-friend h1 {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
main .user-list-container .user-friend 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-box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3);
|
||||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3);
|
||||
-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:hover {
|
||||
background: #86b5e1;
|
||||
border: 1px solid #658fb8;
|
||||
color: #FFFFFF;
|
||||
|
||||
-webkit-box-shadow: 0 7px 15px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 7px 15px rgba(0, 0, 0, 0.2);
|
||||
-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: 1100px) {
|
||||
header section {
|
||||
align-items: center;
|
||||
@@ -166,11 +219,6 @@ header section div.buttons > a.small:hover {
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
/*header {*/
|
||||
/*min-height: 100%;*/
|
||||
/*height: auto;*/
|
||||
/*}*/
|
||||
|
||||
header section {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@ import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import MineSeeker from './mine-seeker/app';
|
||||
|
||||
let mineWrapper = document.getElementById('mine-wrapper');
|
||||
|
||||
ReactDOM.render(
|
||||
<MineSeeker env={document.getElementById('mine-wrapper').dataset.env}
|
||||
gameId={document.getElementById('mine-wrapper').dataset.gameId}
|
||||
ssl={document.getElementById('mine-wrapper').dataset.ssl}/>,
|
||||
document.getElementById('mine-wrapper')
|
||||
<MineSeeker env={mineWrapper.dataset.env}
|
||||
gameId={mineWrapper.dataset.gameId}
|
||||
ssl={mineWrapper.dataset.ssl}/>,
|
||||
mineWrapper
|
||||
);
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import React from 'react';
|
||||
import Grid from './grid/grid';
|
||||
import GridControl from './grid/grid-control';
|
||||
import MineServices from '../mine-system/mine-services';
|
||||
|
||||
class MineSeeker extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
let gameAssoc = props.gameId !== '' ? props.gameId : this.makeGameAssoc(50);
|
||||
let services = new MineServices();
|
||||
let gameAssoc = props.gameId !== '' ? props.gameId : services.randomString(50);
|
||||
let channel = "mineseeker/channel/" + gameAssoc;
|
||||
|
||||
this.state = {
|
||||
@@ -19,19 +21,11 @@ class MineSeeker extends React.Component {
|
||||
createGrid: false,
|
||||
stepCache: [],
|
||||
connectionLost: false,
|
||||
end: false
|
||||
end: false,
|
||||
replay: false
|
||||
}
|
||||
}
|
||||
|
||||
makeGameAssoc(len) {
|
||||
let text = "";
|
||||
let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
for (let i = 0; i < len; i++) {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
currectGridSize() {
|
||||
let $field = $('#mine-wrapper .grid');
|
||||
$field.height($field.width());
|
||||
@@ -76,8 +70,19 @@ class MineSeeker extends React.Component {
|
||||
* @param payload
|
||||
*/
|
||||
makeGameStart(payload) {
|
||||
/** every time the blue starts */
|
||||
this.refs.gridControl.refs.userControl.setState({activePlayer: 1});
|
||||
let steps = JSON.parse(Base64.decode(payload.steps));
|
||||
|
||||
if (steps.length) {
|
||||
steps.forEach((item) => {
|
||||
setTimeout(() => {
|
||||
this.refs.gridControl.refs.userControl.setState({bombSelected: item.wBomb});
|
||||
this.makePointsCalcAndStep([item.row, item.col]);
|
||||
}, 500);
|
||||
});
|
||||
} else {
|
||||
/** every time the blue starts when it is not a continued game */
|
||||
this.refs.gridControl.refs.userControl.setState({activePlayer: 1});
|
||||
}
|
||||
|
||||
/** Set up player names w/ server data */
|
||||
this.refs.gridControl.refs.userControl.refs.red.setState({
|
||||
@@ -234,6 +239,20 @@ class MineSeeker extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
clickRestorePlayer(data) {
|
||||
this.refs.gridControl.setState({
|
||||
overlay: true,
|
||||
overlayTitle: "We are waiting for your opponent...",
|
||||
overlaySubTitle: ''
|
||||
});
|
||||
|
||||
this.refs.gridControl.state.webPlayer = data[0];
|
||||
|
||||
if (data[1].userCnt === 2) {
|
||||
this.makeGameStart(data[1]);
|
||||
}
|
||||
}
|
||||
|
||||
wSubscribe(payload, rpcUsers = null) {
|
||||
this.state.env === 'dev' && console.info(
|
||||
(typeof payload.user !== 'undefined' ? payload.user : 'user') + " has been subscribed to the channel!"
|
||||
@@ -241,28 +260,44 @@ class MineSeeker extends React.Component {
|
||||
|
||||
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'
|
||||
});
|
||||
/** is it a REPLAY */
|
||||
if (this.state.replay) {
|
||||
this.refs.gridControl.setState({
|
||||
overlay: true,
|
||||
overlayTitle: "Which player has been you, in this game?",
|
||||
overlaySubTitle: <div>
|
||||
<button onClick={this.clickRestorePlayer.bind(this, ['blue', payload])}>
|
||||
I was the BLUE, man!
|
||||
</button>
|
||||
<button onClick={this.clickRestorePlayer.bind(this, ['red', payload])} target="_blank">
|
||||
I was RED, obviously!
|
||||
</button>
|
||||
</div>
|
||||
});
|
||||
} else {
|
||||
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'
|
||||
});
|
||||
|
||||
/** 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);
|
||||
}
|
||||
}
|
||||
|
||||
/** 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) {
|
||||
@@ -400,8 +435,21 @@ class MineSeeker extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregistered
|
||||
* http://mine.dev/re-play/I1Bx9UHZP5CWDnTZHpJqGTlkzehblfsbfz4A4xYaH9HFhBK2aN
|
||||
*
|
||||
* Registered
|
||||
* http://mine.dev/re-play/km10oOgM7Xh37vJ8PFjaRRePrHpDkZFDJgxLhNc6hkTYyLyPKD
|
||||
*/
|
||||
|
||||
/** After rendering */
|
||||
componentDidMount() {
|
||||
/** is it a REPLAY */
|
||||
window.location.pathname.indexOf('re-play') > 0
|
||||
? this.setState({replay: true})
|
||||
: this.setState({replay: false});
|
||||
|
||||
this.connectWithWebsocket();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
export default MineServices;
|
||||
10
src/Mine/SeekerBundle/Resources/public/js/mine-user-list.js
Normal file
10
src/Mine/SeekerBundle/Resources/public/js/mine-user-list.js
Normal file
@@ -0,0 +1,10 @@
|
||||
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}/>,
|
||||
mineUserList
|
||||
);
|
||||
142
src/Mine/SeekerBundle/Resources/public/js/mine-user-list/app.js
Normal file
142
src/Mine/SeekerBundle/Resources/public/js/mine-user-list/app.js
Normal file
@@ -0,0 +1,142 @@
|
||||
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,
|
||||
services: services,
|
||||
session: null,
|
||||
users: [],
|
||||
requests: new Map(),
|
||||
};
|
||||
}
|
||||
|
||||
refreshUserList(payload) {
|
||||
let webUsers = JSON.parse(Base64.decode(payload.users)),
|
||||
users = new Map();
|
||||
|
||||
webUsers.forEach((item) => {
|
||||
if (!users.has(item.email)) {
|
||||
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;
|
||||
|
||||
if (!req.has(payload.username)) {
|
||||
req.set(payload.username, payload);
|
||||
}
|
||||
|
||||
this.setState({reqests: req});
|
||||
break;
|
||||
case 'RESP':
|
||||
window.location.href = window.location.origin + '/re-play/' + payload.gameAssoc;
|
||||
break;
|
||||
case 'GAME':
|
||||
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) {
|
||||
return <img src={"http://graph.facebook.com/" + id + "/picture?type=square&width=100&height=100"}
|
||||
alt="Facebook profile"/>;
|
||||
}
|
||||
|
||||
clickChallengeFriend(username) {
|
||||
this.state.session.publish('mineseeker/userList', {
|
||||
'type': 'REQ',
|
||||
'username': username,
|
||||
'gameAssoc': this.state.services.randomString(50)
|
||||
});
|
||||
}
|
||||
|
||||
clickChallengeAccepted(data) {
|
||||
this.state.session.publish('mineseeker/userList', {
|
||||
'type': 'RESP',
|
||||
'username': data[0],
|
||||
'gameAssoc': data[1]
|
||||
});
|
||||
|
||||
window.location.href = window.location.origin + '/re-play/' + data[1];
|
||||
}
|
||||
|
||||
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">
|
||||
{this.getProfilePicture(item.id)}
|
||||
<h1>{item.name}</h1>
|
||||
<h2>{item.online ? 'online' : 'offline'}</h2>
|
||||
<button onClick={this.clickChallengeFriend.bind(this, item.username)}>Challenge</button>
|
||||
</div>)
|
||||
})
|
||||
: '';
|
||||
|
||||
this.state.requests.size > 0
|
||||
? this.state.requests.forEach((item) => {
|
||||
req.push(<div key={this.state.services.randomString(50)} className="user-friend">
|
||||
<h1>{item.username} <strong>challenged you</strong>!</h1>
|
||||
<button onClick={this.clickChallengeAccepted.bind(this, [item.username, item.gameAssoc])}>Accept</button>
|
||||
</div>)
|
||||
})
|
||||
: '';
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="user-request-container">
|
||||
{req}
|
||||
</div>
|
||||
<div className="user-list-container">
|
||||
{users}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MineUserList;
|
||||
@@ -63,6 +63,9 @@
|
||||
|
||||
{% block body %}
|
||||
<div class="txt">
|
||||
{% if is_granted("IS_AUTHENTICATED_REMEMBERED") %}
|
||||
<div id="mine-user-list" data-env="{{ env }}"></div>
|
||||
{% endif %}
|
||||
<div class="technologies">
|
||||
<h1>Used technologies</h1>
|
||||
<img src="{{ asset('bundles/mineseeker/images/technologies/websocket.png') }}" alt="Used Websocket"
|
||||
@@ -107,11 +110,31 @@
|
||||
{% block javascripts %}
|
||||
{{ parent() }}
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/JQuery-Snowfall/1.7.4/snowfall.jquery.min.js"
|
||||
type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$(document).snowfall({deviceorientation: true, round: true, minSize: 5, maxSize: 8});
|
||||
});
|
||||
</script>
|
||||
{% if is_granted("IS_AUTHENTICATED_REMEMBERED") and app.user.facebookAccessToken is defined %}
|
||||
{% if env == 'dev' %}
|
||||
{% javascripts filter='?uglifyjs2'
|
||||
'@GosWebSocketBundle/Resources/public/js/vendor/autobahn.min.js'
|
||||
'@GosWebSocketBundle/Resources/public/js/gos_web_socket_client.js'
|
||||
'@MineSeekerBundle/Resources/public/js/node/js-base64/base64.min.js'
|
||||
'@MineSeekerBundle/Resources/public/js/build/mine-user-list/index.js' %}
|
||||
<script type="text/javascript" src="{{ asset_url }}"></script>
|
||||
{% endjavascripts %}
|
||||
{% else %}
|
||||
{% javascripts filter='?uglifyjs2'
|
||||
'@GosWebSocketBundle/Resources/public/js/vendor/autobahn.min.js'
|
||||
'@GosWebSocketBundle/Resources/public/js/gos_web_socket_client.js'
|
||||
'@MineSeekerBundle/Resources/public/js/node/js-base64/base64.min.js'
|
||||
'@MineSeekerBundle/Resources/public/js/build/mine-user-list/index.min.js' %}
|
||||
<script type="text/javascript" src="{{ asset_url }}"></script>
|
||||
{% endjavascripts %}
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/JQuery-Snowfall/1.7.4/snowfall.jquery.min.js"
|
||||
type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$(document).snowfall({deviceorientation: true, round: true, minSize: 5, maxSize: 8});
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
<meta property="og:type" content="website"/>
|
||||
<meta property="og:title" content="Your friend challenges YOU!"/>
|
||||
<meta property="og:description" content="Do you accept the challenge?"/>
|
||||
<meta property="og:image" content="{{ app.request.getSchemeAndHttpHost() }}{{ asset('bundles/mineseeker/images/mine-1600x627.png') }}"/>
|
||||
<meta property="og:image"
|
||||
content="{{ app.request.getSchemeAndHttpHost() }}{{ asset('bundles/mineseeker/images/mine-1600x627.png') }}"/>
|
||||
{% endblock %}
|
||||
|
||||
{% block stylesheets %}
|
||||
@@ -46,7 +47,7 @@
|
||||
'@GosWebSocketBundle/Resources/public/js/gos_web_socket_client.js'
|
||||
'@MineSeekerBundle/Resources/public/js/node/howler/dist/howler.min.js'
|
||||
'@MineSeekerBundle/Resources/public/js/node/js-base64/base64.min.js'
|
||||
'@MineSeekerBundle/Resources/public/js/index.js' %}
|
||||
'@MineSeekerBundle/Resources/public/js/build/mine-seeker/index.js' %}
|
||||
<script type="text/javascript" src="{{ asset_url }}"></script>
|
||||
{% endjavascripts %}
|
||||
{% else %}
|
||||
@@ -55,7 +56,7 @@
|
||||
'@GosWebSocketBundle/Resources/public/js/gos_web_socket_client.js'
|
||||
'@MineSeekerBundle/Resources/public/js/node/howler/dist/howler.min.js'
|
||||
'@MineSeekerBundle/Resources/public/js/node/js-base64/base64.min.js'
|
||||
'@MineSeekerBundle/Resources/public/js/index.min.js' %}
|
||||
'@MineSeekerBundle/Resources/public/js/build/mine-seeker/index.min.js' %}
|
||||
<script type="text/javascript" src="{{ asset_url }}"></script>
|
||||
{% endjavascripts %}
|
||||
{% endif %}
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
<script type="text/javascript">
|
||||
window.fbAsyncInit = function () {
|
||||
FB.init({
|
||||
appId: '{{ facebook_api }}',
|
||||
xfbml: true,
|
||||
cookie: true,
|
||||
status: true,
|
||||
oauth: true,
|
||||
version: '{{ facebook_api_version }}'
|
||||
});
|
||||
};
|
||||
$(function () {
|
||||
window.fbAsyncInit = function () {
|
||||
FB.init({
|
||||
appId: '{{ facebook_api }}',
|
||||
xfbml: true,
|
||||
cookie: true,
|
||||
status: true,
|
||||
version: '{{ facebook_api_version }}',
|
||||
|
||||
});
|
||||
|
||||
/** trigger jQuery when Facebook SKD loads */
|
||||
$(document).trigger('fb-load');
|
||||
// $(document).bind('fb-load', function () {
|
||||
// TODO
|
||||
// });
|
||||
};
|
||||
});
|
||||
|
||||
(function (d, s, id) {
|
||||
var js, fjs = d.getElementsByTagName(s)[0];
|
||||
|
||||
@@ -63,7 +63,8 @@ class MineseekerTopic implements TopicInterface
|
||||
'channel' => $topic->getId(),
|
||||
'user' => $userName,
|
||||
'userCnt' => $topic->count(),
|
||||
'users' => $users
|
||||
'users' => $users,
|
||||
'steps' => $this->getSteps($topic),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -180,6 +181,28 @@ 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
|
||||
*
|
||||
@@ -300,7 +323,7 @@ class MineseekerTopic implements TopicInterface
|
||||
$connection->close();
|
||||
$connection->connect();
|
||||
}
|
||||
} catch(PDOException $ex) {
|
||||
} catch (PDOException $ex) {
|
||||
throw PDOException::class;
|
||||
}
|
||||
}
|
||||
|
||||
192
src/Mine/SeekerBundle/Topic/UserListTopic.php
Normal file
192
src/Mine/SeekerBundle/Topic/UserListTopic.php
Normal file
@@ -0,0 +1,192 @@
|
||||
<?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;
|
||||
|
||||
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();
|
||||
$reqUser = $this->clientManipulator->findByUsername($topic, $event['username']);
|
||||
|
||||
/** user is still online */
|
||||
if (false !== $reqUser) {
|
||||
$topic->broadcast(
|
||||
array(
|
||||
'type' => $event['type'],
|
||||
'username' => $userName,
|
||||
'gameAssoc' => $event['gameAssoc'],
|
||||
),
|
||||
array(),
|
||||
array(
|
||||
$reqUser['connection']->WAMP->sessionId
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like RPC is will use to prefix the channel
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'userlist.topic';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 PDOException::class;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
var webpack = require("webpack");
|
||||
let webpack = require("webpack");
|
||||
process.env.NODE_ENV = 'production';
|
||||
var isProd = (process.env.NODE_ENV === 'production');
|
||||
let isProd = (process.env.NODE_ENV === 'production');
|
||||
|
||||
/**
|
||||
* Conditionally return a list of plugins to use based on the current environment.
|
||||
@@ -10,7 +10,7 @@ var isProd = (process.env.NODE_ENV === 'production');
|
||||
* @returns {Array}
|
||||
*/
|
||||
function getPlugins() {
|
||||
var plugins = [];
|
||||
let plugins = [];
|
||||
|
||||
/**
|
||||
* Always expose NODE_ENV to webpack, you can now use `process.env.NODE_ENV`
|
||||
@@ -33,10 +33,14 @@ function getPlugins() {
|
||||
return plugins;
|
||||
}
|
||||
|
||||
const config = module.exports = {
|
||||
const config = {
|
||||
module: {}
|
||||
};
|
||||
|
||||
let mineSeekerConfig = Object.assign({}, config, {
|
||||
entry: './web/bundles/mineseeker/js/mine-seeker.js',
|
||||
output: {
|
||||
path: './src/Mine/SeekerBundle/Resources/public/js',
|
||||
path: './src/Mine/SeekerBundle/Resources/public/js/build/mine-seeker/',
|
||||
filename: 'index.min.js'
|
||||
},
|
||||
module: {
|
||||
@@ -50,8 +54,30 @@ const config = module.exports = {
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: getPlugins()
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = config;
|
||||
let mineUserListConfig = Object.assign({}, config, {
|
||||
entry: './web/bundles/mineseeker/js/mine-user-list.js',
|
||||
output: {
|
||||
path: './src/Mine/SeekerBundle/Resources/public/js/build/mine-user-list/',
|
||||
filename: 'index.min.js'
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel',
|
||||
query: {
|
||||
presets: ['es2015', 'react']
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
mineSeekerConfig,
|
||||
mineUserListConfig,
|
||||
];
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const config = module.exports = {
|
||||
const config = {
|
||||
module: {}
|
||||
};
|
||||
|
||||
let mineSeekerConfig = Object.assign({}, config, {
|
||||
entry: './web/bundles/mineseeker/js/mine-seeker.js',
|
||||
output: {
|
||||
path: './src/Mine/SeekerBundle/Resources/public/js',
|
||||
path: './src/Mine/SeekerBundle/Resources/public/js/build/mine-seeker/',
|
||||
filename: 'index.js'
|
||||
},
|
||||
module: {
|
||||
@@ -18,6 +22,29 @@ const config = module.exports = {
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
module.exports = config;
|
||||
let mineUserListConfig = Object.assign({}, config, {
|
||||
entry: './web/bundles/mineseeker/js/mine-user-list.js',
|
||||
output: {
|
||||
path: './src/Mine/SeekerBundle/Resources/public/js/build/mine-user-list/',
|
||||
filename: 'index.js'
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel',
|
||||
query: {
|
||||
presets: ['es2015', 'react']
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = [
|
||||
mineSeekerConfig,
|
||||
mineUserListConfig,
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user