+1
-4
@@ -2,7 +2,4 @@ POSTGRES_HOST="localhost"
|
|||||||
POSTGRES_DATABASE="postgres"
|
POSTGRES_DATABASE="postgres"
|
||||||
POSTGRES_USER="postgres"
|
POSTGRES_USER="postgres"
|
||||||
POSTGRES_PASSWORD="postgres"
|
POSTGRES_PASSWORD="postgres"
|
||||||
REDIS_USER="default"
|
SERVERAUTH_TOKEN="1:f9f8fc8c-9185-47a8-8a99-9e3acf6ca007"
|
||||||
REDIS_PASSWORD=""
|
|
||||||
REDIS_URL="redis://awesome.redis.server:6379"
|
|
||||||
SERVERAUTH_TEST="1:f9f8fc8c-9185-47a8-8a99-9e3acf6ca007"
|
|
||||||
+63
-29
File diff suppressed because one or more lines are too long
+453
@@ -0,0 +1,453 @@
|
|||||||
|
openapi: 3.0.2
|
||||||
|
info:
|
||||||
|
title: ToneAPI
|
||||||
|
version: '2.0'
|
||||||
|
contact:
|
||||||
|
name: Legonzaur
|
||||||
|
url: 'https://github.com/Legonzaur'
|
||||||
|
license:
|
||||||
|
name: The Unlicense
|
||||||
|
url: 'https://unlicense.org/'
|
||||||
|
description: |-
|
||||||
|
Stats tracking API for Titanfall 2 Northstar
|
||||||
|
|
||||||
|
All query params can be negated using `!`. Example : (`https://tone.sleepycat.date/v2/client/gamemodes?gamemode=!sns`)
|
||||||
|
tags:
|
||||||
|
- name: client
|
||||||
|
description: Routes accessibles by everyone
|
||||||
|
- name: server
|
||||||
|
description: Routes accessibles only to server owners
|
||||||
|
servers:
|
||||||
|
- url: 'https://tone.sleepycat.date/v2'
|
||||||
|
paths:
|
||||||
|
/client/hosts:
|
||||||
|
get:
|
||||||
|
summary: Your GET endpoint
|
||||||
|
tags:
|
||||||
|
- client
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
'[number] host_id':
|
||||||
|
type: string
|
||||||
|
example: Fvnkhead
|
||||||
|
examples:
|
||||||
|
Example 1:
|
||||||
|
value:
|
||||||
|
'1': Fvnkhead
|
||||||
|
'2': Legonzaur
|
||||||
|
operationId: get-client-hosts
|
||||||
|
description: An object with host ID as keys and host name as values
|
||||||
|
/client/servers:
|
||||||
|
get:
|
||||||
|
summary: List of servers
|
||||||
|
tags:
|
||||||
|
- client
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
'[string] server_name':
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
deaths:
|
||||||
|
type: integer
|
||||||
|
kills:
|
||||||
|
type: integer
|
||||||
|
max_distance:
|
||||||
|
type: integer
|
||||||
|
total_distance:
|
||||||
|
type: integer
|
||||||
|
host:
|
||||||
|
type: integer
|
||||||
|
examples:
|
||||||
|
Example 1:
|
||||||
|
value:
|
||||||
|
fvnkhead's 3v3:
|
||||||
|
deaths: 79810
|
||||||
|
kills: 76218
|
||||||
|
max_distance: 3733
|
||||||
|
total_distance: 42200935
|
||||||
|
host: 1
|
||||||
|
fvnkhead's 7v7:
|
||||||
|
deaths: 148609
|
||||||
|
kills: 163272
|
||||||
|
max_distance: 6578
|
||||||
|
total_distance: 113915706
|
||||||
|
host: 1
|
||||||
|
operationId: get-server-list
|
||||||
|
description: A JSON object with server names as keys and server data as values
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/server'
|
||||||
|
- $ref: '#/components/parameters/player'
|
||||||
|
- $ref: '#/components/parameters/weapon'
|
||||||
|
- $ref: '#/components/parameters/map'
|
||||||
|
- $ref: '#/components/parameters/gamemode'
|
||||||
|
- $ref: '#/components/parameters/host'
|
||||||
|
parameters: []
|
||||||
|
/client/weapons:
|
||||||
|
get:
|
||||||
|
summary: Statistics for all weapons
|
||||||
|
tags:
|
||||||
|
- client
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
'[string] weapon_id':
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
deaths:
|
||||||
|
type: integer
|
||||||
|
description: Number of deaths caused by this weapon
|
||||||
|
kills:
|
||||||
|
type: integer
|
||||||
|
max_distance:
|
||||||
|
type: integer
|
||||||
|
total_distance:
|
||||||
|
type: integer
|
||||||
|
deaths_while_equipped:
|
||||||
|
type: integer
|
||||||
|
description: Number of deaths while this weapon is equipped
|
||||||
|
examples:
|
||||||
|
Example 1:
|
||||||
|
value:
|
||||||
|
autopistol:
|
||||||
|
deaths: 27
|
||||||
|
kills: 28
|
||||||
|
max_distance: 2173
|
||||||
|
total_distance: 12792
|
||||||
|
car:
|
||||||
|
deaths: 1394
|
||||||
|
kills: 1534
|
||||||
|
max_distance: 3217
|
||||||
|
total_distance: 905505
|
||||||
|
operationId: get-client-weapons
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/server'
|
||||||
|
- $ref: '#/components/parameters/player'
|
||||||
|
- $ref: '#/components/parameters/weapon'
|
||||||
|
- $ref: '#/components/parameters/map'
|
||||||
|
- $ref: '#/components/parameters/gamemode'
|
||||||
|
- $ref: '#/components/parameters/host'
|
||||||
|
requestBody:
|
||||||
|
content: {}
|
||||||
|
description: A JSON Object with weapon IDS as keys and weapon data as value
|
||||||
|
description: A JSON Object with weapon IDs as keys and weapon data as values
|
||||||
|
parameters: []
|
||||||
|
/client/players:
|
||||||
|
get:
|
||||||
|
summary: Statistics for all players
|
||||||
|
tags:
|
||||||
|
- client
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
'[number] player_id':
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
deaths:
|
||||||
|
type: integer
|
||||||
|
kills:
|
||||||
|
type: integer
|
||||||
|
max_distance:
|
||||||
|
type: integer
|
||||||
|
total_distance:
|
||||||
|
type: integer
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
examples:
|
||||||
|
Example 1:
|
||||||
|
value:
|
||||||
|
'2250125460':
|
||||||
|
username: Legonzaur
|
||||||
|
deaths: 0
|
||||||
|
kills: 0
|
||||||
|
max_kill_distance: 0
|
||||||
|
avg_kill_distance: 0
|
||||||
|
operationId: get-client-players
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/weapon'
|
||||||
|
- $ref: '#/components/parameters/server'
|
||||||
|
- $ref: '#/components/parameters/player'
|
||||||
|
- $ref: '#/components/parameters/gamemode'
|
||||||
|
- $ref: '#/components/parameters/map'
|
||||||
|
- $ref: '#/components/parameters/host'
|
||||||
|
description: A JSON Object with player IDs as keys and player data as values
|
||||||
|
/client/maps:
|
||||||
|
get:
|
||||||
|
summary: Your GET endpoint
|
||||||
|
tags:
|
||||||
|
- client
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
'[string] map_id':
|
||||||
|
$ref: '#/components/schemas/KillData'
|
||||||
|
examples:
|
||||||
|
Example 1:
|
||||||
|
value:
|
||||||
|
lf_meadow:
|
||||||
|
deaths: 13628
|
||||||
|
kills: 11791
|
||||||
|
max_distance: 3970
|
||||||
|
total_distance: 7139763
|
||||||
|
lf_township:
|
||||||
|
deaths: 14861
|
||||||
|
kills: 14232
|
||||||
|
max_distance: 3642
|
||||||
|
total_distance: 9022513
|
||||||
|
operationId: get-client-maps
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/host'
|
||||||
|
- $ref: '#/components/parameters/gamemode'
|
||||||
|
- $ref: '#/components/parameters/map'
|
||||||
|
- $ref: '#/components/parameters/weapon'
|
||||||
|
- $ref: '#/components/parameters/player'
|
||||||
|
- $ref: '#/components/parameters/server'
|
||||||
|
description: A JSON Object with map IDs as keys and map data as values
|
||||||
|
/client/gamemodes:
|
||||||
|
get:
|
||||||
|
summary: Your GET endpoint
|
||||||
|
tags:
|
||||||
|
- client
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
'[string] gamemode_id':
|
||||||
|
$ref: '#/components/schemas/KillData'
|
||||||
|
examples:
|
||||||
|
Example 1:
|
||||||
|
value:
|
||||||
|
ps:
|
||||||
|
deaths: 273232
|
||||||
|
kills: 286679
|
||||||
|
max_distance: 8521
|
||||||
|
total_distance: 184069789
|
||||||
|
aitdm:
|
||||||
|
deaths: 11388
|
||||||
|
kills: 12814
|
||||||
|
max_distance: 5689
|
||||||
|
total_distance: 9184028
|
||||||
|
operationId: get-client-gamemodes
|
||||||
|
description: A JSON Object with gamemodes IDs as keys and gamemodes data as values
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/host'
|
||||||
|
- $ref: '#/components/parameters/gamemode'
|
||||||
|
- $ref: '#/components/parameters/map'
|
||||||
|
- $ref: '#/components/parameters/weapon'
|
||||||
|
- $ref: '#/components/parameters/player'
|
||||||
|
- $ref: '#/components/parameters/server'
|
||||||
|
/servers:
|
||||||
|
parameters: []
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- server
|
||||||
|
summary: ''
|
||||||
|
operationId: post-servers-serverId
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: OK
|
||||||
|
'403':
|
||||||
|
description: Forbidden
|
||||||
|
description: Used to test auth
|
||||||
|
security:
|
||||||
|
- Auth_Token: []
|
||||||
|
/servers/kills:
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- server
|
||||||
|
summary: Post kills
|
||||||
|
operationId: post-kills
|
||||||
|
responses:
|
||||||
|
'201':
|
||||||
|
description: Created
|
||||||
|
'400':
|
||||||
|
description: Bad Request
|
||||||
|
'401':
|
||||||
|
description: Unauthorized
|
||||||
|
parameters: []
|
||||||
|
security:
|
||||||
|
- Auth_Token: []
|
||||||
|
description: Allows a Northstar server to record kills on the database
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Kill'
|
||||||
|
description: ''
|
||||||
|
parameters: []
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
KillData:
|
||||||
|
title: Killdata
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
deaths:
|
||||||
|
type: integer
|
||||||
|
kills:
|
||||||
|
type: integer
|
||||||
|
max_kill_distance:
|
||||||
|
type: integer
|
||||||
|
avg_kill_distance:
|
||||||
|
type: number
|
||||||
|
Kill:
|
||||||
|
title: Kill
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
readOnly: true
|
||||||
|
server:
|
||||||
|
type: string
|
||||||
|
readOnly: true
|
||||||
|
tone_version:
|
||||||
|
type: string
|
||||||
|
match_id:
|
||||||
|
type: string
|
||||||
|
game_mode:
|
||||||
|
type: string
|
||||||
|
map:
|
||||||
|
type: string
|
||||||
|
game_time:
|
||||||
|
type: string
|
||||||
|
format: time
|
||||||
|
player_count:
|
||||||
|
type: integer
|
||||||
|
attacker_name:
|
||||||
|
type: string
|
||||||
|
attacker_id:
|
||||||
|
type: integer
|
||||||
|
attacker_current_weapon:
|
||||||
|
type: string
|
||||||
|
attacker_current_weapon_mods:
|
||||||
|
type: integer
|
||||||
|
attacker_weapon_1:
|
||||||
|
type: string
|
||||||
|
attacker_weapon_1_mods:
|
||||||
|
type: integer
|
||||||
|
attacker_weapon_2:
|
||||||
|
type: string
|
||||||
|
attacker_weapon_2_mods:
|
||||||
|
type: integer
|
||||||
|
attacker_weapon_3:
|
||||||
|
type: string
|
||||||
|
attacker_weapon_3_mods:
|
||||||
|
type: integer
|
||||||
|
attacker_offhand_weapon_1:
|
||||||
|
type: integer
|
||||||
|
attacker_offhand_weapon_2:
|
||||||
|
type: integer
|
||||||
|
victim_name:
|
||||||
|
type: string
|
||||||
|
victim_id:
|
||||||
|
type: integer
|
||||||
|
victim_current_weapon:
|
||||||
|
type: string
|
||||||
|
victim_current_weapon_mods:
|
||||||
|
type: integer
|
||||||
|
victim_weapon_1:
|
||||||
|
type: string
|
||||||
|
victim_weapon_1_mods:
|
||||||
|
type: integer
|
||||||
|
victim_weapon_2:
|
||||||
|
type: string
|
||||||
|
victim_weapon_2_mods:
|
||||||
|
type: integer
|
||||||
|
victim_weapon_3:
|
||||||
|
type: string
|
||||||
|
victim_weapon_3_mods:
|
||||||
|
type: integer
|
||||||
|
victim_offhand_weapon_1:
|
||||||
|
type: integer
|
||||||
|
victim_offhand_weapon_2:
|
||||||
|
type: integer
|
||||||
|
cause_of_death:
|
||||||
|
type: string
|
||||||
|
distance:
|
||||||
|
type: number
|
||||||
|
required:
|
||||||
|
- cause_of_death
|
||||||
|
securitySchemes:
|
||||||
|
Auth_Token:
|
||||||
|
type: http
|
||||||
|
scheme: bearer
|
||||||
|
parameters:
|
||||||
|
server:
|
||||||
|
name: server
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: fvnkhead's 3v3
|
||||||
|
description: Fetch data for specific server
|
||||||
|
player:
|
||||||
|
name: player
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
example: 1005930844007
|
||||||
|
description: Fetch data for specific player
|
||||||
|
weapon:
|
||||||
|
name: weapon
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: epg
|
||||||
|
description: Fetch data for specific weapon
|
||||||
|
map:
|
||||||
|
name: map
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: lf_stacks
|
||||||
|
description: Fetch data for specific map
|
||||||
|
gamemode:
|
||||||
|
name: gamemode
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: aitdm
|
||||||
|
description: Fetch data for specific gamemode
|
||||||
|
host:
|
||||||
|
name: host
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
example: 1
|
||||||
|
description: Fetch data for specific host
|
||||||
|
responses: {}
|
||||||
|
examples: {}
|
||||||
+1
-1
@@ -33,7 +33,7 @@
|
|||||||
"build": "npx tsc",
|
"build": "npx tsc",
|
||||||
"startServer": "node out/serverMain.js",
|
"startServer": "node out/serverMain.js",
|
||||||
"startClient": "node out/clientMain.js",
|
"startClient": "node out/clientMain.js",
|
||||||
"documentation": "redocly build-docs .\\docs\\v1.yml --output=docs\\index.html",
|
"documentation": "redocly build-docs .\\docs\\v2.yml --output=docs\\index.html",
|
||||||
"test": "jest --runInBand"
|
"test": "jest --runInBand"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+76
-24
@@ -10,22 +10,30 @@ router.get('/*', (req, res, next) => {
|
|||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|
||||||
function filters(e: any, query: any) {
|
function computeQueryParam(
|
||||||
|
queryParam: string,
|
||||||
|
comparison: string | number
|
||||||
|
): boolean {
|
||||||
|
return queryParam.startsWith('!')
|
||||||
|
? queryParam.substring(1) != comparison
|
||||||
|
: queryParam == comparison
|
||||||
|
}
|
||||||
|
|
||||||
|
function filters(e: typeof allData[0], query: any) {
|
||||||
return (
|
return (
|
||||||
(query.player ? query.player == e.attacker_id : true) &&
|
(query.player ? computeQueryParam(query.player, e.attacker_id) : true) &&
|
||||||
(query.server ? query.server == e.servername : true) &&
|
(query.server ? computeQueryParam(query.server, e.servername) : true) &&
|
||||||
(query.host ? Number(query.host) == e.host : true) &&
|
(query.host ? computeQueryParam(query.host, e.host) : true) &&
|
||||||
(query.map ? query.map == e.map : true) &&
|
(query.map ? computeQueryParam(query.map, e.map) : true) &&
|
||||||
(query.weapon ? query.weapon == e.cause_of_death : true) &&
|
(query.weapon ? computeQueryParam(query.weapon, e.cause_of_death) : true) &&
|
||||||
(query.gamemode ? query.gamemode == e.game_mode : true)
|
(query.gamemode ? computeQueryParam(query.gamemode, e.game_mode) : true)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
router.get('/hosts',
|
router.get('/hosts', async (req, res) => {
|
||||||
async (req, res) => {
|
const result = await getHostList()
|
||||||
const result = (await getHostList())
|
|
||||||
const data: { [key: number]: string } = {}
|
const data: { [key: number]: string } = {}
|
||||||
result.forEach(e => data[Number(e.id)] = e.name)
|
result.forEach((e) => (data[Number(e.id)] = e.name))
|
||||||
res.status(200).send(data)
|
res.status(200).send(data)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -33,11 +41,17 @@ router.get(
|
|||||||
'/:dataType',
|
'/:dataType',
|
||||||
param('dataType')
|
param('dataType')
|
||||||
.custom(
|
.custom(
|
||||||
(e) => e == 'weapons' || e == 'players' || e == 'maps' || e == 'servers'
|
(e) =>
|
||||||
|
e == 'weapons' ||
|
||||||
|
e == 'players' ||
|
||||||
|
e == 'maps' ||
|
||||||
|
e == 'servers' ||
|
||||||
|
e == 'gamemodes'
|
||||||
)
|
)
|
||||||
.withMessage('Only weapons, players, maps or servers are valid paths'),
|
.withMessage('Only weapons, players, maps or servers are valid paths'),
|
||||||
query(['player', 'host']).optional().toInt().isInt(),
|
query(['server', 'map', 'weapon', 'gamemode', 'player', 'host'])
|
||||||
query(['server', 'map', 'weapon', 'gamemode']).optional().isString(),
|
.optional()
|
||||||
|
.isString(),
|
||||||
validateErrors,
|
validateErrors,
|
||||||
(req, res) => {
|
(req, res) => {
|
||||||
const data: {
|
const data: {
|
||||||
@@ -48,9 +62,15 @@ router.get(
|
|||||||
total_distance: number
|
total_distance: number
|
||||||
username?: string
|
username?: string
|
||||||
host?: number
|
host?: number
|
||||||
|
deaths_while_equipped?: number
|
||||||
}
|
}
|
||||||
} = {}
|
} = {}
|
||||||
let index: 'cause_of_death' | 'attacker_id' | 'map' | 'servername'
|
let index:
|
||||||
|
| 'cause_of_death'
|
||||||
|
| 'attacker_id'
|
||||||
|
| 'map'
|
||||||
|
| 'servername'
|
||||||
|
| 'game_mode'
|
||||||
switch (req.params.dataType) {
|
switch (req.params.dataType) {
|
||||||
case 'weapons':
|
case 'weapons':
|
||||||
index = 'cause_of_death'
|
index = 'cause_of_death'
|
||||||
@@ -64,29 +84,61 @@ router.get(
|
|||||||
case 'servers':
|
case 'servers':
|
||||||
index = 'servername'
|
index = 'servername'
|
||||||
break
|
break
|
||||||
|
case 'gamemodes':
|
||||||
|
index = 'game_mode'
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
return res.status(400).send()
|
return res.status(400).send()
|
||||||
}
|
}
|
||||||
|
const timeStart = new Date()
|
||||||
allData
|
allData
|
||||||
.filter((e) => filters(e, req.query))
|
.filter((e) => filters(e, req.query))
|
||||||
.forEach((e) => {
|
.forEach((e) => {
|
||||||
if (!data[e[index]])
|
if (
|
||||||
data[e[index]] = {
|
!e.cause_of_death ||
|
||||||
|
!e.attacker_id ||
|
||||||
|
!e.map ||
|
||||||
|
!e.servername ||
|
||||||
|
!e.game_mode
|
||||||
|
)
|
||||||
|
return
|
||||||
|
const requestIndex = e[index]
|
||||||
|
if (!requestIndex) return
|
||||||
|
if (!data[requestIndex])
|
||||||
|
data[requestIndex] = {
|
||||||
deaths: 0,
|
deaths: 0,
|
||||||
kills: 0,
|
kills: 0,
|
||||||
max_distance: 0,
|
max_distance: 0,
|
||||||
total_distance: 0
|
total_distance: 0
|
||||||
}
|
}
|
||||||
if (index === 'attacker_id') data[e[index]].username = e.attacker_name
|
if (index === 'attacker_id')
|
||||||
if (index === 'servername') data[e[index]].host = e.host
|
data[requestIndex].username = e.attacker_name
|
||||||
data[e[index]].deaths += Number(e.deaths)
|
if (index === 'servername') data[requestIndex].host = e.host
|
||||||
data[e[index]].kills += Number(e.kills)
|
if (
|
||||||
data[e[index]].total_distance += Number(e.total_distance)
|
index === 'cause_of_death' ||
|
||||||
data[e[index]].max_distance = Math.max(
|
(req.query.weapon &&
|
||||||
data[e[index]].max_distance,
|
!req.query.weapon.toString().startsWith('!') &&
|
||||||
|
index === 'attacker_id')
|
||||||
|
)
|
||||||
|
data[requestIndex].deaths_while_equipped =
|
||||||
|
Number(e.deaths_with_weapon) +
|
||||||
|
(data[requestIndex].deaths_while_equipped || 0)
|
||||||
|
data[requestIndex].deaths += Number(e.deaths)
|
||||||
|
data[requestIndex].kills += Number(e.kills)
|
||||||
|
data[requestIndex].total_distance += Number(e.total_distance)
|
||||||
|
data[requestIndex].max_distance = Math.max(
|
||||||
|
data[requestIndex].max_distance,
|
||||||
Number(e.max_distance)
|
Number(e.max_distance)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
console.log(
|
||||||
|
new Date().toLocaleString() + ',' +
|
||||||
|
(req.headers['x-forwarded-for']?.toString() ||
|
||||||
|
req.socket.remoteAddress?.toString() ||
|
||||||
|
'') +
|
||||||
|
',' +
|
||||||
|
Math.abs(new Date().getTime() - timeStart.getTime()) / 1000 + ',' + req.originalUrl
|
||||||
|
)
|
||||||
res.status(200).send(data)
|
res.status(200).send(data)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import cors from 'cors'
|
|||||||
import client from './client/client'
|
import client from './client/client'
|
||||||
import { dbReady } from './db/db'
|
import { dbReady } from './db/db'
|
||||||
import processAll from './process/process'
|
import processAll from './process/process'
|
||||||
|
import listenKills from './process/onKill'
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
const port = 3000
|
const port = 3000
|
||||||
@@ -22,6 +23,7 @@ export default
|
|||||||
new Promise(async (resolve, reject) => {
|
new Promise(async (resolve, reject) => {
|
||||||
await dbReady()
|
await dbReady()
|
||||||
await processAll()
|
await processAll()
|
||||||
|
await listenKills()
|
||||||
const listenServer = app.listen(port, '0.0.0.0', () => {
|
const listenServer = app.listen(port, '0.0.0.0', () => {
|
||||||
console.log(`Tone client api listening on port ${port}`)
|
console.log(`Tone client api listening on port ${port}`)
|
||||||
resolve(listenServer)
|
resolve(listenServer)
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { Kysely, sql } from 'kysely'
|
||||||
|
import { Client } from 'pg'
|
||||||
|
|
||||||
|
const pgClient = new Client({
|
||||||
|
host: process.env.POSTGRES_HOST,
|
||||||
|
database: process.env.POSTGRES_DATABASE,
|
||||||
|
user: process.env.POSTGRES_USER,
|
||||||
|
password: process.env.POSTGRES_PASSWORD
|
||||||
|
})
|
||||||
|
|
||||||
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
|
await pgClient.connect()
|
||||||
|
await pgClient.query(`ALTER TABLE kill ADD COLUMN IF NOT EXISTS attacker_titan character varying NULL;`)
|
||||||
|
await pgClient.query(`UPDATE kill SET attacker_titan = 'scorch' WHERE (cause_of_death = 'mp_titancore_flame_wave_secondary' OR cause_of_death = 'mp_titancore_flame_wave' OR cause_of_death = 'mp_titancore_flame_wall' OR cause_of_death = 'mp_titanweapon_meteor_thermite' OR cause_of_death = 'mp_titanweapon_heat_shield' OR cause_of_death = 'titan_punch_scorch' OR cause_of_death = 'mp_titanweapon_meteor')`)
|
||||||
|
await pgClient.query(`UPDATE kill SET attacker_titan = 'ronin' WHERE (cause_of_death = 'mp_titanweapon_arc_wave' OR cause_of_death = 'mp_titancore_shift_core' OR cause_of_death = 'titan_sword' OR cause_of_death = 'mp_titanweapon_leadwall')`)
|
||||||
|
await pgClient.query(`UPDATE kill SET attacker_titan = 'ion' WHERE (cause_of_death = 'titan_punch_ion' OR cause_of_death = 'mp_titanweapon_vortex_shield_ion' OR cause_of_death = 'mp_titancore_laser_cannon' OR cause_of_death = 'mp_titanability_laser_trip' OR cause_of_death = 'mp_titanweapon_laser_lite')`)
|
||||||
|
await pgClient.query(`UPDATE kill SET attacker_titan = 'tone' WHERE (cause_of_death = 'mp_titancore_salvo_core' OR cause_of_death = 'titan_punch_tone' OR cause_of_death = 'mp_titanweapon_salvo_rockets' OR cause_of_death = 'mp_titanweapon_sticky_40mm')`)
|
||||||
|
await pgClient.query(`UPDATE kill SET attacker_titan = 'northstar' WHERE (cause_of_death = 'mp_titanability_slow_trap' OR cause_of_death = 'mp_titanweapon_flightcore_rockets' OR cause_of_death = 'titan_punch_northstar' OR cause_of_death = 'mp_titanweapon_sniper')`)
|
||||||
|
await pgClient.query(`UPDATE kill SET attacker_titan = 'legion' WHERE (cause_of_death = 'titan_punch_legion' OR cause_of_death = 'mp_titanweapon_predator_cannon')`)
|
||||||
|
await pgClient.query(`UPDATE kill SET attacker_titan = 'vanguard' WHERE (cause_of_death = 'mp_titanweapon_dumbfire_rockets' OR cause_of_death = 'titan_punch_vanguard' OR cause_of_death = 'mp_titanweapon_tracker_rockets' OR cause_of_death = 'mp_titanweapon_particle_accelerator' OR cause_of_death = 'mp_titanweapon_xo16_vanguard')`)
|
||||||
|
await pgClient.query(`ALTER TABLE kill ADD COLUMN IF NOT EXISTS victim_titan character varying NULL;`)
|
||||||
|
//Update the hosts column once the hoster table is manually updated
|
||||||
|
await pgClient.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(db: Kysely<any>): Promise<void> {
|
||||||
|
await pgClient.connect()
|
||||||
|
pgClient.query('ALTER TABLE kill DROP COLUMN IF EXISTS attacker_titan')
|
||||||
|
pgClient.query('ALTER TABLE kill DROP COLUMN IF EXISTS victim_titan')
|
||||||
|
await pgClient.end()
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import { Kysely, sql } from 'kysely'
|
||||||
|
import { Client } from 'pg'
|
||||||
|
|
||||||
|
const pgClient = new Client({
|
||||||
|
host: process.env.POSTGRES_HOST,
|
||||||
|
database: process.env.POSTGRES_DATABASE,
|
||||||
|
user: process.env.POSTGRES_USER,
|
||||||
|
password: process.env.POSTGRES_PASSWORD
|
||||||
|
})
|
||||||
|
|
||||||
|
const weapons = [
|
||||||
|
'car',
|
||||||
|
'alternator_smg',
|
||||||
|
'hemlok_smg',
|
||||||
|
'r97',
|
||||||
|
'hemlok',
|
||||||
|
'vinson',
|
||||||
|
'g2',
|
||||||
|
'rspn101',
|
||||||
|
'rspn101_og',
|
||||||
|
'esaw',
|
||||||
|
'lstar',
|
||||||
|
'lmg',
|
||||||
|
'shotgun',
|
||||||
|
'mastiff',
|
||||||
|
'dmr',
|
||||||
|
'sniper',
|
||||||
|
'doubletake',
|
||||||
|
'pulse_lmg',
|
||||||
|
'smr',
|
||||||
|
'softball',
|
||||||
|
'epg',
|
||||||
|
'shotgun_pistol',
|
||||||
|
'wingman_n',
|
||||||
|
'autopistol',
|
||||||
|
'semipistol',
|
||||||
|
'wingman',
|
||||||
|
'mgl',
|
||||||
|
'arc_launcher',
|
||||||
|
'rocket_launcher',
|
||||||
|
'defender', 'grenade_sonar', 'thermite_grenade', 'grenade_emp', 'smart_pistol', 'satchel', 'grenade_electric_smoke', 'frag_grenade', 'grenade_gravity', 'turretplasma',
|
||||||
|
]
|
||||||
|
|
||||||
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
|
await pgClient.connect()
|
||||||
|
await Promise.all(weapons.map(async (e) => {
|
||||||
|
console.log('update ' + e)
|
||||||
|
await pgClient.query(
|
||||||
|
`UPDATE KILL SET cause_of_death = 'mp_weapon_${e}' WHERE cause_of_death = '${e}';`
|
||||||
|
)
|
||||||
|
await pgClient.query(
|
||||||
|
`UPDATE KILL SET attacker_current_weapon = 'mp_weapon_${e}' WHERE attacker_current_weapon = '${e}';`
|
||||||
|
)
|
||||||
|
await pgClient.query(
|
||||||
|
`UPDATE KILL SET attacker_weapon_1 = 'mp_weapon_${e}' WHERE attacker_weapon_1 = '${e}';`
|
||||||
|
)
|
||||||
|
await pgClient.query(
|
||||||
|
`UPDATE KILL SET attacker_weapon_2 = 'mp_weapon_${e}' WHERE attacker_weapon_2 = '${e}';`
|
||||||
|
)
|
||||||
|
await pgClient.query(
|
||||||
|
`UPDATE KILL SET attacker_weapon_3 = 'mp_weapon_${e}' WHERE attacker_weapon_3 = '${e}';`
|
||||||
|
)
|
||||||
|
await pgClient.query(
|
||||||
|
`UPDATE KILL SET victim_current_weapon = 'mp_weapon_${e}' WHERE victim_current_weapon = '${e}';`
|
||||||
|
)
|
||||||
|
await pgClient.query(
|
||||||
|
`UPDATE KILL SET victim_weapon_1 = 'mp_weapon_${e}' WHERE victim_weapon_1 = '${e}';`
|
||||||
|
)
|
||||||
|
await pgClient.query(
|
||||||
|
`UPDATE KILL SET victim_weapon_2 = 'mp_weapon_${e}' WHERE victim_weapon_2 = '${e}';`
|
||||||
|
)
|
||||||
|
await pgClient.query(
|
||||||
|
`UPDATE KILL SET victim_weapon_3 = 'mp_weapon_${e}' WHERE victim_weapon_3 = '${e}';`
|
||||||
|
)
|
||||||
|
console.log('weapon ' + e + ' updated')
|
||||||
|
}))
|
||||||
|
|
||||||
|
console.log('end')
|
||||||
|
|
||||||
|
//Update the hosts column once the hoster table is manually updated
|
||||||
|
await pgClient.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(db: Kysely<any>): Promise<void> { }
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { Kysely, sql } from 'kysely'
|
||||||
|
import { Client } from 'pg'
|
||||||
|
|
||||||
|
const pgClient = new Client({
|
||||||
|
host: process.env.POSTGRES_HOST,
|
||||||
|
database: process.env.POSTGRES_DATABASE,
|
||||||
|
user: process.env.POSTGRES_USER,
|
||||||
|
password: process.env.POSTGRES_PASSWORD
|
||||||
|
})
|
||||||
|
|
||||||
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
|
await pgClient.connect()
|
||||||
|
await pgClient.query(`ALTER TABLE kill RENAME attacker_offhand_weapon_2 TO attacker_offhand_weapon_2_mods;`)
|
||||||
|
await pgClient.query(`ALTER TABLE kill RENAME attacker_offhand_weapon_1 TO attacker_offhand_weapon_1_mods;`)
|
||||||
|
await pgClient.query(`ALTER TABLE kill RENAME victim_offhand_weapon_2 TO victim_offhand_weapon_2_mods;`)
|
||||||
|
await pgClient.query(`ALTER TABLE kill RENAME victim_offhand_weapon_1 TO victim_offhand_weapon_1_mods;`)
|
||||||
|
|
||||||
|
await pgClient.query(`ALTER TABLE kill ADD COLUMN IF NOT EXISTS victim_offhand_weapon_1 character varying NULL;`)
|
||||||
|
await pgClient.query(`ALTER TABLE kill ADD COLUMN IF NOT EXISTS victim_offhand_weapon_2 character varying NULL;`)
|
||||||
|
await pgClient.query(`ALTER TABLE kill ADD COLUMN IF NOT EXISTS attacker_offhand_weapon_1 character varying NULL;`)
|
||||||
|
await pgClient.query(`ALTER TABLE kill ADD COLUMN IF NOT EXISTS attacker_offhand_weapon_2 character varying NULL;`)
|
||||||
|
//Update the hosts column once the hoster table is manually updated
|
||||||
|
await pgClient.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(db: Kysely<any>): Promise<void> {
|
||||||
|
await pgClient.connect()
|
||||||
|
|
||||||
|
await pgClient.query(`ALTER TABLE kill DROP COLUMN IF EXISTS victim_offhand_weapon_1;`)
|
||||||
|
await pgClient.query(`ALTER TABLE kill DROP COLUMN IF EXISTS victim_offhand_weapon_2;`)
|
||||||
|
await pgClient.query(`ALTER TABLE kill DROP COLUMN IF EXISTS attacker_offhand_weapon_1;`)
|
||||||
|
await pgClient.query(`ALTER TABLE kill DROP COLUMN IF EXISTS attacker_offhand_weapon_2;`)
|
||||||
|
|
||||||
|
await pgClient.query(`ALTER TABLE kill RENAME attacker_offhand_weapon_2_mods TO victim_offhand_weapon_2;`)
|
||||||
|
await pgClient.query(`ALTER TABLE kill RENAME attacker_offhand_weapon_1_mods TO victim_offhand_weapon_1;`)
|
||||||
|
await pgClient.query(`ALTER TABLE kill RENAME victim_offhand_weapon_2_mods TO victim_offhand_weapon_2;`)
|
||||||
|
await pgClient.query(`ALTER TABLE kill RENAME victim_offhand_weapon_1_mods TO victim_offhand_weapon_1;`)
|
||||||
|
|
||||||
|
await pgClient.end()
|
||||||
|
}
|
||||||
+9
-4
@@ -21,8 +21,10 @@ export interface KillTable {
|
|||||||
attacker_weapon_2_mods: number
|
attacker_weapon_2_mods: number
|
||||||
attacker_weapon_3: string
|
attacker_weapon_3: string
|
||||||
attacker_weapon_3_mods: number
|
attacker_weapon_3_mods: number
|
||||||
attacker_offhand_weapon_1: number
|
attacker_offhand_weapon_1: string
|
||||||
attacker_offhand_weapon_2: number
|
attacker_offhand_weapon_1_mods: number
|
||||||
|
attacker_offhand_weapon_2: string
|
||||||
|
attacker_offhand_weapon_2_mods: number
|
||||||
victim_name: string
|
victim_name: string
|
||||||
victim_id: string
|
victim_id: string
|
||||||
victim_current_weapon: string
|
victim_current_weapon: string
|
||||||
@@ -33,10 +35,13 @@ export interface KillTable {
|
|||||||
victim_weapon_2_mods: number
|
victim_weapon_2_mods: number
|
||||||
victim_weapon_3: string
|
victim_weapon_3: string
|
||||||
victim_weapon_3_mods: string
|
victim_weapon_3_mods: string
|
||||||
victim_offhand_weapon_1: number
|
victim_offhand_weapon_1: string
|
||||||
victim_offhand_weapon_2: number
|
victim_offhand_weapon_1_mods: number
|
||||||
|
victim_offhand_weapon_2: string
|
||||||
|
victim_offhand_weapon_2_mods: number
|
||||||
cause_of_death: string
|
cause_of_death: string
|
||||||
distance: number
|
distance: number
|
||||||
|
titan?: string
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
interface PlayerTable {
|
interface PlayerTable {
|
||||||
|
|||||||
+33
-3
@@ -1,7 +1,7 @@
|
|||||||
import { Client } from 'pg'
|
import { Client } from 'pg'
|
||||||
import { allData } from './process'
|
import { allData } from './process'
|
||||||
|
|
||||||
const pgClient = new Client({
|
export const pgClient = new Client({
|
||||||
host: process.env.POSTGRES_HOST,
|
host: process.env.POSTGRES_HOST,
|
||||||
database: process.env.POSTGRES_DATABASE,
|
database: process.env.POSTGRES_DATABASE,
|
||||||
user: process.env.POSTGRES_USER,
|
user: process.env.POSTGRES_USER,
|
||||||
@@ -33,12 +33,22 @@ export default async function listenKills() {
|
|||||||
e.map == payload.map &&
|
e.map == payload.map &&
|
||||||
e.servername == payload.servername
|
e.servername == payload.servername
|
||||||
)
|
)
|
||||||
|
let deathWithWeaponEntry = allData.find(
|
||||||
|
(e) =>
|
||||||
|
e.attacker_id == payload.victim_id &&
|
||||||
|
e.cause_of_death == payload.victim_current_weapon &&
|
||||||
|
e.game_mode == payload.game_mode &&
|
||||||
|
e.host == payload.host &&
|
||||||
|
e.map == payload.map &&
|
||||||
|
e.servername == payload.servername
|
||||||
|
)
|
||||||
if (!killEntry) {
|
if (!killEntry) {
|
||||||
killEntry = {
|
killEntry = {
|
||||||
kills: 0,
|
kills: 0,
|
||||||
deaths: 0,
|
deaths: 0,
|
||||||
max_distance: 0,
|
max_distance: 0,
|
||||||
total_distance: 0,
|
total_distance: 0,
|
||||||
|
deaths_with_weapon: 0,
|
||||||
attacker_name: payload.attacker_name,
|
attacker_name: payload.attacker_name,
|
||||||
attacker_id: payload.attacker_id,
|
attacker_id: payload.attacker_id,
|
||||||
cause_of_death: payload.cause_of_death,
|
cause_of_death: payload.cause_of_death,
|
||||||
@@ -56,9 +66,10 @@ export default async function listenKills() {
|
|||||||
deaths: 0,
|
deaths: 0,
|
||||||
max_distance: 0,
|
max_distance: 0,
|
||||||
total_distance: 0,
|
total_distance: 0,
|
||||||
|
deaths_with_weapon: 0,
|
||||||
attacker_name: payload.victim_name,
|
attacker_name: payload.victim_name,
|
||||||
attacker_id: payload.victim_id,
|
attacker_id: payload.victim_id,
|
||||||
cause_of_death: payload.victim_current_weapon,
|
cause_of_death: payload.cause_of_death,
|
||||||
game_mode: payload.game_mode,
|
game_mode: payload.game_mode,
|
||||||
host: payload.host,
|
host: payload.host,
|
||||||
map: payload.map,
|
map: payload.map,
|
||||||
@@ -67,10 +78,29 @@ export default async function listenKills() {
|
|||||||
allData.push(deathEntry)
|
allData.push(deathEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!deathWithWeaponEntry) {
|
||||||
|
deathWithWeaponEntry = {
|
||||||
|
kills: 0,
|
||||||
|
deaths: 0,
|
||||||
|
max_distance: 0,
|
||||||
|
total_distance: 0,
|
||||||
|
deaths_with_weapon: 0,
|
||||||
|
attacker_name: payload.victim_name,
|
||||||
|
attacker_id: payload.victim_id,
|
||||||
|
cause_of_death: payload.victim_current_weapon,
|
||||||
|
game_mode: payload.game_mode,
|
||||||
|
host: payload.host,
|
||||||
|
map: payload.map,
|
||||||
|
servername: payload.servername
|
||||||
|
}
|
||||||
|
allData.push(deathWithWeaponEntry)
|
||||||
|
}
|
||||||
|
|
||||||
killEntry.kills++
|
killEntry.kills++
|
||||||
killEntry.total_distance += payload.distance
|
killEntry.total_distance += payload.distance
|
||||||
killEntry.max_distance = Math.max(killEntry.max_distance, payload.distance)
|
killEntry.max_distance = Math.max(killEntry.max_distance || 0, payload.distance)
|
||||||
deathEntry.deaths++
|
deathEntry.deaths++
|
||||||
|
deathWithWeaponEntry.deaths_with_weapon++
|
||||||
})
|
})
|
||||||
return pgClient
|
return pgClient
|
||||||
}
|
}
|
||||||
|
|||||||
+48
-5
@@ -57,13 +57,33 @@ async function processGlobalStats() {
|
|||||||
])
|
])
|
||||||
)
|
)
|
||||||
.with('deaths', (db) =>
|
.with('deaths', (db) =>
|
||||||
|
db
|
||||||
|
.selectFrom('kill')
|
||||||
|
.select([
|
||||||
|
count<number>('id').as('deaths'),
|
||||||
|
'victim_id',
|
||||||
|
sql<string>`last(victim_name)`.as('victim_name'),
|
||||||
|
'cause_of_death',
|
||||||
|
'map',
|
||||||
|
'game_mode',
|
||||||
|
'servername',
|
||||||
|
'host'
|
||||||
|
])
|
||||||
|
.groupBy([
|
||||||
|
'victim_id',
|
||||||
|
'cause_of_death',
|
||||||
|
'map',
|
||||||
|
'servername',
|
||||||
|
'host',
|
||||||
|
'game_mode'
|
||||||
|
])
|
||||||
|
).with('deathswithweapon', (db) =>
|
||||||
db
|
db
|
||||||
.selectFrom('kill')
|
.selectFrom('kill')
|
||||||
.select([
|
.select([
|
||||||
count<number>('id').as('deaths'),
|
count<number>('id').as('deaths'),
|
||||||
'victim_id',
|
'victim_id',
|
||||||
'victim_current_weapon',
|
'victim_current_weapon',
|
||||||
sql<string>`last(attacker_name)`.as('attacker_name'),
|
|
||||||
'map',
|
'map',
|
||||||
'game_mode',
|
'game_mode',
|
||||||
'servername',
|
'servername',
|
||||||
@@ -79,16 +99,39 @@ async function processGlobalStats() {
|
|||||||
])
|
])
|
||||||
)
|
)
|
||||||
.selectFrom('kills')
|
.selectFrom('kills')
|
||||||
.leftJoin('deaths', (join) =>
|
.fullJoin('deaths', (join) =>
|
||||||
join
|
join
|
||||||
.onRef('deaths.victim_id', '=', 'kills.attacker_id')
|
.onRef('deaths.victim_id', '=', 'kills.attacker_id')
|
||||||
.onRef('deaths.victim_current_weapon', '=', 'kills.cause_of_death')
|
.onRef('deaths.cause_of_death', '=', 'kills.cause_of_death')
|
||||||
.onRef('deaths.servername', '=', 'kills.servername')
|
.onRef('deaths.servername', '=', 'kills.servername')
|
||||||
.onRef('deaths.host', '=', 'kills.host')
|
.onRef('deaths.host', '=', 'kills.host')
|
||||||
.onRef('deaths.game_mode', '=', 'kills.game_mode')
|
.onRef('deaths.game_mode', '=', 'kills.game_mode')
|
||||||
|
.onRef('deaths.map', '=', 'kills.map')
|
||||||
)
|
)
|
||||||
.selectAll('kills')
|
.leftJoin('deathswithweapon', (join) =>
|
||||||
.select(sql<number>`COALESCE(deaths.deaths, 0)`.as('deaths'))
|
join
|
||||||
|
.onRef('deathswithweapon.victim_id', '=', 'kills.attacker_id')
|
||||||
|
.onRef('deathswithweapon.victim_current_weapon', '=', 'kills.cause_of_death')
|
||||||
|
.onRef('deathswithweapon.servername', '=', 'kills.servername')
|
||||||
|
.onRef('deathswithweapon.host', '=', 'kills.host')
|
||||||
|
.onRef('deathswithweapon.game_mode', '=', 'kills.game_mode')
|
||||||
|
.onRef('deathswithweapon.map', '=', 'kills.map')
|
||||||
|
)
|
||||||
|
//.selectAll('kills')
|
||||||
|
.select([
|
||||||
|
sql<string>`COALESCE(kills.attacker_name, deaths.victim_name)`.as('attacker_name'),
|
||||||
|
sql<string>`COALESCE(kills.attacker_id, deaths.victim_id)`.as('attacker_id'),
|
||||||
|
sql<number>`COALESCE(kills.kills, 0)`.as('kills'),
|
||||||
|
sql<number>`COALESCE(kills.total_distance, 0)`.as('total_distance'),
|
||||||
|
sql<number>`COALESCE(deathswithweapon.deaths, 0)`.as('deaths_with_weapon'),
|
||||||
|
sql<number>`COALESCE(kills.max_distance, 0)`.as('max_distance'),
|
||||||
|
sql<number>`COALESCE(deaths.deaths, 0)`.as('deaths'),
|
||||||
|
sql<string>`COALESCE(kills.servername, deaths.servername)`.as('servername'),
|
||||||
|
sql<number>`COALESCE(kills.host, deaths.host)`.as('host'),
|
||||||
|
sql<string>`COALESCE(kills.cause_of_death, deaths.cause_of_death)`.as('cause_of_death'),
|
||||||
|
sql<string>`COALESCE(kills.map, deaths.map)`.as('map'),
|
||||||
|
sql<string>`COALESCE(kills.game_mode, deaths.game_mode)`.as('game_mode'),
|
||||||
|
])
|
||||||
.execute()
|
.execute()
|
||||||
}
|
}
|
||||||
export default processAll
|
export default processAll
|
||||||
|
|||||||
+23
-9
@@ -56,14 +56,14 @@ router.post(
|
|||||||
'attacker_weapon_1_mods',
|
'attacker_weapon_1_mods',
|
||||||
'attacker_weapon_2_mods',
|
'attacker_weapon_2_mods',
|
||||||
'attacker_weapon_3_mods',
|
'attacker_weapon_3_mods',
|
||||||
'attacker_offhand_weapon_1',
|
'attacker_offhand_weapon_1_mods',
|
||||||
'attacker_offhand_weapon_2',
|
'attacker_offhand_weapon_2_mods',
|
||||||
'victim_current_weapon_mods',
|
'victim_current_weapon_mods',
|
||||||
'victim_weapon_1_mods',
|
'victim_weapon_1_mods',
|
||||||
'victim_weapon_2_mods',
|
'victim_weapon_2_mods',
|
||||||
'victim_weapon_3_mods',
|
'victim_weapon_3_mods',
|
||||||
'victim_offhand_weapon_1',
|
'victim_offhand_weapon_1_mods',
|
||||||
'victim_offhand_weapon_2'
|
'victim_offhand_weapon_2_mods'
|
||||||
]).customSanitizer((value) => {
|
]).customSanitizer((value) => {
|
||||||
if (isNaN(value) || !value) {
|
if (isNaN(value) || !value) {
|
||||||
value = 0
|
value = 0
|
||||||
@@ -75,14 +75,14 @@ router.post(
|
|||||||
'attacker_weapon_1_mods',
|
'attacker_weapon_1_mods',
|
||||||
'attacker_weapon_2_mods',
|
'attacker_weapon_2_mods',
|
||||||
'attacker_weapon_3_mods',
|
'attacker_weapon_3_mods',
|
||||||
'attacker_offhand_weapon_1',
|
'attacker_offhand_weapon_1_mods',
|
||||||
'attacker_offhand_weapon_2',
|
'attacker_offhand_weapon_2_mods',
|
||||||
'victim_current_weapon_mods',
|
'victim_current_weapon_mods',
|
||||||
'victim_weapon_1_mods',
|
'victim_weapon_1_mods',
|
||||||
'victim_weapon_2_mods',
|
'victim_weapon_2_mods',
|
||||||
'victim_weapon_3_mods',
|
'victim_weapon_3_mods',
|
||||||
'victim_offhand_weapon_1',
|
'victim_offhand_weapon_1_mods',
|
||||||
'victim_offhand_weapon_2'
|
'victim_offhand_weapon_2_mods'
|
||||||
])
|
])
|
||||||
.toInt()
|
.toInt()
|
||||||
.isInt()
|
.isInt()
|
||||||
@@ -109,12 +109,18 @@ router.post(
|
|||||||
'attacker_weapon_1',
|
'attacker_weapon_1',
|
||||||
'attacker_weapon_2',
|
'attacker_weapon_2',
|
||||||
'attacker_weapon_3',
|
'attacker_weapon_3',
|
||||||
|
'attacker_offhand_weapon_1',
|
||||||
|
'attacker_offhand_weapon_2',
|
||||||
'victim_name',
|
'victim_name',
|
||||||
'victim_current_weapon',
|
'victim_current_weapon',
|
||||||
'victim_weapon_1',
|
'victim_weapon_1',
|
||||||
'victim_weapon_2',
|
'victim_weapon_2',
|
||||||
'victim_weapon_3',
|
'victim_weapon_3',
|
||||||
'cause_of_death'
|
'victim_offhand_weapon_1',
|
||||||
|
'victim_offhand_weapon_2',
|
||||||
|
'cause_of_death',
|
||||||
|
'victim_titan',
|
||||||
|
'attacker_titan'
|
||||||
],
|
],
|
||||||
'must be composed of a maximum of 50 valid ascii characters'
|
'must be composed of a maximum of 50 valid ascii characters'
|
||||||
)
|
)
|
||||||
@@ -153,7 +159,9 @@ router.post(
|
|||||||
attacker_weapon_3,
|
attacker_weapon_3,
|
||||||
attacker_weapon_3_mods,
|
attacker_weapon_3_mods,
|
||||||
attacker_offhand_weapon_1,
|
attacker_offhand_weapon_1,
|
||||||
|
attacker_offhand_weapon_1_mods,
|
||||||
attacker_offhand_weapon_2,
|
attacker_offhand_weapon_2,
|
||||||
|
attacker_offhand_weapon_2_mods,
|
||||||
victim_name,
|
victim_name,
|
||||||
victim_id,
|
victim_id,
|
||||||
victim_current_weapon,
|
victim_current_weapon,
|
||||||
@@ -165,7 +173,9 @@ router.post(
|
|||||||
victim_weapon_3,
|
victim_weapon_3,
|
||||||
victim_weapon_3_mods,
|
victim_weapon_3_mods,
|
||||||
victim_offhand_weapon_1,
|
victim_offhand_weapon_1,
|
||||||
|
victim_offhand_weapon_1_mods,
|
||||||
victim_offhand_weapon_2,
|
victim_offhand_weapon_2,
|
||||||
|
victim_offhand_weapon_2_mods,
|
||||||
cause_of_death,
|
cause_of_death,
|
||||||
distance
|
distance
|
||||||
} = req.body
|
} = req.body
|
||||||
@@ -189,7 +199,9 @@ router.post(
|
|||||||
attacker_weapon_3,
|
attacker_weapon_3,
|
||||||
attacker_weapon_3_mods,
|
attacker_weapon_3_mods,
|
||||||
attacker_offhand_weapon_1,
|
attacker_offhand_weapon_1,
|
||||||
|
attacker_offhand_weapon_1_mods,
|
||||||
attacker_offhand_weapon_2,
|
attacker_offhand_weapon_2,
|
||||||
|
attacker_offhand_weapon_2_mods,
|
||||||
victim_name,
|
victim_name,
|
||||||
victim_id,
|
victim_id,
|
||||||
victim_current_weapon,
|
victim_current_weapon,
|
||||||
@@ -201,7 +213,9 @@ router.post(
|
|||||||
victim_weapon_3,
|
victim_weapon_3,
|
||||||
victim_weapon_3_mods,
|
victim_weapon_3_mods,
|
||||||
victim_offhand_weapon_1,
|
victim_offhand_weapon_1,
|
||||||
|
victim_offhand_weapon_1_mods,
|
||||||
victim_offhand_weapon_2,
|
victim_offhand_weapon_2,
|
||||||
|
victim_offhand_weapon_2_mods,
|
||||||
cause_of_death,
|
cause_of_death,
|
||||||
distance
|
distance
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { afterAll, beforeAll, describe, expect, test } from '@jest/globals'
|
import { afterAll, beforeAll, describe, expect, jest, test } from '@jest/globals'
|
||||||
import clientMain from '../src/clientMain'
|
import clientMain from '../src/clientMain'
|
||||||
|
import { pgClient } from '../src/process/onKill'
|
||||||
import * as dotenv from 'dotenv'
|
import * as dotenv from 'dotenv'
|
||||||
import db from '../src/db/db'
|
import db from '../src/db/db'
|
||||||
dotenv.config()
|
dotenv.config()
|
||||||
|
|
||||||
let listenServer
|
let listenServer
|
||||||
|
|
||||||
|
jest.setTimeout(30000)
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
listenServer = await clientMain;
|
listenServer = await clientMain;
|
||||||
})
|
})
|
||||||
@@ -59,6 +61,7 @@ describe('client', () => {
|
|||||||
|
|
||||||
afterAll((done) => {
|
afterAll((done) => {
|
||||||
listenServer.close(async () => {
|
listenServer.close(async () => {
|
||||||
|
await pgClient.end()
|
||||||
await db.destroy()
|
await db.destroy()
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
|
|||||||
+33
-16
@@ -1,29 +1,30 @@
|
|||||||
import { afterAll, beforeAll, describe, expect, jest, test } from '@jest/globals'
|
import { afterAll, beforeAll, describe, expect, jest, test } from '@jest/globals'
|
||||||
import clientMain from '../src/clientMain'
|
import clientMain from '../src/clientMain'
|
||||||
import serverMain from '../src/serverMain'
|
import serverMain from '../src/serverMain'
|
||||||
import listenKills from "../src/process/onKill"
|
import { pgClient } from '../src/process/onKill'
|
||||||
import * as dotenv from 'dotenv'
|
import * as dotenv from 'dotenv'
|
||||||
import db from '../src/db/db'
|
import db from '../src/db/db'
|
||||||
dotenv.config()
|
dotenv.config()
|
||||||
|
|
||||||
let listenClient
|
let listenClient
|
||||||
let listenServer
|
let listenServer
|
||||||
let pgClient
|
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
|
servername: 'testserver',
|
||||||
attacker_weapon_1_mods: 0,
|
attacker_weapon_1_mods: 0,
|
||||||
victim_id: '0',
|
victim_id: '0',
|
||||||
servername: 'testServer',
|
|
||||||
victim_name: 'TestVictim',
|
victim_name: 'TestVictim',
|
||||||
victim_offhand_weapon_2: 0,
|
victim_offhand_weapon_2: 'null',
|
||||||
attacker_offhand_weapon_3: '0',
|
victim_offhand_weapon_2_mods: 0,
|
||||||
victim_weapon_3_mods: 0,
|
victim_weapon_3_mods: 0,
|
||||||
attacker_weapon_2_mods: 0,
|
attacker_weapon_2_mods: NaN,
|
||||||
attacker_offhand_weapon_1: 0,
|
attacker_offhand_weapon_1: 'null',
|
||||||
attacker_weapon_3_mods: 0,
|
attacker_offhand_weapon_1_mods: 0,
|
||||||
attacker_offhand_weapon_2: 0,
|
attacker_weapon_3_mods: NaN,
|
||||||
victim_offhand_weapon_3: '0',
|
attacker_offhand_weapon_2: 'null',
|
||||||
victim_offhand_weapon_1: 0,
|
attacker_offhand_weapon_2_mods: NaN,
|
||||||
|
victim_offhand_weapon_1: 'offhand_weapon_test',
|
||||||
|
victim_offhand_weapon_1_mods: 0,
|
||||||
attacker_weapon_3: 'defender',
|
attacker_weapon_3: 'defender',
|
||||||
killstat_version: 'ks_3.0.0',
|
killstat_version: 'ks_3.0.0',
|
||||||
attacker_weapon_1: 'smr',
|
attacker_weapon_1: 'smr',
|
||||||
@@ -41,11 +42,13 @@ const data = {
|
|||||||
game_mode: 'tdm',
|
game_mode: 'tdm',
|
||||||
map: 'thaw',
|
map: 'thaw',
|
||||||
attacker_weapon_2: 'autopistol',
|
attacker_weapon_2: 'autopistol',
|
||||||
victim_weapon_1: 'smr',
|
victim_weapon_1: 'null',
|
||||||
victim_weapon_2: 'autopistol',
|
victim_weapon_2: 'autopistol',
|
||||||
victim_weapon_1_mods: 0,
|
victim_weapon_1_mods: 0,
|
||||||
victim_weapon_3: 'defender',
|
victim_weapon_3: 'defender',
|
||||||
attacker_name: 'TestAttacker'
|
attacker_name: 'TestAttacker',
|
||||||
|
victim_titan: 'null',
|
||||||
|
attacker_titan: 'null'
|
||||||
}
|
}
|
||||||
|
|
||||||
function waitFor(time: number) {
|
function waitFor(time: number) {
|
||||||
@@ -54,13 +57,11 @@ function waitFor(time: number) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
jest.setTimeout(15000)
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
jest.setTimeout(15000)
|
|
||||||
listenClient = await clientMain;
|
listenClient = await clientMain;
|
||||||
listenServer = await serverMain;
|
listenServer = await serverMain;
|
||||||
pgClient = await listenKills()
|
|
||||||
const yea = waitFor(1000)
|
const yea = waitFor(1000)
|
||||||
const response = await fetch(`http://127.0.0.1:3001/kill`, {
|
const response = await fetch(`http://127.0.0.1:3001/kill`, {
|
||||||
method: "POST", // *GET, POST, PUT, DELETE, etc.
|
method: "POST", // *GET, POST, PUT, DELETE, etc.
|
||||||
@@ -79,6 +80,7 @@ describe('realtime', () => {
|
|||||||
let playerKills
|
let playerKills
|
||||||
let weaponKills
|
let weaponKills
|
||||||
let playerDeaths
|
let playerDeaths
|
||||||
|
let serverKills
|
||||||
test('fetch player', async () => {
|
test('fetch player', async () => {
|
||||||
const request = await fetch("http://127.0.0.1:3000/players")
|
const request = await fetch("http://127.0.0.1:3000/players")
|
||||||
const data = await request.json()
|
const data = await request.json()
|
||||||
@@ -100,6 +102,14 @@ describe('realtime', () => {
|
|||||||
weaponKills = weapon.kills
|
weaponKills = weapon.kills
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('fetch server', async () => {
|
||||||
|
const request = await fetch("http://127.0.0.1:3000/servers")
|
||||||
|
const data = await request.json()
|
||||||
|
const server = data.testserver
|
||||||
|
expect(server).toHaveProperty('kills')
|
||||||
|
serverKills = server.kills
|
||||||
|
})
|
||||||
|
|
||||||
test('update player', async () => {
|
test('update player', async () => {
|
||||||
jest.setTimeout(15000)
|
jest.setTimeout(15000)
|
||||||
const yea = waitFor(1000)
|
const yea = waitFor(1000)
|
||||||
@@ -130,6 +140,13 @@ describe('realtime', () => {
|
|||||||
expect(weapon.kills).toBe(weaponKills + 1)
|
expect(weapon.kills).toBe(weaponKills + 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('check server update', async () => {
|
||||||
|
const request = await fetch("http://127.0.0.1:3000/servers")
|
||||||
|
const data = await request.json()
|
||||||
|
const server = data.testserver
|
||||||
|
expect(server.kills).toBe(serverKills + 1)
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
afterAll((done) => {
|
afterAll((done) => {
|
||||||
|
|||||||
+42
-34
@@ -65,15 +65,17 @@ describe('server', () => {
|
|||||||
attacker_weapon_1_mods: 0,
|
attacker_weapon_1_mods: 0,
|
||||||
victim_id: '0',
|
victim_id: '0',
|
||||||
victim_name: 'TestVictim',
|
victim_name: 'TestVictim',
|
||||||
victim_offhand_weapon_2: 0,
|
victim_offhand_weapon_2: 'offhand_weapon_test',
|
||||||
attacker_offhand_weapon_3: '0',
|
victim_offhand_weapon_2_mods: 0,
|
||||||
victim_weapon_3_mods: 0,
|
victim_weapon_3_mods: 0,
|
||||||
attacker_weapon_2_mods: 0,
|
attacker_weapon_2_mods: 0,
|
||||||
attacker_offhand_weapon_1: 0,
|
attacker_offhand_weapon_1: 'offhand_weapon_test',
|
||||||
|
attacker_offhand_weapon_1_mods: 0,
|
||||||
attacker_weapon_3_mods: 0,
|
attacker_weapon_3_mods: 0,
|
||||||
attacker_offhand_weapon_2: 0,
|
attacker_offhand_weapon_2: 'offhand_weapon_test',
|
||||||
victim_offhand_weapon_3: '0',
|
attacker_offhand_weapon_2_mods: 0,
|
||||||
victim_offhand_weapon_1: 0,
|
victim_offhand_weapon_1: 'offhand_weapon_test',
|
||||||
|
victim_offhand_weapon_1_mods: 0,
|
||||||
attacker_weapon_3: 'defender',
|
attacker_weapon_3: 'defender',
|
||||||
killstat_version: 'ks_3.0.0',
|
killstat_version: 'ks_3.0.0',
|
||||||
attacker_weapon_1: 'smr',
|
attacker_weapon_1: 'smr',
|
||||||
@@ -95,7 +97,9 @@ describe('server', () => {
|
|||||||
victim_weapon_2: 'autopistol',
|
victim_weapon_2: 'autopistol',
|
||||||
victim_weapon_1_mods: 0,
|
victim_weapon_1_mods: 0,
|
||||||
victim_weapon_3: 'defender',
|
victim_weapon_3: 'defender',
|
||||||
attacker_name: 'TestAttacker'
|
attacker_name: 'TestAttacker',
|
||||||
|
victim_titan: 'null',
|
||||||
|
attacker_titan: 'null'
|
||||||
}
|
}
|
||||||
const response = await fetch(`http://127.0.0.1:3001/kill`, {
|
const response = await fetch(`http://127.0.0.1:3001/kill`, {
|
||||||
method: "POST", // *GET, POST, PUT, DELETE, etc.
|
method: "POST", // *GET, POST, PUT, DELETE, etc.
|
||||||
@@ -114,40 +118,44 @@ describe('server', () => {
|
|||||||
test('register a kill with missing data', async () => {
|
test('register a kill with missing data', async () => {
|
||||||
const data = {
|
const data = {
|
||||||
servername: 'testserver',
|
servername: 'testserver',
|
||||||
attacker_weapon_1_mods: NaN,
|
attacker_weapon_1_mods: 0,
|
||||||
victim_id: '0',
|
victim_id: '0',
|
||||||
victim_name: 'TestVictim',
|
victim_name: 'TestVictim',
|
||||||
victim_offhand_weapon_2: NaN,
|
victim_offhand_weapon_2: 'null',
|
||||||
attacker_offhand_weapon_3: 'null',
|
victim_offhand_weapon_2_mods: 0,
|
||||||
victim_weapon_3_mods: 19,
|
victim_weapon_3_mods: 0,
|
||||||
attacker_weapon_2_mods: NaN,
|
attacker_weapon_2_mods: NaN,
|
||||||
attacker_offhand_weapon_1: NaN,
|
attacker_offhand_weapon_1: 'null',
|
||||||
|
attacker_offhand_weapon_1_mods: 0,
|
||||||
attacker_weapon_3_mods: NaN,
|
attacker_weapon_3_mods: NaN,
|
||||||
attacker_offhand_weapon_2: NaN,
|
attacker_offhand_weapon_2: 'null',
|
||||||
victim_offhand_weapon_3: '0',
|
attacker_offhand_weapon_2_mods: NaN,
|
||||||
victim_offhand_weapon_1: 1,
|
victim_offhand_weapon_1: 'offhand_weapon_test',
|
||||||
attacker_weapon_3: 'null',
|
victim_offhand_weapon_1_mods: 0,
|
||||||
|
attacker_weapon_3: 'defender',
|
||||||
killstat_version: 'ks_3.0.0',
|
killstat_version: 'ks_3.0.0',
|
||||||
attacker_weapon_1: 'null',
|
attacker_weapon_1: 'smr',
|
||||||
match_id: '554fa7a3',
|
match_id: '31b1f34d',
|
||||||
distance: 202,
|
distance: 0,
|
||||||
victim_current_weapon: 'car',
|
victim_current_weapon: 'smr',
|
||||||
cause_of_death: 'satchel',
|
cause_of_death: 'smr',
|
||||||
victim_weapon_2_mods: NaN,
|
victim_weapon_2_mods: 0,
|
||||||
victim_current_weapon_mods: NaN,
|
victim_current_weapon_mods: 0,
|
||||||
attacker_current_weapon_mods: NaN,
|
attacker_current_weapon_mods: 0,
|
||||||
game_time: 516.417,
|
game_time: 377.799,
|
||||||
player_count: 13,
|
player_count: 1,
|
||||||
attacker_current_weapon: 'null',
|
attacker_current_weapon: 'smr',
|
||||||
attacker_id: '1',
|
attacker_id: '1',
|
||||||
game_mode: 'ps',
|
game_mode: 'tdm',
|
||||||
map: 'grave',
|
map: 'thaw',
|
||||||
attacker_weapon_2: 'null',
|
attacker_weapon_2: 'autopistol',
|
||||||
victim_weapon_1: 'car',
|
victim_weapon_1: 'null',
|
||||||
victim_weapon_2: 'autopistol',
|
victim_weapon_2: 'autopistol',
|
||||||
victim_weapon_1_mods: NaN,
|
victim_weapon_1_mods: 0,
|
||||||
victim_weapon_3: 'arc_launcher',
|
victim_weapon_3: 'defender',
|
||||||
attacker_name: 'TestAttacker'
|
attacker_name: 'TestAttacker',
|
||||||
|
victim_titan: 'null',
|
||||||
|
attacker_titan: 'null'
|
||||||
}
|
}
|
||||||
const response = await fetch(`http://127.0.0.1:3001/kill`, {
|
const response = await fetch(`http://127.0.0.1:3001/kill`, {
|
||||||
method: "POST", // *GET, POST, PUT, DELETE, etc.
|
method: "POST", // *GET, POST, PUT, DELETE, etc.
|
||||||
|
|||||||
Reference in New Issue
Block a user