diff --git a/common/utilities.js b/common/utilities.js index 788431a..b854cf4 100644 --- a/common/utilities.js +++ b/common/utilities.js @@ -15,7 +15,9 @@ let excluded = [ //messages that should not be logged 'Profile sent', 'Ladder sent', 'attacking', - 'idle' + 'idle', + + 'Combat log sent' ]; const log = (msg, ...args) => { diff --git a/server/combat.js b/server/combat.js index 8d51099..97541fb 100644 --- a/server/combat.js +++ b/server/combat.js @@ -85,6 +85,16 @@ const attackStatusRequest = (connection) => (req, res) => { }); } +const combatLogRequest = (connection) => (req, res) => { + let query = 'SELECT pastCombat.*, atk.username AS attackerUsername, def.username AS defenderUsername FROM pastCombat JOIN accounts AS atk ON pastCombat.attackerId = atk.id JOIN accounts AS def ON pastCombat.defenderId = def.id WHERE def.username = ? ORDER BY eventTime DESC LIMIT ?, ?;'; + connection.query(query, [req.body.username, req.body.start, req.body.length], (err, results) => { + if (err) throw err; + + res.status(200).json(results); + log('Combat log sent', req.body.username, req.body.start, req.body.length, JSON.stringify(results)); + }); +} + const runCombatTick = (connection) => { let combatTick = new CronJob('* * * * * *', () => { //find each pending combat @@ -178,6 +188,7 @@ const isAttacking = (connection, user, cb) => { module.exports = { attackRequest: attackRequest, attackStatusRequest: attackStatusRequest, + combatLogRequest: combatLogRequest, runCombatTick: runCombatTick } diff --git a/server/index.js b/server/index.js index fc8b5b3..c4e2b86 100644 --- a/server/index.js +++ b/server/index.js @@ -43,6 +43,7 @@ profiles.runGoldTick(connection); let combat = require('./combat.js'); app.post('/attackrequest', combat.attackRequest(connection)); app.post('/attackstatusrequest', combat.attackStatusRequest(connection)); +app.post('/combatlogrequest', combat.combatLogRequest(connection)); combat.runCombatTick(connection); //static directories diff --git a/src/components/pages/home.jsx b/src/components/pages/home.jsx index 75c9742..ed6e67e 100644 --- a/src/components/pages/home.jsx +++ b/src/components/pages/home.jsx @@ -26,6 +26,7 @@ class Home extends React.Component {
+

About

News

diff --git a/src/components/pages/ladder.jsx b/src/components/pages/ladder.jsx index 77c865b..d59cfb3 100644 --- a/src/components/pages/ladder.jsx +++ b/src/components/pages/ladder.jsx @@ -28,6 +28,7 @@ class Ladder extends React.Component {
+

Game Ladder

diff --git a/src/components/pages/profile.jsx b/src/components/pages/profile.jsx index ed1c3b9..1aa56a1 100644 --- a/src/components/pages/profile.jsx +++ b/src/components/pages/profile.jsx @@ -7,6 +7,7 @@ import queryString from 'query-string'; //panels import CommonLinks from '../panels/common_links.jsx'; import AttackButton from '../panels/attack_button.jsx'; +import CombatLog from '../panels/combat_log.jsx'; class Profile extends React.Component { constructor(props) { @@ -19,6 +20,7 @@ class Profile extends React.Component { soldiers: 0, spies: 0, scientists: 0, + warning: '' }; @@ -145,46 +147,53 @@ class Profile extends React.Component { MyProfileMainPanel() { return ( -
-
-

Username:

-

{this.state.username}

-
-
+
+

Profile

+
+
+

Username:

+

{this.state.username}

+
+
+
+ +
+

Gold:

+

{this.state.gold}

+
(+1 gold for each recruit every half hour)
+
+ +
+

Recruits:

+

{this.state.recruits}

+ +
+ +
+

Soldiers:

+

{this.state.soldiers}

+ + +
+ +
+

Spies:

+

{this.state.spies}

+ + +
+ +
+

Scientists:

+

{this.state.scientists}

+ + +
-
-

Gold:

-

{this.state.gold}

-
(+1 gold for each recruit every half hour)
-
- -
-

Recruits:

-

{this.state.recruits}

- -
- -
-

Soldiers:

-

{this.state.soldiers}

- - -
- -
-

Spies:

-

{this.state.spies}

- - -
- -
-

Scientists:

-

{this.state.scientists}

- - -
+
+

Combat Log

+
); } @@ -200,46 +209,49 @@ class Profile extends React.Component { NotMyProfileMainPanel() { return ( -
-
-

Username:

-

{this.state.username}

-
-
-
+
+

Profile

+
+
+

Username:

+

{this.state.username}

+
+
+
-
-

Gold:

-

{this.state.gold}

-
-
-
+
+

Gold:

+

{this.state.gold}

+
+
+
-
-

Recruits:

-

{this.state.recruits}

- -
+
+

Recruits:

+

{this.state.recruits}

+ +
-
-

Soldiers:

-

{this.state.soldiers}

-
-
-
+
+

Soldiers:

+

{this.state.soldiers}

+
+
+
-
-

Spies:

-

{this.state.spies}

-
-
-
+
+

Spies:

+

{this.state.spies}

+
+
+
-
-

Scientists:

-

{this.state.scientists}

-
-
+
+

Scientists:

+

{this.state.scientists}

+
+
+
); @@ -255,47 +267,50 @@ class Profile extends React.Component { LoggedOutMainPanel() { return ( -
-
-

Username:

-

{this.state.username}

-
-
-
+
+

Profile

+
+
+

Username:

+

{this.state.username}

+
+
+
-
-

Gold:

-

{this.state.gold}

-
-
-
+
+

Gold:

+

{this.state.gold}

+
+
+
-
-

Recruits:

-

{this.state.recruits}

-
-
-
+
+

Recruits:

+

{this.state.recruits}

+
+
+
-
-

Soldiers:

-

{this.state.soldiers}

-
-
-
+
+

Soldiers:

+

{this.state.soldiers}

+
+
+
-
-

Spies:

-

{this.state.spies}

-
-
-
+
+

Spies:

+

{this.state.spies}

+
+
+
-
-

Scientists:

-

{this.state.scientists}

-
-
+
+

Scientists:

+

{this.state.scientists}

+
+
+
); diff --git a/src/components/panels/combat_log.jsx b/src/components/panels/combat_log.jsx new file mode 100644 index 0000000..4295c36 --- /dev/null +++ b/src/components/panels/combat_log.jsx @@ -0,0 +1,74 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import PagedCombatLog from './paged_combat_log.jsx'; + +class CombatLog extends React.Component { + constructor(props) { + super(props); + this.state = { + start: props.start || 0, + length: props.length || 20, + fetch: null + }; + } + + componentDidUpdate() { + this.state.fetch(); + } + + render() { + let ButtonHeader = this.buttonHeader.bind(this); + + return ( +
+ + + +
+ + ); + } + + buttonHeader() { + return ( +
+
+ +
+
+ +
+
+ ); + } + + increment() { + this.setState({ + start: this.state.start + this.state.length + }); + } + + decrement() { + this.setState({ + start: Math.max(0, this.state.start - this.state.length) + }); + } + + //bound callbacks + getFetch(fn) { + this.setState({ fetch: fn }); + } + + onReceived(data) { + if (data.length === 0) { + this.decrement(); + } + } +} + +CombatLog.propTypes = { + username: PropTypes.string.isRequired +}; + +export default CombatLog; \ No newline at end of file diff --git a/src/components/panels/news_panel.jsx b/src/components/panels/news_panel.jsx index ba3e531..1b4cc6e 100644 --- a/src/components/panels/news_panel.jsx +++ b/src/components/panels/news_panel.jsx @@ -15,7 +15,10 @@ class NewsPanel extends React.Component { render() { return (
- {Object.keys(this.state.data).map((key) =>

)} + {Object.keys(this.state.data).map((key) =>
+ +
+
)}
); } diff --git a/src/components/panels/paged_combat_log.jsx b/src/components/panels/paged_combat_log.jsx new file mode 100644 index 0000000..4734793 --- /dev/null +++ b/src/components/panels/paged_combat_log.jsx @@ -0,0 +1,88 @@ +import React from 'react'; +import { withRouter, Link } from 'react-router-dom'; +import PropTypes from 'prop-types'; + +class PagedCombatLog extends React.Component { + constructor(props) { + super(props); + this.state = { + data: {} + } + + if (props.getFetch) { + props.getFetch(this.fetchCombatLog.bind(this)); + } + + this.fetchCombatLog(); + } + + render() { + return ( +
+
+

When

+

Attacker

+

Attacking Force

+

Defending Force

+

Undefended?

+

Victor

+

Gold Lost

+

Victor Casualties

+
+ {Object.keys(this.state.data).map((key) =>
+

{ this.parseDate(this.state.data[key].eventTime) }

+

{this.state.data[key].attackerUsername}

+

{this.state.data[key].attackingUnits}

+

{this.state.data[key].defendingUnits}

+

{this.state.data[key].undefended ? 'yes' : 'no'}

+

{this.state.data[key].victor}

+

{this.state.data[key].spoilsGold}

+

{this.state.data[key].casualtiesVictor}

+
)} +
+ ); + } + + fetchCombatLog(username = this.props.username, start = this.props.start, length = this.props.length) { + //build the XHR + let xhr = new XMLHttpRequest(); + + xhr.onreadystatechange = () => { + if (xhr.readyState === 4) { + if (xhr.status === 200) { + let data = JSON.parse(xhr.responseText); + this.setState({data: data}); + + if (this.props.onReceived) { + this.props.onReceived(data); + } + } + } + } + + xhr.open('POST', '/combatlogrequest', true); + xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); + xhr.send(JSON.stringify({ + username: username, + start: start, + length: length + })); + } + + parseDate(eventTime) { + let month = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + + let date = new Date(eventTime); + return `${date.getDate()} ${month[date.getMonth()]}`; + } +} + +PagedCombatLog.propTypes = { + username: PropTypes.string.isRequired, + start: PropTypes.number.isRequired, + length: PropTypes.number.isRequired, + getFetch: PropTypes.func, + onReceived: PropTypes.func +}; + +export default withRouter(PagedCombatLog); \ No newline at end of file diff --git a/src/components/panels/paged_ladder.jsx b/src/components/panels/paged_ladder.jsx index 3954ca7..76d8734 100644 --- a/src/components/panels/paged_ladder.jsx +++ b/src/components/panels/paged_ladder.jsx @@ -20,12 +20,17 @@ class PagedLadder extends React.Component { return (
-

Username

-

Soldiers

-

Recruits

-

Gold

+

Username

+

Soldiers

+

Recruits

+

Gold

- {Object.keys(this.state.data).map((key) =>
{this.state.data[key].username}

{this.state.data[key].soldiers}

{this.state.data[key].recruits}

{this.state.data[key].gold}

)} + {Object.keys(this.state.data).map((key) =>
+

{this.state.data[key].username}

+

{this.state.data[key].soldiers}

+

{this.state.data[key].recruits}

+

{this.state.data[key].gold}

+
)}
); } diff --git a/webpack.config.js b/webpack.config.js index 212c444..908c145 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -24,7 +24,7 @@ module.exports = { ] }, optimization: { - minimize: true, + minimize: false, minimizer: [ new TerserPlugin({ terserOptions: {