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 < 35; 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;