diff --git a/.gitignore b/.gitignore index 58eda4f..753b0fe 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/src/Mine/SeekerBundle/Controller/GameController.php b/src/Mine/SeekerBundle/Controller/GameController.php index a4bebbf..1f0e400 100644 --- a/src/Mine/SeekerBundle/Controller/GameController.php +++ b/src/Mine/SeekerBundle/Controller/GameController.php @@ -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', )); } diff --git a/src/Mine/SeekerBundle/Resources/config/pubsub/routing.yml b/src/Mine/SeekerBundle/Resources/config/pubsub/routing.yml index 0ffe177..b3bdede 100644 --- a/src/Mine/SeekerBundle/Resources/config/pubsub/routing.yml +++ b/src/Mine/SeekerBundle/Resources/config/pubsub/routing.yml @@ -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} diff --git a/src/Mine/SeekerBundle/Resources/config/routing.yml b/src/Mine/SeekerBundle/Resources/config/routing.yml index 976ce57..22e79fc 100644 --- a/src/Mine/SeekerBundle/Resources/config/routing.yml +++ b/src/Mine/SeekerBundle/Resources/config/routing.yml @@ -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 } diff --git a/src/Mine/SeekerBundle/Resources/config/services.yml b/src/Mine/SeekerBundle/Resources/config/services.yml index c3806ef..5d09a2b 100644 --- a/src/Mine/SeekerBundle/Resources/config/services.yml +++ b/src/Mine/SeekerBundle/Resources/config/services.yml @@ -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 } diff --git a/src/Mine/SeekerBundle/Resources/public/css/style.homepage.css b/src/Mine/SeekerBundle/Resources/public/css/style.homepage.css index 866e107..7d3003a 100644 --- a/src/Mine/SeekerBundle/Resources/public/css/style.homepage.css +++ b/src/Mine/SeekerBundle/Resources/public/css/style.homepage.css @@ -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; } diff --git a/src/Mine/SeekerBundle/Resources/public/js/mine-seeker.js b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker.js index 3d47443..d4bb695 100644 --- a/src/Mine/SeekerBundle/Resources/public/js/mine-seeker.js +++ b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker.js @@ -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( - , - document.getElementById('mine-wrapper') + , + mineWrapper ); diff --git a/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/app.js b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/app.js index fefeb88..25ebae2 100644 --- a/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/app.js +++ b/src/Mine/SeekerBundle/Resources/public/js/mine-seeker/app.js @@ -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:
+ + +
+ }); + } 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(); } diff --git a/src/Mine/SeekerBundle/Resources/public/js/mine-system/mine-services.js b/src/Mine/SeekerBundle/Resources/public/js/mine-system/mine-services.js new file mode 100644 index 0000000..0b40028 --- /dev/null +++ b/src/Mine/SeekerBundle/Resources/public/js/mine-system/mine-services.js @@ -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; \ No newline at end of file diff --git a/src/Mine/SeekerBundle/Resources/public/js/mine-user-list.js b/src/Mine/SeekerBundle/Resources/public/js/mine-user-list.js new file mode 100644 index 0000000..a843c84 --- /dev/null +++ b/src/Mine/SeekerBundle/Resources/public/js/mine-user-list.js @@ -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 +); diff --git a/src/Mine/SeekerBundle/Resources/public/js/mine-user-list/app.js b/src/Mine/SeekerBundle/Resources/public/js/mine-user-list/app.js new file mode 100644 index 0000000..2400b2b --- /dev/null +++ b/src/Mine/SeekerBundle/Resources/public/js/mine-user-list/app.js @@ -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 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(
+ {this.getProfilePicture(item.id)} +

{item.name}

+

{item.online ? 'online' : 'offline'}

+ +
) + }) + : ''; + + this.state.requests.size > 0 + ? this.state.requests.forEach((item) => { + req.push(
+

{item.username} challenged you!

+ +
) + }) + : ''; + + return ( +
+
+ {req} +
+
+ {users} +
+
+ ); + } +} + +export default MineUserList; diff --git a/src/Mine/SeekerBundle/Resources/views/Game/index.html.twig b/src/Mine/SeekerBundle/Resources/views/Game/index.html.twig index 942c843..c8747c7 100644 --- a/src/Mine/SeekerBundle/Resources/views/Game/index.html.twig +++ b/src/Mine/SeekerBundle/Resources/views/Game/index.html.twig @@ -63,6 +63,9 @@ {% block body %}
+ {% if is_granted("IS_AUTHENTICATED_REMEMBERED") %} +
+ {% endif %}

Used technologies

Used Websocket - + {% 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' %} + + {% 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' %} + + {% endjavascripts %} + + + + {% endif %} + {% endif %} {% endblock %} diff --git a/src/Mine/SeekerBundle/Resources/views/Game/play.html.twig b/src/Mine/SeekerBundle/Resources/views/Game/play.html.twig index 79e2ec6..102de23 100644 --- a/src/Mine/SeekerBundle/Resources/views/Game/play.html.twig +++ b/src/Mine/SeekerBundle/Resources/views/Game/play.html.twig @@ -20,7 +20,8 @@ - + {% 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' %} {% 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' %} {% endjavascripts %} {% endif %} diff --git a/src/Mine/SeekerBundle/Resources/views/Recent/facebook.html.twig b/src/Mine/SeekerBundle/Resources/views/Recent/facebook.html.twig index 29885f2..863525f 100644 --- a/src/Mine/SeekerBundle/Resources/views/Recent/facebook.html.twig +++ b/src/Mine/SeekerBundle/Resources/views/Recent/facebook.html.twig @@ -1,14 +1,22 @@