254 lines
No EOL
7 KiB
JavaScript
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; |