Skip to content

Commit

Permalink
/matches: Save history of all matches to database.
Browse files Browse the repository at this point in the history
  • Loading branch information
geneotech committed Jan 15, 2024
1 parent 7a1a357 commit 07b4c21
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 4 deletions.
15 changes: 15 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,20 @@ if (!fs.existsSync(dbPath)) {
matches_lost INTEGER DEFAULT 0
)
`);

db.run(`
CREATE TABLE IF NOT EXISTS matches (
match_id INTEGER PRIMARY KEY AUTOINCREMENT,
server_name TEXT,
arena TEXT,
game_mode TEXT,
winners TEXT,
losers TEXT,
win_score INTEGER,
lose_score INTEGER,
match_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
});

// Close the database connection
Expand Down Expand Up @@ -173,6 +187,7 @@ app.get('/discord', (req, res) => res.redirect(discord));
app.use('/upload', require('./src/upload'));
app.use('/report_match', require('./src/report_match'));
app.use('/leaderboards', require('./src/leaderboards'));
app.use('/matches', require('./src/matches'));
app.get('/admin', adm, (req, res) => res.redirect('/admin/system'));
app.use('/admin/system', adm, require('./src/admin/system'));
app.use('/admin/visitors', adm, require('./src/admin/visitors')(visitors));
Expand Down
53 changes: 53 additions & 0 deletions src/matches.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const express = require('express');
const Database = require('better-sqlite3');

const router = express.Router();
const dbPath = process.env.DB_PATH;

router.get('/', (req, res) => {
try {
// Open the database connection
const db = new Database(dbPath);

// Prepare and execute the query to fetch all matches
const stmt = db.prepare('SELECT * FROM matches');
const rows = stmt.all();

// Create a response array with detailed match information
const matches = rows.map(match => {
// Parse winner and loser IDs from JSON strings
const winners = JSON.parse(match.winners);
const losers = JSON.parse(match.losers);

// Return the detailed match information
return {
match_id: match.match_id,
server_name: match.server_name,
arena: match.arena,
game_mode: match.game_mode,
winners,
losers,
win_score: match.win_score,
lose_score: match.lose_score,
match_date: match.match_date
};
});

matches.sort((a, b) => b.match_id - a.match_id);

if (req.query.format !== undefined && req.query.format == 'json') {
return res.status(200).json({ matches });
}
else {
res.render('matches', {
page: 'Matches',
user: req.user,
matches: matches
});
}
} catch (error) {
res.status(500).json({ error: error.message });
}
});

module.exports = router;
52 changes: 48 additions & 4 deletions src/report_match.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function apiKeyAuth(req, res, next) {
router.post('/', apiKeyAuth, (req, res) => {
res.setHeader('Content-Type', 'application/json; charset=utf-8');

const { win_score, lose_score, win_players, lose_players, nicknames } = req.body;
const { server_name, arena, game_mode, win_score, lose_score, win_players, lose_players, nicknames } = req.body;

// Validate input
if (typeof win_score === 'undefined') {
Expand All @@ -41,6 +41,18 @@ router.post('/', apiKeyAuth, (req, res) => {
return res.status(400).json({ error: 'Missing nicknames' });
}

if (!server_name) {
return res.status(400).json({ error: 'Missing server_name' });
}

if (!arena) {
return res.status(400).json({ error: 'Missing arena' });
}

if (!game_mode) {
return res.status(400).json({ error: 'Missing game_mode' });
}

if (win_players.length == 0 || lose_players.length == 0) {
return res.status(400).json({ error: 'Invalid input format: lose/win players array is empty' });
}
Expand Down Expand Up @@ -68,9 +80,9 @@ router.post('/', apiKeyAuth, (req, res) => {
});

// Calculate new ratings
const winners = win_players.map(playerId => playerRatings[playerId]);
const losers = lose_players.map(playerId => playerRatings[playerId]);
const updatedRatings = rate([winners, losers]);
const winner_ratings = win_players.map(playerId => playerRatings[playerId]);
const loser_ratings = lose_players.map(playerId => playerRatings[playerId]);
const updatedRatings = rate([winner_ratings, loser_ratings]);

// Update players in the database with new ratings
updatedRatings.forEach((team, index) => {
Expand All @@ -84,6 +96,38 @@ router.post('/', apiKeyAuth, (req, res) => {
stmt_update_player.run(playerRating.mu, playerRating.sigma, mmr, winIncrement, lossIncrement, new_nickname, playerId);
});
});

const winners = [];
const losers = [];

updatedRatings[0].forEach((rating, index) => {
const player_id = win_players[index];

winners.push({
nickname: nicknames[player_id],
id: player_id,
new_mmr: ordinal(rating),
mmr_delta: ordinal(rating) - ordinal(winner_ratings[index])
});
});

updatedRatings[1].forEach((rating, index) => {
const player_id = lose_players[index];

losers.push({
nickname: nicknames[player_id],
id: player_id,
new_mmr: ordinal(rating),
mmr_delta: ordinal(rating) - ordinal(loser_ratings[index])
});
});

const insertMatchSql = `
INSERT INTO matches (server_name, arena, game_mode, winners, losers, win_score, lose_score)
VALUES (?, ?, ?, ?, ?, ?, ?)
`;

db.prepare(insertMatchSql).run(server_name, arena, game_mode, JSON.stringify(winners), JSON.stringify(losers), win_score, lose_score);
})(); // Execute the transaction

res.json({ message: 'Match reported successfully' });
Expand Down
53 changes: 53 additions & 0 deletions views/matches.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<%- include('overall_header'); %>

<h1>Matches</h1>
<div class="table-responsive">
<table class="sortable">
<thead>
<tr>
<th>#</th>
<th>Winning team</th>
<th>Losing team</th>
<th>Score</th>
<th>Arena</th>
<th>Game mode</th>
<th>Date</th>
</tr>
</thead>
<tbody>
<% matches.forEach(i => { %>
<tr>
<td><%= i.match_id %></td>
<td>
<% i.winners.forEach(player => { %>
<a href="/user/<%= player.id %>">
<%= player.nickname %>
</a>
<span style="color: <%= player.mmr_delta >= 0 ? 'chartreuse' : 'red' %>;">
<%= player.mmr_delta >= 0 ? '' : '' %><%= Math.abs(player.mmr_delta.toFixed(2)) %>
</span>
<% if (i.winners.indexOf(player) < i.winners.length - 1) { %>, <% } %>
<% }); %>
</td>
<td>
<% i.losers.forEach(player => { %>
<a href="/user/<%= player.id %>">
<%= player.nickname %>
</a>
<span style="color: <%= player.mmr_delta >= 0 ? 'chartreuse' : 'red' %>;">
<%= player.mmr_delta >= 0 ? '' : '' %><%= Math.abs(player.mmr_delta.toFixed(2)) %>
</span>
<% if (i.losers.indexOf(player) < i.losers.length - 1) { %>, <% } %>
<% }); %>
</td>
<td><%= i.win_score %>:<%= i.lose_score %></td>
<td><%= i.arena %></td>
<td><%= i.game_mode %></td>
<td><%= new Date(i.match_date).toLocaleString() %></td>
</tr>
<% }); %>
</tbody>
</table>
</div>

<%- include('overall_footer'); %>
3 changes: 3 additions & 0 deletions views/overall_header.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
<a href="/leaderboards"<% if(page == 'Leaderboards'){ %> class="active"<% } %> aria-label="Leaderboards">
<span class="desktop">Leaderboards</span><span class="mobile"><i class="fa-solid fa-ranking-star"></i></span>
</a>
<a href="/matches"<% if(page == 'Matches'){ %> class="active"<% } %> aria-label="Leaderboards">
<span class="desktop">Matches</span><span class="mobile"><i class="fa-solid fa-users"></i></span>
</a>
<a href="/arenas"<% if(page == 'Arenas'){ %> class="active"<% } %> aria-label="Arenas">
<span class="desktop">Arenas</span><span class="mobile"><i class="fa-solid fa-map"></i></span>
</a>
Expand Down

0 comments on commit 07b4c21

Please sign in to comment.