kill insertion working

This commit is contained in:
2023-08-12 13:51:18 +02:00
parent 338a283a07
commit 59c53a8417
9 changed files with 330 additions and 94 deletions
+1 -19
View File
@@ -10,25 +10,7 @@
"name": "Launch Server API", "name": "Launch Server API",
"skipFiles": ["<node_internals>/**"], "skipFiles": ["<node_internals>/**"],
"program": "${workspaceFolder}/src/serverMain.ts", "program": "${workspaceFolder}/src/serverMain.ts",
"preLaunchTask": "tsc: build - tsconfig.json", "preLaunchTask": "npm: build",
"outFiles": ["${workspaceFolder}/out/**/*.js"]
},
{
"type": "node",
"request": "launch",
"name": "Launch Client API",
"skipFiles": ["<node_internals>/**"],
"program": "${workspaceFolder}/src/clientMain.ts",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": ["${workspaceFolder}/out/**/*.js"]
},
{
"type": "node",
"request": "launch",
"name": "Launch Websocket",
"skipFiles": ["<node_internals>/**"],
"program": "${workspaceFolder}/src/websocket.ts",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": ["${workspaceFolder}/out/**/*.js"] "outFiles": ["${workspaceFolder}/out/**/*.js"]
} }
] ]
+25
View File
@@ -0,0 +1,25 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "typescript",
"tsconfig": "tsconfig.json",
"problemMatcher": ["$tsc"],
"group": "build",
"label": "tsc: build - tsconfig.json"
},
{
"label": "myShellCommand",
"type": "shell",
"command": "echo goodfood"
},
{
"type": "npm",
"script": "build",
"group": "build",
"problemMatcher": [],
"label": "npm: build",
"detail": "npm run generate && npx tsc"
}
]
}
+1 -1
Submodule src/db updated: c985feb726...dc5325c23a
+34 -22
View File
@@ -1,31 +1,43 @@
// Typed routes from https://urosstok.com/blog/typed-routes-in-express // Typed routes from https://urosstok.com/blog/typed-routes-in-express
import * as dotenv from "dotenv";
import express from "express";
import cors from "cors";
import { dbReady } from "./db";
import server from "./generated/server";
dotenv.config();
const app = express();
const port = 3001;
import * as dotenv from 'dotenv' app.use(express.json());
import express from 'express'
import cors from 'cors'
import { dbReady } from './db/db'
import server from './generated/server'
dotenv.config()
const app = express() app.use(cors());
const port = 3001 app.get("/", (req, res) => {
res.send("Tone server API online");
});
app.use(express.json()) app.use("/", (req, res, next) => {
try {
app.use(cors()) next();
app.get('/', (req, res) => { } catch (error) {
res.send('Tone server API online') res.status(500).send({
}) errors: [
{
app.use('/', server) msg: "Internal error",
data: error,
},
],
});
}
});
app.use("/", server);
export default new Promise((resolve, reject) => { export default new Promise((resolve, reject) => {
void dbReady().then((e) => { void dbReady().then((e) => {
const listenServer = app.listen(port, '0.0.0.0', () => { const listenServer = app.listen(port, "0.0.0.0", () => {
console.log(`Tone server api listening on port ${port}`) console.log(`Tone server api listening on port ${port}`);
resolve(listenServer) resolve(listenServer);
}) });
}) });
}) });
+87 -21
View File
@@ -1,10 +1,15 @@
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
import { Router, Request, Response } from "express"; import { Router, Request, Response } from "express";
import { header } from "express-validator"; import { header, param } from "express-validator";
import db, { /* CreateKillRecord, */ CheckServerToken } from "../db/db"; import {
checkOrCreateLoadout,
checkOrCreateWeapon,
checkUpdateOrCreatePlayer,
} from "../utils";
import db, { /* createKillRecord, */ checkServerToken } from "../db";
import { validateErrors } from "../common"; import { validateErrors } from "../common";
import { KillData, MatchData, validateBody } from "../server/types"; import { KillData, MatchData, validateBody } from "../types";
import typia from "typia"; import typia, { validate } from "typia";
const router = Router(); const router = Router();
@@ -23,7 +28,7 @@ router.post(
if (!req.headers.authorization) { if (!req.headers.authorization) {
return res.sendStatus(401); return res.sendStatus(401);
} }
const query = await CheckServerToken( const query = await checkServerToken(
req.headers.authorization.split(" ")[1] req.headers.authorization.split(" ")[1]
); );
if (!query?.host_id) { if (!query?.host_id) {
@@ -85,7 +90,14 @@ router.post(
(req: RequestBody<KillData>, res: Response) => { (req: RequestBody<KillData>, res: Response) => {
void (async () => { void (async () => {
const host_id = res.locals.host_id; const host_id = res.locals.host_id;
const { match_id } = req.body; const {
game_time,
distance,
match_id,
attacker: attackerData,
victim: victimData,
cause_of_death,
} = req.body;
const match = await db const match = await db
.selectFrom("ToneAPI_v3.match") .selectFrom("ToneAPI_v3.match")
.select(["ToneAPI_v3.match.host_id", "ToneAPI_v3.match.match_id"]) .select(["ToneAPI_v3.match.host_id", "ToneAPI_v3.match.match_id"])
@@ -93,20 +105,60 @@ router.post(
.where("ToneAPI_v3.match.match_id", "=", match_id) .where("ToneAPI_v3.match.match_id", "=", match_id)
.executeTakeFirst(); .executeTakeFirst();
if (!match) { if (!match) {
res res.status(403).send({
.status(403) errors: [
.send({ {
errors: [ msg: "Match does not exists",
{ param: "match_id",
msg: "Match does not exists", location: "body",
param: "match_id", },
location: "body", ],
}, });
],
});
return; return;
} }
req.body; let attacker_loadout: number | undefined;
let victim_loadout: number | undefined;
await Promise.all([
checkUpdateOrCreatePlayer(attackerData),
checkUpdateOrCreatePlayer(victimData),
checkOrCreateWeapon(cause_of_death),
checkOrCreateWeapon(attackerData.current_weapon.id),
checkOrCreateWeapon(victimData.current_weapon.id),
checkOrCreateLoadout(attackerData.loadout).then(
(e) => (attacker_loadout = e)
),
checkOrCreateLoadout(victimData.loadout).then(
(e) => (victim_loadout = e)
),
]);
if (attacker_loadout === undefined) {
throw new Error("attacker_lodaout is undefined");
}
if (victim_loadout === undefined) {
throw new Error("victim_lodaout is undefined");
}
const insertResult = await db
.insertInto("ToneAPI_v3.kill")
.values({
attacker_id: attackerData.id,
victim_id: victimData.id,
match_id,
attacker_loadout_id: attacker_loadout,
victim_loadout_id: victim_loadout,
attacker_speed: attackerData.velocity,
victim_speed: victimData.velocity,
attacker_movementstate: attackerData.state,
victim_movementstate: victimData.state,
distance,
game_time,
cause_of_death,
attacker_held_weapon: attackerData.current_weapon.id,
victim_held_weapon: victimData.current_weapon.id,
})
.returning("kill_id")
.executeTakeFirstOrThrow();
res.status(201).send({ id: insertResult.kill_id });
})(); })();
} }
); );
@@ -130,11 +182,25 @@ router.post(
.values({ host_id, server_name }) .values({ host_id, server_name })
.execute(); .execute();
} }
await db.updateTable("ToneAPI_v3.match")
.set({ ongoing: false })
.where("ToneAPI_v3.match.server_name", "=", server_name).execute();
const match = await db const match = await db
.insertInto("ToneAPI_v3.match") .insertInto("ToneAPI_v3.match")
.values({ air_accel, server_name, game_map, gamemode, host_id }) .values({
.executeTakeFirst(); air_accel,
res.status(201).send({ match: match.insertId }); server_name,
game_map,
gamemode,
host_id,
ongoing: true,
})
.returning("ToneAPI_v3.match.match_id")
.executeTakeFirstOrThrow();
res.status(201).send({ match: match?.match_id });
})(); })();
} }
); );
+14 -10
View File
@@ -2,24 +2,28 @@ import { RequestHandler, Request } from "express";
import typia from "typia"; import typia from "typia";
type WeaponKillData = { type WeaponKillData = {
name: string; id: string;
mods: number; mods: number;
}; };
export type LoadoutKillData = {
ordnance: WeaponKillData | null;
secondary: WeaponKillData | null;
primary: WeaponKillData | null;
tactical: WeaponKillData | null;
anti_titan: WeaponKillData | null;
passive1:string | null;
passive2:string | null;
titan: string | null;
};
type PlayerKillData = { type PlayerKillData = {
velocity: number; velocity: number;
name: string; name: string;
loadout: { loadout: LoadoutKillData;
ordnance: WeaponKillData;
secondary: WeaponKillData;
primary: WeaponKillData;
tactical: WeaponKillData;
anti_titan: WeaponKillData;
};
current_weapon: WeaponKillData; current_weapon: WeaponKillData;
state: string; state: string;
titan: string | null; id: number;
id: number | bigint;
cloaked: boolean; cloaked: boolean;
}; };
+138
View File
@@ -0,0 +1,138 @@
import db from "./db";
import { LoadoutKillData } from "./types";
export async function checkUpdateOrCreatePlayer(data: {
id: number;
name: string;
}) {
const player = await db
.selectFrom("ToneAPI_v3.player")
.select(["player_name"])
.where("player_id", "=", data.id)
.executeTakeFirst();
if (!player) {
await db
.insertInto("ToneAPI_v3.player")
.values({
player_id: data.id,
player_name: data.name,
})
.execute();
} else if (player.player_name != data.name) {
await db
.updateTable("ToneAPI_v3.player")
.set({ player_name: data.name })
.where("ToneAPI_v3.player.player_id", "=", data.id)
.execute();
}
}
export async function checkOrCreateWeapon(weapon_id: string) {
const weapon = await db
.selectFrom("ToneAPI_v3.weapon")
.select("ToneAPI_v3.weapon.weapon_id")
.where("weapon_id", "=", weapon_id)
.executeTakeFirst();
if (!weapon) {
await db
.insertInto("ToneAPI_v3.weapon")
.values({
weapon_id,
})
.execute();
}
}
export async function checkOrCreateWeaponMods(weapon_mods: {
id: string;
mods: number;
}) {
await checkOrCreateWeapon(weapon_mods.id);
const weaponMods = await db
.selectFrom("ToneAPI_v3.mods_on_weapon")
.select("ToneAPI_v3.mods_on_weapon.mod_id")
.where("mod_id", "=", weapon_mods.mods)
.where("weapon_id", "=", weapon_mods.id)
.executeTakeFirst();
if (!weaponMods) {
await db
.insertInto("ToneAPI_v3.mods_on_weapon")
.values({
mod_id: weapon_mods.mods,
weapon_id: weapon_mods.id,
autogenerated: true,
})
.execute();
}
}
export async function checkOrCreateTitan(titan_id: string | null) {
if (titan_id == null) {
return;
}
const titan = await db
.selectFrom("ToneAPI_v3.titan_chassis")
.select("ToneAPI_v3.titan_chassis.titan_id")
.where("titan_id", "=", titan_id)
.executeTakeFirst();
if (!titan) {
await db
.insertInto("ToneAPI_v3.titan_chassis")
.values({
titan_id,
})
.execute();
}
}
export async function checkOrCreateLoadout(loadoutData: LoadoutKillData) {
let loadout = await db
.selectFrom("ToneAPI_v3.loadout")
.select("ToneAPI_v3.loadout.loadout_id")
.where("primary_weapon", "=", loadoutData.primary?.id ?? null)
.where("primary_mod_id", "=", loadoutData.primary?.mods ?? null)
.where("secondary_weapon", "=", loadoutData.secondary?.id ?? null)
.where("secondary_mod_id", "=", loadoutData.secondary?.mods ?? null)
.where("anti_titan_weapon", "=", loadoutData.anti_titan?.id ?? null)
.where("anti_titan_mod_id", "=", loadoutData.anti_titan?.mods ?? null)
.where("ToneAPI_v3.loadout.ordnance", "=", loadoutData.ordnance?.id ?? null)
.where("ToneAPI_v3.loadout.tactical", "=", loadoutData.tactical?.id ?? null)
.where("ToneAPI_v3.loadout.pilot_passive_1", "=", loadoutData.passive1)
.where("ToneAPI_v3.loadout.pilot_passive_2", "=", loadoutData.passive2)
.where("ToneAPI_v3.loadout.titan_id", "=", loadoutData.titan)
.executeTakeFirst();
if (!loadout) {
const promises = new Array<Promise<void>>();
if (loadoutData.primary !== null) {
promises.push(checkOrCreateWeaponMods(loadoutData.primary));
}
if (loadoutData.secondary !== null) {
promises.push(checkOrCreateWeaponMods(loadoutData.secondary));
}
if (loadoutData.anti_titan !== null) {
promises.push(checkOrCreateWeaponMods(loadoutData.anti_titan));
}
if (loadoutData.ordnance !== null) {
promises.push(checkOrCreateWeapon(loadoutData.ordnance.id));
}
await Promise.all([...promises, checkOrCreateTitan(loadoutData.titan)]);
const result = await db
.insertInto("ToneAPI_v3.loadout")
.values({
primary_weapon: loadoutData.primary?.id ?? null,
primary_mod_id: loadoutData.primary?.mods ?? null,
secondary_weapon: loadoutData.secondary?.id ?? null,
secondary_mod_id: loadoutData.secondary?.mods ?? null,
anti_titan_weapon: loadoutData.anti_titan?.id ?? null,
anti_titan_mod_id: loadoutData.anti_titan?.mods ?? null,
ordnance: loadoutData.ordnance?.id ?? null,
tactical: loadoutData.tactical?.id ?? null,
pilot_passive_1: loadoutData.passive1,
pilot_passive_2: loadoutData.passive2,
titan_id: loadoutData.titan,
}).returning('ToneAPI_v3.loadout.loadout_id')
.executeTakeFirstOrThrow();
return result.loadout_id;
}
return loadout.loadout_id
}
+29 -20
View File
@@ -1,8 +1,8 @@
import { afterAll, beforeAll, describe, expect, test } from '@jest/globals' import { afterAll, beforeAll, describe, expect, test } from '@jest/globals'
import serverMain from '../src/serverMain' import serverMain from '../src/serverMain'
import db from "../src/db/db" import db, {dbReady} from "../src/db"
import * as dotenv from 'dotenv' import * as dotenv from 'dotenv'
import {type KillData} from '../src/server/types' import {type KillData} from '../src/types'
dotenv.config() dotenv.config()
let listenServer let listenServer
@@ -11,7 +11,7 @@ const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
'Authorization': `Bearer ${Buffer.from(process.env.SERVERAUTH_TOKEN + '').toString('base64')}` 'Authorization': `Bearer ${Buffer.from(process.env.SERVERAUTH_TOKEN + '').toString('base64')}`
} }
const testMatch = { air_accel:false, server_name:Math.floor(Math.random()*100).toString(), game_map:"testMap", gamemode:"test" } const testMatch = { air_accel:false, server_name:"servertest"+Math.floor(Math.random()*100).toString(), game_map:"testMap", gamemode:"test" }
const testKill = { const testKill = {
"game_time": 22.616668701171876, "game_time": 22.616668701171876,
"player_count": 1, "player_count": 1,
@@ -20,68 +20,74 @@ const testKill = {
"velocity": 0.0, "velocity": 0.0,
"name": "Legonzaur", "name": "Legonzaur",
"loadout": { "loadout": {
"titan":"testTitan",
"passive1":"testPassive1",
"passive2":"testPassive2",
"ordnance": { "ordnance": {
"name": "mp_weapon_satchel", "id": "mp_weapon_satchel",
"mods": 0 "mods": 0
}, },
"secondary": { "secondary": {
"name": "mp_weapon_wingman", "id": "mp_weapon_wingman",
"mods": 140 "mods": 140
}, },
"primary": { "primary": {
"name": "mp_weapon_sniper", "id": "mp_weapon_sniper",
"mods": 1168 "mods": 1168
}, },
"tactical": { "tactical": {
"name": "mp_ability_grapple", "id": "mp_ability_grapple",
"mods": 8 "mods": 8
}, },
"anti_titan": { "anti_titan": {
"name": "mp_weapon_defender", "id": "mp_weapon_defender",
"mods": 134 "mods": 134
} }
}, },
"current_weapon": { "current_weapon": {
"name": "mp_weapon_sniper", "id": "mp_weapon_sniper",
"mods": 1168 "mods": 1168
}, },
"state": "OnGround", "state": "OnGround",
"titan": "null", "titan": "null",
"id": 1005930844007n, "id": 1005930844007,
"cloaked": false "cloaked": false
}, },
"attacker": { "attacker": {
"velocity": 0.0, "velocity": 0.0,
"name": "Legonzaur", "name": "Legonzaur",
"loadout": { "loadout": {
"titan":"testTitan",
"passive1":"testPassive1",
"passive2":"testPassive2",
"ordnance": { "ordnance": {
"name": "mp_weapon_satchel", "id": "mp_weapon_satchel",
"mods": 0 "mods": 0
}, },
"secondary": { "secondary": {
"name": "mp_weapon_wingman", "id": "mp_weapon_wingman",
"mods": 140 "mods": 140
}, },
"primary": { "primary": {
"name": "mp_weapon_sniper", "id": "mp_weapon_sniper",
"mods": 1168 "mods": 1168
}, },
"tactical": { "tactical": {
"name": "mp_ability_grapple", "id": "mp_ability_grapple",
"mods": 8 "mods": 8
}, },
"anti_titan": { "anti_titan": {
"name": "mp_weapon_defender", "id": "mp_weapon_defender",
"mods": 134 "mods": 134
} }
}, },
"current_weapon": { "current_weapon": {
"name": "mp_weapon_sniper", "id": "mp_weapon_sniper",
"mods": 1168 "mods": 1168
}, },
"state": "OnGround", "state": "OnGround",
"titan": "null", "titan": "null",
"id": 1005930844007n, "id": 1005930844007,
"cloaked": false "cloaked": false
}, },
"distance": 0.0, "distance": 0.0,
@@ -91,6 +97,7 @@ const testKill = {
beforeAll(async () => { beforeAll(async () => {
listenServer = await serverMain; listenServer = await serverMain;
await dbReady()
}) })
describe('server', () => { describe('server', () => {
@@ -114,7 +121,7 @@ describe('server', () => {
'Authorization': `Bearer ${Buffer.from('badtoken').toString('base64')}` 'Authorization': `Bearer ${Buffer.from('badtoken').toString('base64')}`
} }
}); });
expect(response2.status).toBe(403) expect(response2.status).toBe(401)
}) })
test('bad kill token', async () => { test('bad kill token', async () => {
@@ -126,7 +133,7 @@ describe('server', () => {
'Authorization': `Bearer ${Buffer.from('badtoken').toString('base64')}` 'Authorization': `Bearer ${Buffer.from('badtoken').toString('base64')}`
} }
}); });
expect(response2.status).toBe(403) expect(response2.status).toBe(401)
}) })
test('good auth prefetch', async () => { test('good auth prefetch', async () => {
@@ -144,7 +151,9 @@ describe('server', () => {
headers, headers,
body: JSON.stringify(testMatch), body: JSON.stringify(testMatch),
}); });
expect(await response.json()).toHaveProperty("match") const json = await response.json()
expect(json).toHaveProperty("match")
expect(json.match).not.toBeNaN()
expect(response.status).toBe(201) expect(response.status).toBe(201)
}) })
+1 -1
View File
@@ -101,5 +101,5 @@
"skipLibCheck": true /* Skip type checking all .d.ts files. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */
}, },
"compileOnSave": true, "compileOnSave": true,
"exclude": ["jest.config.ts", "./tests"] "exclude": ["jest.config.ts", "./tests", "./src/templates"]
} }