Add PsAdmin support to get player list

This commit is contained in:
Chord 2020-05-12 23:06:22 +02:00
parent d616a57404
commit 1e34cbdfb0
6 changed files with 145 additions and 16 deletions

View file

@ -317,6 +317,17 @@ export async function get_account_by_name(name) {
}
}
export async function get_character_by_name(name) {
try {
const account = await pool.query('SELECT id, account_id, name, faction_id, created, last_login FROM characters WHERE name=$1 AND deleted=false', [name]);
return account.rows[0];
} catch (e) {
if (e.code)
e.code = pg_error_inv[e.code]
throw e;
}
}
export async function create_account(username, password) {
try {
const passhash = await bcrypt.hash(password, BCRYPT_ROUNDS);

View file

@ -1,12 +1,25 @@
import express from 'express'
import * as db from './db.js'
import {get_server_info} from './psadmin.js'
const api = express.Router();
api.get('/stats', async (req, res, next) => {
try {
const stats = await db.get_stats();
res.status(200).json({ ...stats });
const info = get_server_info();
let player_info = []
let players = info.players;
for (let i = 0; i < players.length; i++) {
const char = await db.get_character_by_name(players[i]);
if (char)
player_info = player_info.concat(char)
else
console.log("WARNING: cannot find player info " + players[i])
}
info.players = player_info
res.status(200).json({ ...stats, ...info });
} catch (e) {
console.log(e);
res.status(500).json({ message : 'error' });

83
api/psadmin.js Normal file
View file

@ -0,0 +1,83 @@
import net from 'net'
let error_count = 0;
let server_info = {}
let psadmin_port = 0;
let psadmin_domain = "";
export async function start_server_polling() {
const connect_to = process.env.PSADMIN;
if (!connect_to) {
console.log("WARNING: PSADMIN not configured. Not polling server")
return
} else {
const tokens = connect_to.split(":")
psadmin_domain = tokens[0]
psadmin_port = tokens[1]
console.log("Starting PSAdmin polling for " + connect_to)
}
if (!(await poll_server())) {
console.log("WARNING: initial PSAdmin poll FAILED! Are you sure the server is up and the config is right?")
}
setInterval(poll_server, 10000)
}
async function poll_server() {
try {
const player_list = await get_player_list();
server_info = {status : "UP",players : player_list}
if (error_count > 0) {
console.log("PSAdmin connection has returned after " + error_count + " errors")
}
error_count = 0;
return true;
} catch (e) {
if (error_count < 5)
console.log("WARNING: Failed to get player list: " + e)
server_info = {status : "DOWN",players : []}
error_count += 1
return false;
}
}
export function get_server_info() {
return server_info;
}
async function get_player_list() {
return new Promise((resolve, reject) => {
const client = new net.Socket();
client.connect(psadmin_port, psadmin_domain, function() {
client.write('list_players\n');
});
client.on('error', function(e) {
reject(e)
client.destroy()
});
client.on('data', function(data) {
try {
const info = JSON.parse(data);
resolve(info.player_list)
} catch (e) {
reject(new Error("Failed to parse PSFCI JSON response"))
} finally {
client.destroy(); // kill client after server's response
}
});
client.on('close', function() {
});
});
}

View file

@ -13,6 +13,7 @@
let stats;
let alert;
let players;
function format_account(account) {
return `<a href="/user/${account.id}">${account.username}</a>`
@ -22,6 +23,7 @@
try {
const resp = await axios.get("/api/stats")
stats = resp.data;
players = stats.players
alert.message("")
} catch (e) {
console.log(e)
@ -40,27 +42,44 @@
{#if stats}
<div class="row">
<div class="col-8">
<h1>PSForever Beta Server</h1>
<div class="col-md-8 col-12">
<h1>PSForever Live Server</h1>
<div class="row">
<div class="col">
{#if !$loggedIn}
<a class="btn btn-primary" href="/login" role="button">Login</a>
<a class="btn btn-primary" href="/register" role="button">Create Account</a>
{/if}
<a class="btn btn-secondary" href="https://docs.google.com/document/d/1ZMx1NUylVZCXJNRyhkuVWT0eUKSVYu0JXsU-y3f93BY/edit" role="button">Setup Instructions</a>
</div>
</div><br/>
<p>
<strong>Server address:</strong> <code>play.psforever.net:51200</code> (<a href="https://docs.google.com/document/d/1ZMx1NUylVZCXJNRyhkuVWT0eUKSVYu0JXsU-y3f93BY/edit">Setup Instructions</a>)<br/>
<strong>PSForever accounts:</strong> {stats.accounts.toLocaleString()}<br/>
<strong>Server characters:</strong> {stats.characters.toLocaleString()}<br/>
<strong>Last character created:</strong> <CharacterLink character={stats.last.character} /> (<span title={moment(stats.last.character.created).format(`MMMM Do YYYY, h:mm:ss a`)}>{moment(stats.last.character.created).fromNow()}</span>)</p>
<strong>Server address:</strong> <code>play.psforever.net:51000</code>&nbsp;
<button type="button" class="btn btn-sm" class:btn-success={stats.status == "UP"}
class:btn-danger={stats.status != "UP"}>Server {stats.status}</button><br/>
<strong>Last character created:</strong> <CharacterLink character={stats.last.character} /> (<span title={moment(stats.last.character.created).format(`MMMM Do YYYY, h:mm:ss a`)}>{moment(stats.last.character.created).fromNow()}</span>)<br/>
<strong># Accounts:</strong> {stats.accounts.toLocaleString()}<br/>
<strong># Characters:</strong> {stats.characters.toLocaleString()}
</p>
{#if players}
<h2>Online Players ({players.length})</h2>
<div class="row">
{#each players as char, i}
<div class="col-md-4 col-12"><CharacterLink character={char} /></div>
{/each}
</div>
{/if}
</div>
<div class="col-4">
<div class="col-md-4 col-12 mt-md-0 mt-3">
<EmpireStats stats={stats.empires} />
</div>
</div>
{#if !$loggedIn}
<div class="row">
<div class="col">
<a class="btn btn-primary" href="/login" role="button">Login</a>
<a class="btn btn-primary" href="/register" role="button">Create Account</a>
</div>
</div>
{/if}
{/if}

View file

@ -5,6 +5,7 @@ import morgan from 'morgan'
import history from 'connect-history-api-fallback'
import dotenv from 'dotenv'
import api from './api/index.js'
import { start_server_polling } from './api/psadmin.js'
import * as db from './api/db.js'
const envresult = dotenv.config();
@ -44,6 +45,7 @@ if (process.env.TRUST_PROXY) {
// Needs to be in a function to await the DB connection state
(async () => {
await db.connect_to_db();
await start_server_polling()
const pgSession = connectPg(session);

View file

@ -8,6 +8,7 @@
<link rel='icon' href='/favicon.ico'>
<link href="https://fonts.googleapis.com/css?family=Poppins" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,600;1,400;1,600&display=swap" rel="stylesheet">
<link rel='stylesheet' href='/bundle.css'>
<script defer src='/bundle.js'></script>