mirror of
https://github.com/psforever/PSFPortal.git
synced 2026-01-19 18:14:45 +00:00
Add PsAdmin support to get player list
This commit is contained in:
parent
d616a57404
commit
1e34cbdfb0
11
api/db.js
11
api/db.js
|
|
@ -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);
|
||||
|
|
|
|||
15
api/info.js
15
api/info.js
|
|
@ -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
83
api/psadmin.js
Normal 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() {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -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>
|
||||
<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}
|
||||
|
||||
|
|
|
|||
2
index.js
2
index.js
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue