handle match close
This commit is contained in:
+155
-94
@@ -1,103 +1,164 @@
|
|||||||
import { Router, Response } from "express";
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
import { param } from "express-validator";
|
import { Router, type Response } from 'express'
|
||||||
import { validateErrors } from "../common";
|
import { param } from 'express-validator'
|
||||||
import { MatchData, validateBody, RequestBody } from "../types";
|
import { validateErrors } from '../common'
|
||||||
import db from "../db";
|
import { type MatchData, type MatchCloseData, validateBody, type RequestBody } from '../types'
|
||||||
import typia from "typia";
|
import db from '../db'
|
||||||
|
import typia from 'typia'
|
||||||
|
import { checkOrCreateTitan, checkOrCreateWeapon } from '../utils'
|
||||||
|
import { type TitanStatsInMatchTable, type WeaponStatsInMatchTable } from '../db/model'
|
||||||
|
|
||||||
const router = Router();
|
const router = Router()
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"/match",
|
'/',
|
||||||
validateBody(typia.createValidate<MatchData>()),
|
validateBody(typia.createValidate<MatchData>()),
|
||||||
(req: RequestBody<MatchData>, res: Response) => {
|
(req: RequestBody<MatchData>, res: Response) => {
|
||||||
void (async () => {
|
void (async () => {
|
||||||
const host_id = res.locals.host_id;
|
const host_id = res.locals.host_id
|
||||||
const { air_accel, server_name, game_map, gamemode } = req.body;
|
const { air_accel, server_name, game_map, gamemode } = req.body
|
||||||
const server = await db
|
const server = await db
|
||||||
.selectFrom("ToneAPI_v3.server")
|
.selectFrom('ToneAPI_v3.server')
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where("ToneAPI_v3.server.host_id", "=", host_id)
|
.where('ToneAPI_v3.server.host_id', '=', host_id)
|
||||||
.where("ToneAPI_v3.server.server_name", "=", server_name)
|
.where('ToneAPI_v3.server.server_name', '=', server_name)
|
||||||
.executeTakeFirst();
|
.executeTakeFirst()
|
||||||
if (!server) {
|
if (!server) {
|
||||||
await db
|
await db
|
||||||
.insertInto("ToneAPI_v3.server")
|
.insertInto('ToneAPI_v3.server')
|
||||||
.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
|
||||||
|
.insertInto('ToneAPI_v3.match')
|
||||||
|
.values({
|
||||||
|
air_accel,
|
||||||
|
server_name,
|
||||||
|
game_map,
|
||||||
|
gamemode,
|
||||||
|
host_id,
|
||||||
|
ongoing: true
|
||||||
|
})
|
||||||
|
.returning('ToneAPI_v3.match.match_id')
|
||||||
|
.executeTakeFirstOrThrow()
|
||||||
|
|
||||||
|
res.status(201).send({ match: match?.match_id })
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
'/:match_id/close',
|
||||||
|
param('match_id').exists().withMessage('Missing Match ID').bail().isNumeric().withMessage('Match ID is not numeric'),
|
||||||
|
validateErrors,
|
||||||
|
validateBody(typia.createValidate<MatchCloseData>()),
|
||||||
|
(req: RequestBody<MatchCloseData>, res: Response) => {
|
||||||
|
void (async () => {
|
||||||
|
const host_id = res.locals.host_id
|
||||||
|
const match_id = req.params.match_id
|
||||||
|
const match = await db
|
||||||
|
.selectFrom('ToneAPI_v3.match')
|
||||||
|
.selectAll()
|
||||||
|
.where('ToneAPI_v3.match.host_id', '=', host_id)
|
||||||
|
.where('ToneAPI_v3.match.match_id', '=', match_id)
|
||||||
|
.executeTakeFirst()
|
||||||
|
if (!match) {
|
||||||
|
res.status(403).send({
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
msg: 'Match does not exists for this host',
|
||||||
|
param: 'match_id',
|
||||||
|
location: 'param'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const playerPromises: Array<Promise<any>> = []
|
||||||
|
for (const playerId in req.body) {
|
||||||
|
const NumPlayerId = Number(playerId)
|
||||||
|
if (isNaN(NumPlayerId)) {
|
||||||
|
res
|
||||||
|
.status(400)
|
||||||
|
.send({ errors: [{ msg: 'playerId is NaN', location: 'body' }] })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const playerData = req.body[playerId]
|
||||||
|
console.log(req.body)
|
||||||
|
|
||||||
|
const promises: Array<Promise<any>> = []
|
||||||
|
const weaponStats: WeaponStatsInMatchTable[] = []
|
||||||
|
const titanStats: TitanStatsInMatchTable[] = []
|
||||||
|
for (const weaponId in playerData.weapons) {
|
||||||
|
promises.push(
|
||||||
|
checkOrCreateWeapon(weaponId)
|
||||||
|
.then(async () => {
|
||||||
|
weaponStats.push({
|
||||||
|
match_id: match.match_id,
|
||||||
|
weapon_id: weaponId,
|
||||||
|
player_id: NumPlayerId,
|
||||||
|
headshots: playerData.weapons[weaponId].shotsHeadshot,
|
||||||
|
playtime: playerData.weapons[weaponId].playtime,
|
||||||
|
ricochets: playerData.weapons[weaponId].shotsRichochet,
|
||||||
|
shots_fired: playerData.weapons[weaponId].shotsFired,
|
||||||
|
shots_hit: playerData.weapons[weaponId].shotsHit
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for (const titanId in playerData.titans) {
|
||||||
|
promises.push(
|
||||||
|
checkOrCreateTitan(titanId).then(async () => {
|
||||||
|
titanStats.push({
|
||||||
|
match_id: match.match_id,
|
||||||
|
titan_id: titanId,
|
||||||
|
player_id: NumPlayerId,
|
||||||
|
headshots: playerData.titans[titanId].shotsHeadshot,
|
||||||
|
playtime: playerData.titans[titanId].playtime,
|
||||||
|
ricochets: playerData.titans[titanId].shotsRichochet,
|
||||||
|
shots_fired: playerData.titans[titanId].shotsFired,
|
||||||
|
shots_hit: playerData.titans[titanId].shotsHit
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
await db
|
playerPromises.push(Promise.all(promises).then(async e => {
|
||||||
.updateTable("ToneAPI_v3.match")
|
await db.insertInto('ToneAPI_v3.weapon_stats_in_match')
|
||||||
.set({ ongoing: false })
|
.values(weaponStats)
|
||||||
.where("ToneAPI_v3.match.server_name", "=", server_name)
|
.execute()
|
||||||
.execute();
|
}))
|
||||||
|
|
||||||
const match = await db
|
playerPromises.push(Promise.all(promises).then(async e => {
|
||||||
.insertInto("ToneAPI_v3.match")
|
await db.insertInto('ToneAPI_v3.titan_stats_in_match')
|
||||||
|
.values(titanStats)
|
||||||
|
.execute()
|
||||||
|
}))
|
||||||
|
|
||||||
|
playerPromises.push(db.insertInto('ToneAPI_v3.player_stats_in_match')
|
||||||
.values({
|
.values({
|
||||||
air_accel,
|
distance_air: playerData.stats.distance.air,
|
||||||
server_name,
|
distance_ground: playerData.stats.distance.ground,
|
||||||
game_map,
|
distance_wall: playerData.stats.distance.wall,
|
||||||
gamemode,
|
time_air: playerData.stats.time.air,
|
||||||
host_id,
|
time_ground: playerData.stats.time.ground,
|
||||||
ongoing: true,
|
time_wall: playerData.stats.time.wall,
|
||||||
|
match_id,
|
||||||
|
player_id: NumPlayerId
|
||||||
})
|
})
|
||||||
.returning("ToneAPI_v3.match.match_id")
|
.execute())
|
||||||
.executeTakeFirstOrThrow();
|
}
|
||||||
|
await Promise.all(playerPromises)
|
||||||
|
res.sendStatus(201)
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
res.status(201).send({ match: match?.match_id });
|
export default router
|
||||||
})();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
router.post(
|
|
||||||
"/match/:matchId(\d+)/close",
|
|
||||||
param('matchId').exists().withMessage("Missing Match ID").bail().isNumeric().withMessage("Match ID is not numeric"),
|
|
||||||
validateErrors,
|
|
||||||
validateBody(typia.createValidate<MatchData>()),
|
|
||||||
(req: RequestBody<MatchData>, res: Response) => {
|
|
||||||
void (async () => {
|
|
||||||
const host_id = res.locals.host_id;
|
|
||||||
const { air_accel, server_name, game_map, gamemode } = req.body;
|
|
||||||
const server = await db
|
|
||||||
.selectFrom("ToneAPI_v3.server")
|
|
||||||
.selectAll()
|
|
||||||
.where("ToneAPI_v3.server.host_id", "=", host_id)
|
|
||||||
.where("ToneAPI_v3.server.server_name", "=", server_name)
|
|
||||||
.executeTakeFirst();
|
|
||||||
if (!server) {
|
|
||||||
await db
|
|
||||||
.insertInto("ToneAPI_v3.server")
|
|
||||||
.values({ host_id, server_name })
|
|
||||||
.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
await db
|
|
||||||
.updateTable("ToneAPI_v3.match")
|
|
||||||
.set({ ongoing: false })
|
|
||||||
.where("ToneAPI_v3.match.server_name", "=", server_name)
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
const match = await db
|
|
||||||
.insertInto("ToneAPI_v3.match")
|
|
||||||
.values({
|
|
||||||
air_accel,
|
|
||||||
server_name,
|
|
||||||
game_map,
|
|
||||||
gamemode,
|
|
||||||
host_id,
|
|
||||||
ongoing: true,
|
|
||||||
})
|
|
||||||
.returning("ToneAPI_v3.match.match_id")
|
|
||||||
.executeTakeFirstOrThrow();
|
|
||||||
|
|
||||||
res.status(201).send({ match: match?.match_id });
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default router;
|
|
||||||
|
|||||||
+45
-38
@@ -1,63 +1,70 @@
|
|||||||
/* eslint-disable @typescript-eslint/naming-convention */
|
import { Router } from 'express'
|
||||||
import { Router } from "express";
|
import { header } from 'express-validator'
|
||||||
import { header } from "express-validator";
|
|
||||||
|
|
||||||
import { /* createKillRecord, */ checkServerToken } from "../db";
|
import { /* createKillRecord, */ checkServerToken } from '../db'
|
||||||
import { validateErrors } from "../common";
|
import { validateErrors } from '../common'
|
||||||
|
|
||||||
import match from './match'
|
import match from './match'
|
||||||
import kill from './kill'
|
import kill from './kill'
|
||||||
|
import player from './player'
|
||||||
|
|
||||||
const router = Router();
|
const router = Router()
|
||||||
|
|
||||||
|
router.use('/*', (req, res, next) => {
|
||||||
|
try {
|
||||||
|
next()
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).send({ errors: [{ msg: 'Internal Error! You\'d better report this' }] })
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// auth middleware
|
// auth middleware
|
||||||
router.post(
|
router.post(
|
||||||
"/*",
|
'/*',
|
||||||
header("authorization")
|
header('authorization')
|
||||||
.exists({ checkFalsy: true })
|
.exists({ checkFalsy: true })
|
||||||
.withMessage("Missing Authorization Header")
|
.withMessage('Missing Authorization Header')
|
||||||
.bail()
|
.bail()
|
||||||
.custom((e) => e.split(" ")[0].toLowerCase() === "bearer")
|
.custom((e) => e.split(' ')[0].toLowerCase() === 'bearer')
|
||||||
.withMessage("Authorization Token is not Bearer"),
|
.withMessage('Authorization Token is not Bearer'),
|
||||||
validateErrors,
|
validateErrors,
|
||||||
(req, res, next) => {
|
(req, res, next) => {
|
||||||
void (async () => {
|
void (async () => {
|
||||||
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) {
|
||||||
console.error(
|
console.error(
|
||||||
`incorrect token : ${
|
`incorrect token : ${req.headers.authorization.split(' ')[1]
|
||||||
req.headers.authorization.split(" ")[1]
|
} with IP ${req.headers['x-forwarded-for']?.toString() ??
|
||||||
} with IP ${
|
req.socket.remoteAddress?.toString() ??
|
||||||
req.headers["x-forwarded-for"]?.toString() ??
|
''
|
||||||
req.socket.remoteAddress?.toString() ??
|
|
||||||
""
|
|
||||||
}`
|
}`
|
||||||
);
|
)
|
||||||
return res.status(401).send({
|
return res.status(401).send({
|
||||||
errors: [
|
errors: [
|
||||||
{
|
{
|
||||||
msg: "Incorrect Token",
|
msg: 'Incorrect Token',
|
||||||
param: "authorization",
|
param: 'authorization',
|
||||||
location: "headers",
|
location: 'headers'
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
res.locals.host_id = query.host_id;
|
res.locals.host_id = query.host_id
|
||||||
next();
|
next()
|
||||||
})();
|
})()
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
|
|
||||||
// Route to check auth
|
// Route to check auth
|
||||||
router.post("/", (req, res) => {
|
router.post('/', (req, res) => {
|
||||||
res.sendStatus(200);
|
res.sendStatus(200)
|
||||||
});
|
})
|
||||||
|
|
||||||
// const serversCount: Record<string, number> = {}
|
// const serversCount: Record<string, number> = {}
|
||||||
// const serversTimeout: Record<string, NodeJS.Timeout> = {}
|
// const serversTimeout: Record<string, NodeJS.Timeout> = {}
|
||||||
@@ -79,7 +86,7 @@ router.post("/", (req, res) => {
|
|||||||
next()
|
next()
|
||||||
}) */
|
}) */
|
||||||
|
|
||||||
|
router.use('/kill', kill)
|
||||||
router.use("/kill", kill)
|
router.use('/match', match)
|
||||||
router.use("/match", match)
|
router.use('/player', player)
|
||||||
export default router;
|
export default router
|
||||||
|
|||||||
@@ -44,6 +44,32 @@ export interface KillData {
|
|||||||
cause_of_death: string
|
cause_of_death: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type MatchCloseData = Record<string, MatchClosePlayerData>
|
||||||
|
export interface MatchClosePlayerData {
|
||||||
|
weapons: Record<string, MatchCloseWeaponData>
|
||||||
|
titans: Record<string, MatchCloseWeaponData>
|
||||||
|
stats: {
|
||||||
|
distance: {
|
||||||
|
ground: number
|
||||||
|
wall: number
|
||||||
|
air: number
|
||||||
|
}
|
||||||
|
time: {
|
||||||
|
ground: number
|
||||||
|
wall: number
|
||||||
|
air: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface MatchCloseWeaponData {
|
||||||
|
shotsFired: number
|
||||||
|
shotsHit: number
|
||||||
|
shotsCrit: number
|
||||||
|
shotsHeadshot: number
|
||||||
|
shotsRichochet: number
|
||||||
|
playtime: number
|
||||||
|
}
|
||||||
|
|
||||||
export type RequestBody<T> = Request<any, any, T>
|
export type RequestBody<T> = Request<any, any, T>
|
||||||
|
|
||||||
export const validateBody =
|
export const validateBody =
|
||||||
@@ -52,6 +78,7 @@ export const validateBody =
|
|||||||
const result: typia.IValidation<T> = checker(req.body)
|
const result: typia.IValidation<T> = checker(req.body)
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
res.status(400).send({ errors: result.errors })
|
res.status(400).send({ errors: result.errors })
|
||||||
|
console.log(req.body)
|
||||||
console.error(result.errors)
|
console.error(result.errors)
|
||||||
} else {
|
} else {
|
||||||
next()
|
next()
|
||||||
|
|||||||
Reference in New Issue
Block a user