unibutton/game.js

254 lines
No EOL
7 KiB
JavaScript

import GameBasic from "./common/game_basic.js";
import NPC from "./common/npc.js";
import Shooter from "./common/shooter.js";
import fetch from "node-fetch";
import initDb from './db.js';
import { createHash } from "crypto";
let db = await initDb();
function sround(n) {
return Math.round(n * 1000) / 1000;
}
class Game extends GameBasic {
constructor() {
super();
this.ws = [];
}
async updateLeaderboard() {
let jason = await db.all('SELECT * FROM stats ORDER BY CAST(ko AS REAL) DESC LIMIT 1000');
let scoresOld = this.scores || [];
let scores = {};
for (let e of jason) {
scores[e.ip] = scores[e.ip] || 0;
scores[e.ip] += e.ko ** 2;
}
for (let score in scores) {
let s = scores[score];
scores[score] = Math.round(Math.sqrt(s));
}
let s = Object.entries(scores).sort((a, b) => b[1] - a[1]).filter(a => a[1] > 5);
for (let x in s) {
let jk = jason.filter(y => y.ip == s[x][0]).map(y => y.ko).sort((a,b) => b - a);
s[x].push(jk.join(', '));
}
this.scores = s;
s = [...s];
s.length = Math.min(s.length,10);
let message = [];
if (scoresOld.length == 0) return;
for (let i = 0; i < this.scores.length || i < scoresOld.length; i++) {
let a = this.scores[i] ? this.scores[i][0] : '!nobody';
let b = this.scores[i + 1] ? this.scores[i + 1][0] : '!nobody';
let c = scoresOld.findIndex(x => x[0] == a);
let d = scoresOld.findIndex(x => x[0] == b);
if (a == '!nobody' || b == '!nobody' || d - c == 1 || (c != -1 && this.scores[i][1] == scoresOld[c][1])) continue;
message.push(`${a} beat ${b} on the leaderboard!`);
}
if (message.length == 0) return;
message = message.join('\n');
console.log(message);
if (!process.env.HOOK) return;
let h = await fetch(
process.env.HOOK,
{
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: 'UniButton',
content: message,
embeds: [
{
color: 11730954,
title: 'Leaderboard',
description: s.map((x, i) => `**#${i + 1}**: ${x[0]} [${x[1]}]`).join('\n'),
},
],
files: []
}),
}
);
}
async sync(full = false) {
let onScreen = this.entities.filter(x => x && x.health > 0).length;
onScreen += 4;
this.width = (this.width * 0.99) + (Math.sqrt(onScreen) * 16);
this.height = (this.height * 0.99) + (Math.sqrt(onScreen) * 16);
let { entities } = this;
let entList = [];
for (let entity of entities) {
entity.camera = entity.camera || false;
let props = entity.serverProps;
let basic = props.map(prop => entity[prop]);
for (let posn in basic) {
let posp = basic[posn];
if (!posp) continue;
if (posp.x && posp.y) {
posp.x = sround(posp.x);
posp.y = sround(posp.y);
}
if (typeof posp == 'number') {
basic[posn] = sround(posp);
}
}
let str = JSON.stringify(basic,null);
entList.push({ entity, str, basic });
}
if (entList.length == 0) return;
for (let client of this.ws) {
let wsEnt = client.ent;
if (!wsEnt) continue;
if (full && wsEnt.health < 1 && !wsEnt.picked) {
wsEnt.picked = true;
let hash = client.username;
if (!hash) {
hash = createHash('sha256');
hash.update((+new Date) + '');
//hash.update(client.ip || '');
hash = hash.digest('hex');
}
await db.run('INSERT INTO stats (username, ip, ko) VALUES (?,?,?)', [
wsEnt.you,
hash,
wsEnt.headCount
]);
this.updateLeaderboard();
}
if (!client.active) continue;
wsEnt.isYou = true;
let filtered;
if (!full) {
filtered = entList.filter(({ entity: ent }) => {
return Math.sqrt((ent.pos.x - wsEnt.pos.x) ** 2 + (ent.pos.y - wsEnt.pos.y) ** 2) < 777
}
);
} else {
filtered = entList;
}
let na = [];
for (let i in filtered) {
let fi = filtered[i]
na.push(fi.str)
if (fi.entity.isYou) {
let narr = [...fi.basic];
narr[fi.entity.serverProps.indexOf('isYou')] = 'true';
na[na.length - 1] = JSON.stringify(narr);
}
}
na.push(JSON.stringify([this.width, this.height]))
client.send(`[${na.join(',')}]`);
wsEnt.isYou = false;
}
for (let entity of entities) {
entity.r = 1;
}
}
init() {
super.init();
this.updateLeaderboard();
let that = this;
that.entities = [];
for (let i = 0; i < 20; i++) {
that.entities.push(new NPC(false, that))
}
for (let i = 0; i < 50; i++) {
that.entities.push(new Shooter(false, that))
}
setInterval(function () { that.sync(false) }, 1000 / 10);
setInterval(function () { that.sync(true) }, 1000);
setInterval(function () { that.clean() }, 1000);
}
clean() {
for (let ent in this.entities) {
let x = this.entities[ent];
if (x.health > 0 || x.type != 'Player') return;
this.entites[ent] = false;
}
}
async authUser(ws, data) {
ws.token = data;
let f = new FormData();
f.append('token', ws.token);
let j = await fetch("https://bg.dervland.net/api/form/auth_api/",
{
"method": "post",
"body": f
}
);
j = await j.text();
try {
j = JSON.parse(j);
} catch (err) {
j = {};
}
if (j.username == '!nobody' || !j.username) return;
if (this.ws.findIndex(x => x.username == j.username && x.ent.health > 0) != -1) {
console.log(`Player ${ws.ent.you} uses username ${j.username} illegally`)
return;
}
ws.ent.username = ws.username = j.username;
console.log(`Player ${ws.ent.you} uses username ${ws.username}`)
}
}
export default Game;