Highscores are displayed in game. Why not on the web page? Let’s do small react app.
Highscore system runs in bckend and it is basically a webservice exporting all tha data as json:
But as you can see, modern browsers do not trust my self signed server certificate (which is enough for game purposes, as android app trusts it explicitely and vice versa). This means, javascript will be unable to fetch this data and display it - because modern browsers protect you from potentially harmful material.
However, my webserver itself uses trustworthy certificates and as I am in full control I can easily sanitise my API for javascirpt use
Sanitizing API with proxy
Apache HTTPD can take incoming URL and redirect request to some other server (in my case - highscore API) - completely transparent to outside world. First we have to enable modules for proxying:
a2enmod proxy
a2enmod proxy_http
And following goes into virtual host section serving my website:
SSLProxyEngine On
SSLProxyVerify none
SSLProxyCheckPeerCN off
SSLProxyCheckPeerName off
SSLProxyCheckPeerExpire off
ProxyAddHeaders off
ProxyPreserveHost off
ProxyPass "/api/lines/public/" "https://localhost:8443/public/"
ProxyPassReverse "/api/lines/public/" "https://localhost:8443/public/"
And after a server restart browser does not complain anymore:
Small react app
Assuming that all the necessary tools are installed we just create small react app:
npx lines-highscore
cd lines-highscore
And start coding src/App.js
import React from "react";
import * as moment from "moment";
// API endpoints
const scores = 'https://www.pribluda.de/api/lines/public/scores?since=0';
const stats = 'https://www.pribluda.de/api/lines/public/stats';
class App extends React.Component {
constructor(props) {
super(props);
// initial state
this.state = {
scores: [],
stats: {
totalGames: 0,
totalTime: 0
}
}
}
// fetch data from backend
componentDidMount() {
fetch(scores)
.then(response => response.json())
.then(data => this.setState({scores: data}));
fetch(stats)
.then(response => response.json())
.then(data => this.setState({stats: data}));
}
// and render it
render() {
const {scores} = this.state
return (
<div id="highscores">
<dl id="stats" >
<dt>Total games:</dt>
<dd>{this.state.stats.totalGames}</dd>
<dt>Total time spent (D:H:M:S) :</dt>
<dd>{msToTime(this.state.stats.totalTime)}</dd>
</dl>
<table className="scoreTable">
<thead className="header">
<tr>
<td className="rightAligned">#</td>
<td>Name</td>
<td>Points</td>
<td>Time spent</td>
<td>Date</td>
</tr>
</thead>
<tbody>
{
// just render score entries
scores.map(function (score, idx) {
return (
<tr className={idx % 2 == 0 ? 'even' : 'odd'}>
<td className="rightAligned">{idx + 1}.</td>
<td>{score.name}</td>
<td className="rightAligned">{score.points}</td>
<td className="rightAligned">{msToTime(score.duration)}</td>
<td className="rightAligned">{msToDate(score.time)}</td>
</tr>
)
}
)
}
</tbody>
</table>
</div>
)
}
}
function msToTime(duration) {
var milliseconds = parseInt((duration % 1000) / 100),
seconds = Math.floor((duration / 1000) % 60),
minutes = Math.floor((duration / (1000 * 60)) % 60),
hours = Math.floor((duration / (1000 * 60 * 60)) %24),
days = Math.floor((duration / (1000 * 60 * 60 *24 )));
hours = (hours < 10) ? "0" + hours : hours;
minutes = (minutes < 10) ? "0" + minutes : minutes;
seconds = (seconds < 10) ? "0" + seconds : seconds;
return days + ":" + hours + ":" + minutes + ":" + seconds ;
}
function msToDate(s) {
return moment.unix(s / 1000).format("DD MMM YYYY hh:mm a")
}
export default App;
Full source is available on github: (https://github.com/ko5tik/pribluda.de/tree/master/lines-highscore)
Result
And after we have build it with npm build we can put it into web page. And now everybody can see actual highscores