refactor, add vehicles and classes
add gitignore for node_modules
This commit is contained in:
parent
6bedb26037
commit
f5df061b42
19 changed files with 1213 additions and 108 deletions
122
Cargo.lock
generated
122
Cargo.lock
generated
|
@ -41,9 +41,8 @@ dependencies = [
|
||||||
name = "api"
|
name = "api"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
|
||||||
"redis",
|
|
||||||
"rocket",
|
"rocket",
|
||||||
|
"rocket_db_pools",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
@ -174,7 +173,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4"
|
checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -240,6 +243,38 @@ dependencies = [
|
||||||
"cipher",
|
"cipher",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deadpool"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "421fe0f90f2ab22016f32a9881be5134fdd71c65298917084b0c7477cbc3856e"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"deadpool-runtime",
|
||||||
|
"num_cpus",
|
||||||
|
"retain_mut",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deadpool-redis"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a62ebf187bc30bfc1a14bed4073912b988551d111208fe800b27c32df282481"
|
||||||
|
dependencies = [
|
||||||
|
"deadpool",
|
||||||
|
"redis 0.21.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deadpool-runtime"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eaa37046cc0f6c3cc6090fbdbf73ef0b8ef4cfcc37f6befc0020f63e8cf121e1"
|
||||||
|
dependencies = [
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "devise"
|
name = "devise"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
@ -971,6 +1006,17 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r2d2"
|
||||||
|
version = "0.8.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"parking_lot",
|
||||||
|
"scheduled-thread-pool",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
|
@ -1001,6 +1047,25 @@ dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redis"
|
||||||
|
version = "0.21.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "571c252c68d09a2ad3e49edd14e9ee48932f3e0f27b06b4ea4c9b2a706d31103"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"bytes",
|
||||||
|
"combine",
|
||||||
|
"futures-util",
|
||||||
|
"itoa",
|
||||||
|
"percent-encoding",
|
||||||
|
"pin-project-lite",
|
||||||
|
"ryu",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redis"
|
name = "redis"
|
||||||
version = "0.22.1"
|
version = "0.22.1"
|
||||||
|
@ -1010,11 +1075,21 @@ dependencies = [
|
||||||
"combine",
|
"combine",
|
||||||
"itoa",
|
"itoa",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
|
"r2d2",
|
||||||
"ryu",
|
"ryu",
|
||||||
"sha1_smol",
|
"sha1_smol",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redis_ts"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "52c56cb76c7c8a0f2624cd8d2fec425080637dc0e43b850eee1fd6116577c3be"
|
||||||
|
dependencies = [
|
||||||
|
"redis 0.22.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
|
@ -1077,6 +1152,12 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "retain_mut"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rocket"
|
name = "rocket"
|
||||||
version = "0.5.0-rc.2"
|
version = "0.5.0-rc.2"
|
||||||
|
@ -1132,6 +1213,29 @@ dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rocket_db_pools"
|
||||||
|
version = "0.1.0-rc.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8bc154f4f4985a136e2d59c336474a56da02103993f5e637e3a5424971ee4eff"
|
||||||
|
dependencies = [
|
||||||
|
"deadpool",
|
||||||
|
"deadpool-redis",
|
||||||
|
"rocket",
|
||||||
|
"rocket_db_pools_codegen",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rocket_db_pools_codegen"
|
||||||
|
version = "0.1.0-rc.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0aa8f9b37bb1d4827aa5cca400d74e91d30f4352713cb65d6e7427bafe21336c"
|
||||||
|
dependencies = [
|
||||||
|
"devise",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rocket_http"
|
name = "rocket_http"
|
||||||
version = "0.5.0-rc.2"
|
version = "0.5.0-rc.2"
|
||||||
|
@ -1181,6 +1285,15 @@ dependencies = [
|
||||||
"windows-sys 0.36.1",
|
"windows-sys 0.36.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scheduled-thread-pool"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "977a7519bff143a44f842fd07e80ad1329295bd71686457f18e496736f4bf9bf"
|
||||||
|
dependencies = [
|
||||||
|
"parking_lot",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scoped-tls"
|
name = "scoped-tls"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -1364,7 +1477,7 @@ name = "tasks"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"redis",
|
"redis 0.22.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1754,7 +1867,8 @@ dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"redis",
|
"redis 0.22.1",
|
||||||
|
"redis_ts",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|
|
@ -65,8 +65,10 @@ This API only supports GET, and supports CORS.
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
- Websocket processors
|
- Websocket processors
|
||||||
- One per PC, PS4US, PS4EU
|
- A pair per PC, PS4US, PS4EU
|
||||||
- Connects to [wss://push.nanite-systems.net](https://nanite-systems.net), one process per "environment"
|
- Connects to [wss://push.nanite-systems.net](https://nanite-systems.net) and Census Websocket
|
||||||
|
- Primary will connect to NS.
|
||||||
|
- Backup will connect to Census. It will wait for 60 seconds before deciding the primary is dead, and then start processing events.
|
||||||
- API
|
- API
|
||||||
- Serves https://saerro.harasse.rs
|
- Serves https://saerro.harasse.rs
|
||||||
- Redis
|
- Redis
|
||||||
|
|
|
@ -14,9 +14,10 @@ services:
|
||||||
image: ghcr.io/genudine/saerro/api:latest
|
image: ghcr.io/genudine/saerro/api:latest
|
||||||
pull_policy: always
|
pull_policy: always
|
||||||
ports:
|
ports:
|
||||||
- 8000:8000
|
- 80:8000
|
||||||
links:
|
links:
|
||||||
- redis
|
- redis
|
||||||
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
- REDIS_ADDR=redis://redis:6379
|
- REDIS_ADDR=redis://redis:6379
|
||||||
- ROCKET_ADDRESS=0.0.0.0
|
- ROCKET_ADDRESS=0.0.0.0
|
||||||
|
@ -24,30 +25,79 @@ services:
|
||||||
ws_pc:
|
ws_pc:
|
||||||
image: ghcr.io/genudine/saerro/websocket:latest
|
image: ghcr.io/genudine/saerro/websocket:latest
|
||||||
pull_policy: always
|
pull_policy: always
|
||||||
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
REDIS_ADDR: redis://redis:6379
|
REDIS_ADDR: redis://redis:6379
|
||||||
WS_ADDR: wss://push.nanite-systems.net/streaming?environment=ps2&service-id=s:saegd
|
WS_ADDR: wss://push.nanite-systems.net/streaming?environment=ps2&service-id=s:saegd
|
||||||
WORLDS: 1,10,13,17,19,40
|
WORLDS: 1,10,13,17,19,40
|
||||||
|
PAIR: pc
|
||||||
|
ROLE: primary
|
||||||
|
links:
|
||||||
|
- redis
|
||||||
|
|
||||||
|
ws_pc_backup:
|
||||||
|
image: ghcr.io/genudine/saerro/websocket:latest
|
||||||
|
pull_policy: always
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
REDIS_ADDR: redis://redis:6379
|
||||||
|
WS_ADDR: wss://push.planetside2.com/streaming?environment=ps2&service-id=s:saegd
|
||||||
|
WORLDS: 1,10,13,17,19,40
|
||||||
|
PAIR: pc
|
||||||
|
ROLE: backup
|
||||||
links:
|
links:
|
||||||
- redis
|
- redis
|
||||||
|
|
||||||
ws_ps4us:
|
ws_ps4us:
|
||||||
image: ghcr.io/genudine/saerro/websocket:latest
|
image: ghcr.io/genudine/saerro/websocket:latest
|
||||||
pull_policy: always
|
pull_policy: always
|
||||||
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
REDIS_ADDR: redis://redis:6379
|
REDIS_ADDR: redis://redis:6379
|
||||||
WS_ADDR: wss://push.nanite-systems.net/streaming?environment=ps2ps4us&service-id=s:saegd
|
WS_ADDR: wss://push.nanite-systems.net/streaming?environment=ps2ps4us&service-id=s:saegd
|
||||||
WORLDS: 1000
|
WORLDS: 1000
|
||||||
|
PAIR: ps4us
|
||||||
|
ROLE: primary
|
||||||
links:
|
links:
|
||||||
- redis
|
- redis
|
||||||
|
|
||||||
|
ws_ps4us_backup:
|
||||||
|
image: ghcr.io/genudine/saerro/websocket:latest
|
||||||
|
pull_policy: always
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
REDIS_ADDR: redis://redis:6379
|
||||||
|
WS_ADDR: wss://push.planetside2.com/streaming?environment=ps2ps4us&service-id=s:saegd
|
||||||
|
WORLDS: 1000
|
||||||
|
PAIR: ps4us
|
||||||
|
ROLE: backup
|
||||||
|
links:
|
||||||
|
- redis
|
||||||
|
|
||||||
|
|
||||||
ws_ps4eu:
|
ws_ps4eu:
|
||||||
image: ghcr.io/genudine/saerro/websocket:latest
|
image: ghcr.io/genudine/saerro/websocket:latest
|
||||||
pull_policy: always
|
pull_policy: always
|
||||||
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
REDIS_ADDR: redis://redis:6379
|
REDIS_ADDR: redis://redis:6379
|
||||||
WS_ADDR: wss://push.nanite-systems.net/streaming?environment=ps2ps4eu&service-id=s:saegd
|
WS_ADDR: wss://push.nanite-systems.net/streaming?environment=ps2ps4eu&service-id=s:saegd
|
||||||
WORLDS: 2000
|
WORLDS: 2000
|
||||||
|
PAIR: ps4eu
|
||||||
|
ROLE: primary
|
||||||
|
links:
|
||||||
|
- redis
|
||||||
|
|
||||||
|
ws_ps4eu_backup:
|
||||||
|
image: ghcr.io/genudine/saerro/websocket:latest
|
||||||
|
pull_policy: always
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
REDIS_ADDR: redis://redis:6379
|
||||||
|
WS_ADDR: wss://push.planetside2.com/streaming?environment=ps2ps4eu&service-id=s:saegd
|
||||||
|
WORLDS: 2000
|
||||||
|
PAIR: ps4eu
|
||||||
|
ROLE: backup
|
||||||
links:
|
links:
|
||||||
- redis
|
- redis
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ version: "3"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
redis:
|
redis:
|
||||||
image: redis:alpine
|
image: redislabs/redistimeseries:1.8.3
|
||||||
command: redis-server --save "" --appendonly no
|
command: redis-server --save "" --appendonly no
|
||||||
container_name: redis
|
container_name: redis
|
||||||
restart: always
|
restart: always
|
||||||
|
|
1
hack/codegen/.gitignore
vendored
Normal file
1
hack/codegen/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
node_modules
|
87
hack/codegen/codegen.js
Normal file
87
hack/codegen/codegen.js
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import fetch from "node-fetch";
|
||||||
|
|
||||||
|
const vehicles_hashmap = async () => {
|
||||||
|
const req = await fetch("https://census.lithafalcon.cc/get/ps2/vehicle");
|
||||||
|
const resp = await req.json();
|
||||||
|
|
||||||
|
const relevantVehicles = [
|
||||||
|
"flash",
|
||||||
|
"sunderer",
|
||||||
|
"lightning",
|
||||||
|
"scythe",
|
||||||
|
"vanguard",
|
||||||
|
"prowler",
|
||||||
|
"reaver",
|
||||||
|
"mosquito",
|
||||||
|
"galaxy",
|
||||||
|
"valkyrie",
|
||||||
|
"liberator",
|
||||||
|
"ant",
|
||||||
|
"harasser",
|
||||||
|
"dervish",
|
||||||
|
"chimera",
|
||||||
|
"javelin",
|
||||||
|
"corsair",
|
||||||
|
];
|
||||||
|
|
||||||
|
const matcher = new RegExp(`\\b${relevantVehicles.join("|")}\\b`, "i");
|
||||||
|
|
||||||
|
return resp.vehicle_list
|
||||||
|
.reduce((acc, vehicle) => {
|
||||||
|
if (vehicle.name?.en) {
|
||||||
|
let result = vehicle.name.en.match(matcher);
|
||||||
|
if (result) {
|
||||||
|
acc.push(`("${vehicle.vehicle_id}", "${result[0].toLowerCase()}")`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, [])
|
||||||
|
.filter((v) => !!v);
|
||||||
|
};
|
||||||
|
|
||||||
|
const class_hashmap = async () => {
|
||||||
|
const req = await fetch("https://census.lithafalcon.cc/get/ps2/loadout");
|
||||||
|
const resp = await req.json();
|
||||||
|
|
||||||
|
return resp.loadout_list.map(
|
||||||
|
(loadout) =>
|
||||||
|
`("${loadout.loadout_id}", "${loadout.code_name
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\btr|nc|vs|nso\b/, "")
|
||||||
|
.trim()
|
||||||
|
.replace("defector", "max")
|
||||||
|
.replace(/ /g, "_")}")`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(`// GENERATED CODE -- Do not edit. Run \`node hack/codegen/codegen.js > services/websocket/src/translators.rs\` to regenerate.
|
||||||
|
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
static VEHICLE_TO_NAME: Lazy<HashMap<&str, &str>> = Lazy::new(|| {
|
||||||
|
HashMap::from([
|
||||||
|
${(await vehicles_hashmap()).join(",\n ")},
|
||||||
|
])
|
||||||
|
});
|
||||||
|
|
||||||
|
pub fn vehicle_to_name(vehicle_id: &str) -> String {
|
||||||
|
match VEHICLE_TO_NAME.get(&vehicle_id) {
|
||||||
|
Some(name) => name.to_string(),
|
||||||
|
None => "unknown".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static LOADOUT_TO_CLASS: Lazy<HashMap<&str, &str>> = Lazy::new(|| {
|
||||||
|
HashMap::from([
|
||||||
|
${(await class_hashmap()).join(",\n ")},
|
||||||
|
])
|
||||||
|
});
|
||||||
|
|
||||||
|
pub fn loadout_to_class(loadout_id: &str) -> String {
|
||||||
|
match LOADOUT_TO_CLASS.get(&loadout_id) {
|
||||||
|
Some(name) => name.to_string(),
|
||||||
|
None => "unknown".to_string(),
|
||||||
|
}
|
||||||
|
}`);
|
144
hack/codegen/package-lock.json
generated
Normal file
144
hack/codegen/package-lock.json
generated
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
{
|
||||||
|
"name": "saerro-codegen",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 2,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "saerro-codegen",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"node-fetch": "^3.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/data-uri-to-buffer": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fetch-blob": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/jimmywarting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "paypal",
|
||||||
|
"url": "https://paypal.me/jimmywarting"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"node-domexception": "^1.0.0",
|
||||||
|
"web-streams-polyfill": "^3.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20 || >= 14.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/formdata-polyfill": {
|
||||||
|
"version": "4.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
||||||
|
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
|
||||||
|
"dependencies": {
|
||||||
|
"fetch-blob": "^3.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-domexception": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/jimmywarting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://paypal.me/jimmywarting"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-fetch": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==",
|
||||||
|
"dependencies": {
|
||||||
|
"data-uri-to-buffer": "^4.0.0",
|
||||||
|
"fetch-blob": "^3.1.4",
|
||||||
|
"formdata-polyfill": "^4.0.10"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/node-fetch"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/web-streams-polyfill": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"data-uri-to-buffer": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA=="
|
||||||
|
},
|
||||||
|
"fetch-blob": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
|
||||||
|
"requires": {
|
||||||
|
"node-domexception": "^1.0.0",
|
||||||
|
"web-streams-polyfill": "^3.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"formdata-polyfill": {
|
||||||
|
"version": "4.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
||||||
|
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
|
||||||
|
"requires": {
|
||||||
|
"fetch-blob": "^3.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node-domexception": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="
|
||||||
|
},
|
||||||
|
"node-fetch": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==",
|
||||||
|
"requires": {
|
||||||
|
"data-uri-to-buffer": "^4.0.0",
|
||||||
|
"fetch-blob": "^3.1.4",
|
||||||
|
"formdata-polyfill": "^4.0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"web-streams-polyfill": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
hack/codegen/package.json
Normal file
15
hack/codegen/package.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"name": "saerro-codegen",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "codegen.js",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"node-fetch": "^3.3.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,6 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rocket = { version = "0.5.0-rc.2", features = ["json"] }
|
rocket = { version = "0.5.0-rc.2", features = ["json"] }
|
||||||
|
rocket_db_pools = { version = "0.1.0-rc.2", features = [ "deadpool_redis" ] }
|
||||||
serde_json = "1.0.88"
|
serde_json = "1.0.88"
|
||||||
serde = "1.0.147"
|
serde = "1.0.147"
|
||||||
redis = { version = "0.22.1", default_features = false, features = [] }
|
|
||||||
once_cell = "1.16.0"
|
|
106
services/api/src/classes.rs
Normal file
106
services/api/src/classes.rs
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
use core::time;
|
||||||
|
use rocket_db_pools::deadpool_redis::redis::{pipe, AsyncCommands};
|
||||||
|
use rocket_db_pools::Connection;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::ops::Sub;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
use crate::redispool::RedisPool;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
struct ClassCounts {
|
||||||
|
world_id: String,
|
||||||
|
classes: Classes,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
struct Classes {
|
||||||
|
light_assault: u32,
|
||||||
|
engineer: u32,
|
||||||
|
combat_medic: u32,
|
||||||
|
heavy_assault: u32,
|
||||||
|
infiltrator: u32,
|
||||||
|
max: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/w/<world_id>/classes")]
|
||||||
|
pub async fn get_classes(world_id: String, mut con: Connection<RedisPool>) -> serde_json::Value {
|
||||||
|
let cache_key = format!("cache:classes:{}", world_id);
|
||||||
|
|
||||||
|
match con.get::<String, String>(cache_key.clone()).await {
|
||||||
|
Ok(cached) => {
|
||||||
|
return serde_json::from_str(&cached).unwrap();
|
||||||
|
}
|
||||||
|
Err(_) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let filter_timestamp = SystemTime::now()
|
||||||
|
.sub(time::Duration::from_secs(60 * 15))
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs();
|
||||||
|
|
||||||
|
// I hate this but it's fast???
|
||||||
|
// The type only allows 12 at a time.
|
||||||
|
let (light_assault, engineer, combat_medic, heavy_assault, infiltrator, max): (
|
||||||
|
u32,
|
||||||
|
u32,
|
||||||
|
u32,
|
||||||
|
u32,
|
||||||
|
u32,
|
||||||
|
u32,
|
||||||
|
) = pipe()
|
||||||
|
.zcount(
|
||||||
|
format!("c:{}/{}", world_id, "light_assault"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.zcount(
|
||||||
|
format!("c:{}/{}", world_id, "engineer"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.zcount(
|
||||||
|
format!("c:{}/{}", world_id, "combat_medic"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.zcount(
|
||||||
|
format!("c:{}/{}", world_id, "heavy_assault"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.zcount(
|
||||||
|
format!("c:{}/{}", world_id, "infiltrator"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.zcount(
|
||||||
|
format!("c:{}/{}", world_id, "max"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.query_async(&mut *con)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let response = ClassCounts {
|
||||||
|
world_id,
|
||||||
|
classes: Classes {
|
||||||
|
light_assault,
|
||||||
|
engineer,
|
||||||
|
combat_medic,
|
||||||
|
heavy_assault,
|
||||||
|
infiltrator,
|
||||||
|
max,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let out = json!(response);
|
||||||
|
|
||||||
|
con.set_ex::<String, String, ()>(cache_key, out.to_string(), 5)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
|
@ -1,10 +1,16 @@
|
||||||
|
pub mod classes;
|
||||||
pub mod cors;
|
pub mod cors;
|
||||||
|
pub mod population;
|
||||||
|
pub mod redispool;
|
||||||
|
pub mod vehicles;
|
||||||
|
|
||||||
|
use redispool::RedisPool;
|
||||||
|
use rocket::fairing::AdHoc;
|
||||||
|
use rocket::{error, Build, Rocket};
|
||||||
|
use rocket_db_pools::deadpool_redis::redis::{cmd, pipe};
|
||||||
|
use rocket_db_pools::{Connection, Database};
|
||||||
|
|
||||||
use core::time;
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use rocket::{Build, Rocket};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{ops::Sub, time::SystemTime};
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
@ -16,31 +22,14 @@ struct IncomingHeaders {
|
||||||
host: String,
|
host: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
fn hello_world(host: String, world_id: &str) -> serde_json::Value {
|
||||||
struct Factions {
|
json!({
|
||||||
tr: u32,
|
"population": format!("https://{}/w/{}", host, world_id),
|
||||||
nc: u32,
|
"vehicles": format!("https://{}/w/{}/vehicles", host, world_id),
|
||||||
vs: u32,
|
"classes": format!("https://{}/w/{}/classes", host, world_id),
|
||||||
ns: u32,
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
struct WorldPopulation {
|
|
||||||
world_id: u32,
|
|
||||||
total: u32,
|
|
||||||
factions: Factions,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
struct MultipleWorldPopulation {
|
|
||||||
worlds: Vec<WorldPopulation>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub static REDIS_CLIENT: Lazy<redis::Client> = Lazy::new(|| {
|
|
||||||
redis::Client::open(std::env::var("REDIS_ADDR").unwrap_or("redis://localhost:6379".to_string()))
|
|
||||||
.unwrap()
|
|
||||||
});
|
|
||||||
|
|
||||||
fn hello(host: String) -> serde_json::Value {
|
fn hello(host: String) -> serde_json::Value {
|
||||||
json!({
|
json!({
|
||||||
"@": "Saerro Listening Post - PlanetSide 2 Live Population API",
|
"@": "Saerro Listening Post - PlanetSide 2 Live Population API",
|
||||||
|
@ -48,73 +37,76 @@ fn hello(host: String) -> serde_json::Value {
|
||||||
"@Disclaimer": "Genudine Dynamics is not responsible for any damages caused by this software. Use at your own risk.",
|
"@Disclaimer": "Genudine Dynamics is not responsible for any damages caused by this software. Use at your own risk.",
|
||||||
"@Support": "#api-dev in https://discord.com/servers/planetside-2-community-251073753759481856",
|
"@Support": "#api-dev in https://discord.com/servers/planetside-2-community-251073753759481856",
|
||||||
"Worlds": {
|
"Worlds": {
|
||||||
"Connery": format!("https://{}/w/1", host),
|
"Connery": hello_world(host.clone(), "1"),
|
||||||
"Miller": format!("https://{}/w/10", host),
|
"Miller": hello_world(host.clone(), "10"),
|
||||||
"Cobalt": format!("https://{}/w/13", host),
|
"Cobalt": hello_world(host.clone(), "13"),
|
||||||
"Emerald": format!("https://{}/w/17", host),
|
"Emerald": hello_world(host.clone(), "17"),
|
||||||
"Jaeger": format!("https://{}/w/19", host),
|
"Jaeger": hello_world(host.clone(), "19"),
|
||||||
"SolTech": format!("https://{}/w/40", host),
|
"SolTech": hello_world(host.clone(), "40"),
|
||||||
"Genudine": format!("https://{}/w/1000", host),
|
"Genudine": hello_world(host.clone(), "1000"),
|
||||||
"Ceres": format!("https://{}/w/2000", host),
|
"Ceres": hello_world(host.clone(), "2000"),
|
||||||
},
|
},
|
||||||
"All Worlds": format!("https://{}/m/?ids=1,10,13,17,19,40,1000,2000", host),
|
// "All World Population": format!("https://{}/m/?ids=1,10,13,17,19,40,1000,2000", host),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_world_pop(world_id: String) -> WorldPopulation {
|
|
||||||
let mut con = REDIS_CLIENT.get_connection().unwrap();
|
|
||||||
|
|
||||||
let filter_timestamp = SystemTime::now()
|
|
||||||
.sub(time::Duration::from_secs(60 * 15))
|
|
||||||
.duration_since(SystemTime::UNIX_EPOCH)
|
|
||||||
.unwrap()
|
|
||||||
.as_secs();
|
|
||||||
|
|
||||||
let (vs, nc, tr, ns): (u32, u32, u32, u32) = redis::pipe()
|
|
||||||
.zcount(format!("wp:{}/{}", world_id, 1), filter_timestamp, "+inf")
|
|
||||||
.zcount(format!("wp:{}/{}", world_id, 2), filter_timestamp, "+inf")
|
|
||||||
.zcount(format!("wp:{}/{}", world_id, 3), filter_timestamp, "+inf")
|
|
||||||
.zcount(format!("wp:{}/{}", world_id, 4), filter_timestamp, "+inf")
|
|
||||||
.query(&mut con)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let total = tr + vs + nc;
|
|
||||||
|
|
||||||
let response = WorldPopulation {
|
|
||||||
world_id: world_id.parse().unwrap(),
|
|
||||||
total,
|
|
||||||
factions: Factions { tr, nc, vs, ns },
|
|
||||||
};
|
|
||||||
|
|
||||||
response
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/w/<world_id>")]
|
|
||||||
async fn world_pop(world_id: String) -> serde_json::Value {
|
|
||||||
let response = get_world_pop(world_id).await;
|
|
||||||
|
|
||||||
json!(response)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/m?<ids>")]
|
|
||||||
async fn multiple_world_pop(ids: String) -> serde_json::Value {
|
|
||||||
let mut response = MultipleWorldPopulation { worlds: vec![] };
|
|
||||||
|
|
||||||
for id in ids.split(",") {
|
|
||||||
response.worlds.push(get_world_pop(id.to_string()).await);
|
|
||||||
}
|
|
||||||
|
|
||||||
json!(response)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
async fn index() -> serde_json::Value {
|
fn index() -> serde_json::Value {
|
||||||
hello("saerro.harasse.rs".to_string())
|
hello("saerro.harasse.rs".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/health")]
|
||||||
|
async fn health(mut con: Connection<RedisPool>) -> serde_json::Value {
|
||||||
|
let (ping, pc, ps4us, ps4eu): (String, bool, bool, bool) = pipe()
|
||||||
|
.cmd("PING")
|
||||||
|
.get("heartbeat:pc")
|
||||||
|
.get("heartbeat:ps4us")
|
||||||
|
.get("heartbeat:ps4eu")
|
||||||
|
.query_async(&mut *con)
|
||||||
|
.await
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
json!({
|
||||||
|
"status": "ok",
|
||||||
|
"redis": ping == "PONG",
|
||||||
|
"pc": if pc { "primary" } else { "backup/down" },
|
||||||
|
"ps4us": if ps4us { "primary" } else { "backup/down" },
|
||||||
|
"ps4eu": if ps4eu { "primary" } else { "backup/down" },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[launch]
|
#[launch]
|
||||||
fn rocket() -> Rocket<Build> {
|
fn rocket() -> Rocket<Build> {
|
||||||
|
let figment = rocket::Config::figment().merge((
|
||||||
|
"databases.redis.url",
|
||||||
|
format!(
|
||||||
|
"redis://{}:{}",
|
||||||
|
std::env::var("REDIS_HOST").unwrap_or("localhost".to_string()),
|
||||||
|
std::env::var("REDIS_PORT").unwrap_or("6379".to_string()),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
rocket::build()
|
rocket::build()
|
||||||
|
.configure(figment)
|
||||||
.attach(cors::CORS)
|
.attach(cors::CORS)
|
||||||
.mount("/", routes![index, world_pop, multiple_world_pop])
|
.attach(RedisPool::init())
|
||||||
|
.attach(AdHoc::on_ignite("Redis Check", |rocket| async move {
|
||||||
|
if let Some(pool) = RedisPool::fetch(&rocket) {
|
||||||
|
let mut con = pool.get().await.unwrap();
|
||||||
|
let _: () = cmd("PING").query_async(&mut con).await.unwrap();
|
||||||
|
} else {
|
||||||
|
error!("Redis connection failed");
|
||||||
|
}
|
||||||
|
rocket
|
||||||
|
}))
|
||||||
|
.mount(
|
||||||
|
"/",
|
||||||
|
routes![
|
||||||
|
index,
|
||||||
|
health,
|
||||||
|
population::get_world_pop,
|
||||||
|
vehicles::get_vehicles,
|
||||||
|
classes::get_classes,
|
||||||
|
],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
55
services/api/src/population.rs
Normal file
55
services/api/src/population.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
use core::time;
|
||||||
|
use std::{ops::Sub, time::SystemTime};
|
||||||
|
|
||||||
|
use rocket_db_pools::{deadpool_redis::redis::pipe, Connection};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::redispool::RedisPool;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Factions {
|
||||||
|
tr: u32,
|
||||||
|
nc: u32,
|
||||||
|
vs: u32,
|
||||||
|
ns: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct WorldPopulation {
|
||||||
|
world_id: u32,
|
||||||
|
total: u32,
|
||||||
|
factions: Factions,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct MultipleWorldPopulation {
|
||||||
|
worlds: Vec<WorldPopulation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/w/<world_id>")]
|
||||||
|
pub async fn get_world_pop(world_id: String, mut con: Connection<RedisPool>) -> serde_json::Value {
|
||||||
|
let filter_timestamp = SystemTime::now()
|
||||||
|
.sub(time::Duration::from_secs(60 * 15))
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs();
|
||||||
|
|
||||||
|
let (vs, nc, tr, ns): (u32, u32, u32, u32) = pipe()
|
||||||
|
.zcount(format!("wp:{}/{}", world_id, 1), filter_timestamp, "+inf")
|
||||||
|
.zcount(format!("wp:{}/{}", world_id, 2), filter_timestamp, "+inf")
|
||||||
|
.zcount(format!("wp:{}/{}", world_id, 3), filter_timestamp, "+inf")
|
||||||
|
.zcount(format!("wp:{}/{}", world_id, 4), filter_timestamp, "+inf")
|
||||||
|
.query_async(&mut *con)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let total = tr + vs + nc;
|
||||||
|
|
||||||
|
let response = WorldPopulation {
|
||||||
|
world_id: world_id.parse().unwrap(),
|
||||||
|
total,
|
||||||
|
factions: Factions { tr, nc, vs, ns },
|
||||||
|
};
|
||||||
|
|
||||||
|
json!(response)
|
||||||
|
}
|
5
services/api/src/redispool.rs
Normal file
5
services/api/src/redispool.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
use rocket_db_pools::{deadpool_redis, Database};
|
||||||
|
|
||||||
|
#[derive(Database)]
|
||||||
|
#[database("redis")]
|
||||||
|
pub struct RedisPool(deadpool_redis::Pool);
|
214
services/api/src/vehicles.rs
Normal file
214
services/api/src/vehicles.rs
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
use core::time;
|
||||||
|
use rocket_db_pools::deadpool_redis::redis::{pipe, AsyncCommands};
|
||||||
|
use rocket_db_pools::Connection;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::ops::Sub;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
use crate::redispool::RedisPool;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
struct VehiclesCounts {
|
||||||
|
world_id: String,
|
||||||
|
total: u32,
|
||||||
|
vehicles: Vehicles,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
struct Vehicles {
|
||||||
|
flash: u32,
|
||||||
|
sunderer: u32,
|
||||||
|
lightning: u32,
|
||||||
|
scythe: u32,
|
||||||
|
vanguard: u32,
|
||||||
|
prowler: u32,
|
||||||
|
reaver: u32,
|
||||||
|
mosquito: u32,
|
||||||
|
galaxy: u32,
|
||||||
|
valkyrie: u32,
|
||||||
|
liberator: u32,
|
||||||
|
ant: u32,
|
||||||
|
harasser: u32,
|
||||||
|
dervish: u32,
|
||||||
|
chimera: u32,
|
||||||
|
javelin: u32,
|
||||||
|
corsair: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/w/<world_id>/vehicles")]
|
||||||
|
pub async fn get_vehicles(world_id: String, mut con: Connection<RedisPool>) -> serde_json::Value {
|
||||||
|
let cache_key = format!("cache:vehicles:{}", world_id);
|
||||||
|
|
||||||
|
match con.get::<String, String>(cache_key.clone()).await {
|
||||||
|
Ok(cached) => {
|
||||||
|
return serde_json::from_str(&cached).unwrap();
|
||||||
|
}
|
||||||
|
Err(_) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let filter_timestamp = SystemTime::now()
|
||||||
|
.sub(time::Duration::from_secs(60 * 15))
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs();
|
||||||
|
|
||||||
|
// I hate this but it's fast???
|
||||||
|
// The type only allows 12 at a time.
|
||||||
|
let (
|
||||||
|
flash,
|
||||||
|
sunderer,
|
||||||
|
lightning,
|
||||||
|
scythe,
|
||||||
|
vanguard,
|
||||||
|
prowler,
|
||||||
|
reaver,
|
||||||
|
mosquito,
|
||||||
|
galaxy,
|
||||||
|
valkyrie,
|
||||||
|
liberator,
|
||||||
|
ant,
|
||||||
|
): (u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32) = pipe()
|
||||||
|
.zcount(
|
||||||
|
format!("v:{}/{}", world_id, "flash"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.zcount(
|
||||||
|
format!("v:{}/{}", world_id, "sunderer"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.zcount(
|
||||||
|
format!("v:{}/{}", world_id, "lightning"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.zcount(
|
||||||
|
format!("v:{}/{}", world_id, "scythe"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.zcount(
|
||||||
|
format!("v:{}/{}", world_id, "vanguard"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.zcount(
|
||||||
|
format!("v:{}/{}", world_id, "prowler"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.zcount(
|
||||||
|
format!("v:{}/{}", world_id, "reaver"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.zcount(
|
||||||
|
format!("v:{}/{}", world_id, "mosquito"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.zcount(
|
||||||
|
format!("v:{}/{}", world_id, "galaxy"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.zcount(
|
||||||
|
format!("v:{}/{}", world_id, "valkyrie"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.zcount(
|
||||||
|
format!("v:{}/{}", world_id, "liberator"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.zcount(
|
||||||
|
format!("v:{}/{}", world_id, "ant"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.query_async(&mut *con)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (harasser, dervish, chimera, javelin, corsair): (u32, u32, u32, u32, u32) = pipe()
|
||||||
|
.zcount(
|
||||||
|
format!("v:{}/{}", world_id, "harasser"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.zcount(
|
||||||
|
format!("v:{}/{}", world_id, "dervish"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.zcount(
|
||||||
|
format!("v:{}/{}", world_id, "chimera"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.zcount(
|
||||||
|
format!("v:{}/{}", world_id, "javelin"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.zcount(
|
||||||
|
format!("v:{}/{}", world_id, "corsair"),
|
||||||
|
filter_timestamp,
|
||||||
|
"+inf",
|
||||||
|
)
|
||||||
|
.query_async(&mut *con)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let total = flash
|
||||||
|
+ sunderer
|
||||||
|
+ lightning
|
||||||
|
+ scythe
|
||||||
|
+ vanguard
|
||||||
|
+ prowler
|
||||||
|
+ reaver
|
||||||
|
+ mosquito
|
||||||
|
+ galaxy
|
||||||
|
+ valkyrie
|
||||||
|
+ liberator
|
||||||
|
+ ant
|
||||||
|
+ harasser
|
||||||
|
+ dervish
|
||||||
|
+ chimera
|
||||||
|
+ javelin
|
||||||
|
+ corsair;
|
||||||
|
|
||||||
|
let response = VehiclesCounts {
|
||||||
|
world_id,
|
||||||
|
total,
|
||||||
|
vehicles: Vehicles {
|
||||||
|
flash,
|
||||||
|
sunderer,
|
||||||
|
lightning,
|
||||||
|
scythe,
|
||||||
|
vanguard,
|
||||||
|
prowler,
|
||||||
|
reaver,
|
||||||
|
mosquito,
|
||||||
|
galaxy,
|
||||||
|
valkyrie,
|
||||||
|
liberator,
|
||||||
|
ant,
|
||||||
|
harasser,
|
||||||
|
dervish,
|
||||||
|
chimera,
|
||||||
|
javelin,
|
||||||
|
corsair,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let out = json!(response);
|
||||||
|
|
||||||
|
con.set_ex::<String, String, ()>(cache_key, out.to_string(), 5)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
|
@ -21,7 +21,21 @@ fn cmd_prune() {
|
||||||
|
|
||||||
let keys: Vec<String> = con.keys("wp:*").unwrap();
|
let keys: Vec<String> = con.keys("wp:*").unwrap();
|
||||||
for key in keys {
|
for key in keys {
|
||||||
println!("-> Pruning {}", key);
|
println!("-> Pruning world pop {}", key);
|
||||||
|
let removed_items: u64 = con.zrembyscore(key, 0, prune_after).unwrap();
|
||||||
|
println!("==> Removed {} items", removed_items);
|
||||||
|
}
|
||||||
|
|
||||||
|
let keys: Vec<String> = con.keys("v:*").unwrap();
|
||||||
|
for key in keys {
|
||||||
|
println!("-> Pruning vehicle {}", key);
|
||||||
|
let removed_items: u64 = con.zrembyscore(key, 0, prune_after).unwrap();
|
||||||
|
println!("==> Removed {} items", removed_items);
|
||||||
|
}
|
||||||
|
|
||||||
|
let keys: Vec<String> = con.keys("c:*").unwrap();
|
||||||
|
for key in keys {
|
||||||
|
println!("-> Pruning class {}", key);
|
||||||
let removed_items: u64 = con.zrembyscore(key, 0, prune_after).unwrap();
|
let removed_items: u64 = con.zrembyscore(key, 0, prune_after).unwrap();
|
||||||
println!("==> Removed {} items", removed_items);
|
println!("==> Removed {} items", removed_items);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,8 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
redis = "0.22.1"
|
redis = { version = "0.22.1", default_features = false, features = ["r2d2"] }
|
||||||
|
redis_ts = "0.4.2"
|
||||||
once_cell = "1.16.0"
|
once_cell = "1.16.0"
|
||||||
tokio-tungstenite = { version = "0.17.2", features=["native-tls"] }
|
tokio-tungstenite = { version = "0.17.2", features=["native-tls"] }
|
||||||
serde_json = "1.0.88"
|
serde_json = "1.0.88"
|
||||||
|
|
38
services/websocket/event_examples.md
Normal file
38
services/websocket/event_examples.md
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"attacker_character_id": "5429241327001550385",
|
||||||
|
"attacker_loadout_id": "29",
|
||||||
|
"attacker_team_id": "2",
|
||||||
|
"attacker_vehicle_id": "0",
|
||||||
|
"attacker_weapon_id": "6005191",
|
||||||
|
"character_id": "5428257385129506001",
|
||||||
|
"event_name": "VehicleDestroy",
|
||||||
|
"facility_id": "0",
|
||||||
|
"faction_id": "1",
|
||||||
|
"team_id": "1",
|
||||||
|
"timestamp": "1669213115",
|
||||||
|
"vehicle_id": "2",
|
||||||
|
"world_id": "1",
|
||||||
|
"zone_id": "6"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"attacker_character_id": "5428257385129506001",
|
||||||
|
"attacker_fire_mode_id": "80387",
|
||||||
|
"attacker_loadout_id": "15",
|
||||||
|
"attacker_team_id": "1",
|
||||||
|
"attacker_vehicle_id": "0",
|
||||||
|
"attacker_weapon_id": "6003665",
|
||||||
|
"character_id": "5429201282572095697",
|
||||||
|
"character_loadout_id": "6",
|
||||||
|
"event_name": "Death",
|
||||||
|
"is_critical": "0",
|
||||||
|
"is_headshot": "0",
|
||||||
|
"team_id": "2",
|
||||||
|
"timestamp": "1669213113",
|
||||||
|
"world_id": "1",
|
||||||
|
"zone_id": "6"
|
||||||
|
}
|
||||||
|
```
|
|
@ -7,11 +7,17 @@ use serde_json::json;
|
||||||
use std::{env, time::SystemTime};
|
use std::{env, time::SystemTime};
|
||||||
use tokio_tungstenite::{connect_async, tungstenite::Message};
|
use tokio_tungstenite::{connect_async, tungstenite::Message};
|
||||||
|
|
||||||
|
mod translators;
|
||||||
|
|
||||||
pub static REDIS_CLIENT: Lazy<redis::Client> = Lazy::new(|| {
|
pub static REDIS_CLIENT: Lazy<redis::Client> = Lazy::new(|| {
|
||||||
redis::Client::open(std::env::var("REDIS_ADDR").unwrap_or("redis://localhost:6379".to_string()))
|
redis::Client::open(std::env::var("REDIS_ADDR").unwrap_or("redis://localhost:6379".to_string()))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
static PAIR: Lazy<String> = Lazy::new(|| env::var("PAIR").unwrap_or_default());
|
||||||
|
static ROLE: Lazy<String> = Lazy::new(|| env::var("ROLE").unwrap_or("primary".to_string()));
|
||||||
|
static WS_ADDR: Lazy<String> = Lazy::new(|| env::var("WS_ADDR").unwrap_or_default());
|
||||||
|
|
||||||
async fn send_init(tx: futures::channel::mpsc::UnboundedSender<Message>) {
|
async fn send_init(tx: futures::channel::mpsc::UnboundedSender<Message>) {
|
||||||
let worlds_raw = env::var("WORLDS").unwrap_or_default();
|
let worlds_raw = env::var("WORLDS").unwrap_or_default();
|
||||||
if worlds_raw == "" {
|
if worlds_raw == "" {
|
||||||
|
@ -36,22 +42,166 @@ async fn send_init(tx: futures::channel::mpsc::UnboundedSender<Message>) {
|
||||||
println!("Sent setup message");
|
println!("Sent setup message");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_event(event: &Event) {
|
struct PopEvent {
|
||||||
|
world_id: String,
|
||||||
|
team_id: String,
|
||||||
|
character_id: String,
|
||||||
|
timestamp: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VehicleEvent {
|
||||||
|
world_id: String,
|
||||||
|
vehicle_id: String,
|
||||||
|
character_id: String,
|
||||||
|
timestamp: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ClassEvent {
|
||||||
|
world_id: String,
|
||||||
|
character_id: String,
|
||||||
|
loadout_id: String,
|
||||||
|
timestamp: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn track_pop(pop_event: PopEvent) {
|
||||||
let mut con = REDIS_CLIENT.get_connection().unwrap();
|
let mut con = REDIS_CLIENT.get_connection().unwrap();
|
||||||
|
|
||||||
|
let PopEvent {
|
||||||
|
world_id,
|
||||||
|
team_id,
|
||||||
|
character_id,
|
||||||
|
timestamp,
|
||||||
|
} = pop_event;
|
||||||
|
|
||||||
|
let key = format!("wp:{}/{}", world_id, team_id);
|
||||||
|
let _: () = con.zadd(key, character_id, timestamp).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn track_vehicle(vehicle_event: VehicleEvent) {
|
||||||
|
let mut con = REDIS_CLIENT.get_connection().unwrap();
|
||||||
|
|
||||||
|
let VehicleEvent {
|
||||||
|
world_id,
|
||||||
|
vehicle_id,
|
||||||
|
timestamp,
|
||||||
|
character_id,
|
||||||
|
} = vehicle_event;
|
||||||
|
|
||||||
|
let vehicle_name = translators::vehicle_to_name(vehicle_id.as_str());
|
||||||
|
|
||||||
|
if vehicle_name == "unknown" {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = format!("v:{}/{}", world_id, vehicle_name);
|
||||||
|
let _: () = con.zadd(key, character_id, timestamp).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn track_class(class_event: ClassEvent) {
|
||||||
|
let mut con = REDIS_CLIENT.get_connection().unwrap();
|
||||||
|
|
||||||
|
let ClassEvent {
|
||||||
|
world_id,
|
||||||
|
character_id,
|
||||||
|
loadout_id,
|
||||||
|
timestamp,
|
||||||
|
} = class_event;
|
||||||
|
|
||||||
|
let class_name = translators::loadout_to_class(loadout_id.as_str());
|
||||||
|
|
||||||
|
if class_name == "unknown" {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = format!("c:{}/{}", world_id, class_name);
|
||||||
|
let _: () = con.zadd(key, character_id, timestamp).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_process_event() -> bool {
|
||||||
|
let mut con = REDIS_CLIENT.get_connection().unwrap();
|
||||||
|
let role: String = ROLE.parse().unwrap();
|
||||||
|
let heartbeat_key = format!("heartbeat:{}", PAIR.to_string());
|
||||||
|
|
||||||
|
if role == "primary" {
|
||||||
|
let _: () = con.set_ex(heartbeat_key, "1", 60).unwrap();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
match con.get(heartbeat_key) {
|
||||||
|
Ok(1) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_event(event: &Event) {
|
||||||
|
if should_process_event() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let timestamp = SystemTime::now()
|
let timestamp = SystemTime::now()
|
||||||
.duration_since(SystemTime::UNIX_EPOCH)
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_secs();
|
.as_secs();
|
||||||
|
|
||||||
let key: String = format!("wp:{}/{}", event.world_id, event.team_id);
|
// General population tracking
|
||||||
con.zadd::<String, u64, String, ()>(key, event.character_id.clone(), timestamp)
|
track_pop(PopEvent {
|
||||||
.unwrap();
|
world_id: event.world_id.clone(),
|
||||||
|
team_id: event.team_id.clone(),
|
||||||
|
character_id: event.character_id.clone(),
|
||||||
|
timestamp,
|
||||||
|
})
|
||||||
|
.now_or_never();
|
||||||
|
|
||||||
if event.attacker_character_id != "" {
|
if event.event_name == "VehicleDestroy" {
|
||||||
let key = format!("wp:{}/{}", event.world_id, event.attacker_team_id);
|
track_vehicle(VehicleEvent {
|
||||||
con.zadd::<String, u64, String, ()>(key, event.attacker_character_id.clone(), timestamp)
|
world_id: event.world_id.clone(),
|
||||||
.unwrap();
|
vehicle_id: event.vehicle_id.clone(),
|
||||||
|
character_id: event.character_id.clone(),
|
||||||
|
timestamp,
|
||||||
|
})
|
||||||
|
.now_or_never();
|
||||||
|
}
|
||||||
|
|
||||||
|
if event.event_name == "Death" {
|
||||||
|
track_class(ClassEvent {
|
||||||
|
world_id: event.world_id.clone(),
|
||||||
|
character_id: event.character_id.clone(),
|
||||||
|
loadout_id: event.loadout_id.clone(),
|
||||||
|
timestamp,
|
||||||
|
})
|
||||||
|
.now_or_never();
|
||||||
|
}
|
||||||
|
|
||||||
|
if event.attacker_character_id != ""
|
||||||
|
&& (event.attacker_team_id != "" || event.attacker_team_id != "0")
|
||||||
|
{
|
||||||
|
track_pop(PopEvent {
|
||||||
|
world_id: event.world_id.clone(),
|
||||||
|
team_id: event.attacker_team_id.clone(),
|
||||||
|
character_id: event.attacker_character_id.clone(),
|
||||||
|
timestamp,
|
||||||
|
})
|
||||||
|
.now_or_never();
|
||||||
|
|
||||||
|
if event.event_name == "VehicleDestroy" {
|
||||||
|
track_vehicle(VehicleEvent {
|
||||||
|
world_id: event.world_id.clone(),
|
||||||
|
vehicle_id: event.attacker_vehicle_id.clone(),
|
||||||
|
character_id: event.attacker_character_id.clone(),
|
||||||
|
timestamp,
|
||||||
|
})
|
||||||
|
.now_or_never();
|
||||||
|
}
|
||||||
|
|
||||||
|
if event.event_name == "Death" {
|
||||||
|
track_class(ClassEvent {
|
||||||
|
world_id: event.world_id.clone(),
|
||||||
|
character_id: event.attacker_character_id.clone(),
|
||||||
|
loadout_id: event.attacker_loadout_id.clone(),
|
||||||
|
timestamp,
|
||||||
|
})
|
||||||
|
.now_or_never();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +213,18 @@ struct Event {
|
||||||
attacker_character_id: String,
|
attacker_character_id: String,
|
||||||
attacker_team_id: String,
|
attacker_team_id: String,
|
||||||
team_id: String,
|
team_id: String,
|
||||||
|
|
||||||
|
// Class Tracking
|
||||||
|
#[serde(default)]
|
||||||
|
attacker_loadout_id: String,
|
||||||
|
#[serde(default)]
|
||||||
|
loadout_id: String,
|
||||||
|
|
||||||
|
// Vehicle Tracking
|
||||||
|
#[serde(default)]
|
||||||
|
vehicle_id: String,
|
||||||
|
#[serde(default)]
|
||||||
|
attacker_vehicle_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
|
@ -72,12 +234,13 @@ struct Payload {
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let addr = env::var("WS_ADDR").unwrap_or_default();
|
let addr: String = WS_ADDR.to_string();
|
||||||
if addr == "" {
|
if addr == "" {
|
||||||
println!("WS_ADDR not set");
|
println!("WS_ADDR not set");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let url = url::Url::parse(&addr).unwrap();
|
let url = url::Url::parse(&addr).unwrap();
|
||||||
|
|
||||||
let (tx, rx) = futures::channel::mpsc::unbounded();
|
let (tx, rx) = futures::channel::mpsc::unbounded();
|
||||||
let (ws_stream, _) = connect_async(url).await.expect("Failed to connect");
|
let (ws_stream, _) = connect_async(url).await.expect("Failed to connect");
|
||||||
let (write, read) = ws_stream.split();
|
let (write, read) = ws_stream.split();
|
||||||
|
@ -85,6 +248,8 @@ async fn main() {
|
||||||
let fused_writer = rx.map(Ok).forward(write).fuse();
|
let fused_writer = rx.map(Ok).forward(write).fuse();
|
||||||
let fused_reader = read
|
let fused_reader = read
|
||||||
.for_each(|msg| async move {
|
.for_each(|msg| async move {
|
||||||
|
// println!("Processing event: {:?}", msg);
|
||||||
|
|
||||||
let body = &msg.unwrap().to_string();
|
let body = &msg.unwrap().to_string();
|
||||||
let data: Payload = serde_json::from_str(body).unwrap_or(Payload {
|
let data: Payload = serde_json::from_str(body).unwrap_or(Payload {
|
||||||
payload: Event {
|
payload: Event {
|
||||||
|
@ -94,6 +259,10 @@ async fn main() {
|
||||||
attacker_character_id: "".to_string(),
|
attacker_character_id: "".to_string(),
|
||||||
attacker_team_id: "".to_string(),
|
attacker_team_id: "".to_string(),
|
||||||
team_id: "".to_string(),
|
team_id: "".to_string(),
|
||||||
|
attacker_loadout_id: "".to_string(),
|
||||||
|
loadout_id: "".to_string(),
|
||||||
|
vehicle_id: "".to_string(),
|
||||||
|
attacker_vehicle_id: "".to_string(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
99
services/websocket/src/translators.rs
Normal file
99
services/websocket/src/translators.rs
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
// GENERATED CODE -- Do not edit. Run `node hack/codegen/codegen.js > services/websocket/src/translators.rs` to regenerate.
|
||||||
|
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
static VEHICLE_TO_NAME: Lazy<HashMap<&str, &str>> = Lazy::new(|| {
|
||||||
|
HashMap::from([
|
||||||
|
("1", "flash"),
|
||||||
|
("2", "sunderer"),
|
||||||
|
("3", "lightning"),
|
||||||
|
("5", "vanguard"),
|
||||||
|
("6", "prowler"),
|
||||||
|
("7", "scythe"),
|
||||||
|
("8", "reaver"),
|
||||||
|
("9", "mosquito"),
|
||||||
|
("10", "liberator"),
|
||||||
|
("11", "galaxy"),
|
||||||
|
("12", "harasser"),
|
||||||
|
("14", "valkyrie"),
|
||||||
|
("15", "ant"),
|
||||||
|
("100", "ant"),
|
||||||
|
("101", "ant"),
|
||||||
|
("102", "ant"),
|
||||||
|
("150", "ant"),
|
||||||
|
("151", "ant"),
|
||||||
|
("160", "ant"),
|
||||||
|
("161", "ant"),
|
||||||
|
("162", "ant"),
|
||||||
|
("1001", "flash"),
|
||||||
|
("1002", "sunderer"),
|
||||||
|
("1005", "vanguard"),
|
||||||
|
("1007", "scythe"),
|
||||||
|
("1008", "reaver"),
|
||||||
|
("1009", "mosquito"),
|
||||||
|
("1010", "liberator"),
|
||||||
|
("1011", "galaxy"),
|
||||||
|
("1105", "vanguard"),
|
||||||
|
("2006", "ant"),
|
||||||
|
("2009", "ant"),
|
||||||
|
("2010", "flash"),
|
||||||
|
("2033", "javelin"),
|
||||||
|
("2122", "mosquito"),
|
||||||
|
("2123", "reaver"),
|
||||||
|
("2124", "scythe"),
|
||||||
|
("2125", "javelin"),
|
||||||
|
("2129", "javelin"),
|
||||||
|
("2130", "sunderer"),
|
||||||
|
("2131", "galaxy"),
|
||||||
|
("2132", "valkyrie"),
|
||||||
|
("2134", "vanguard"),
|
||||||
|
("2135", "prowler"),
|
||||||
|
("2136", "dervish"),
|
||||||
|
("2137", "chimera"),
|
||||||
|
("2142", "corsair"),
|
||||||
|
])
|
||||||
|
});
|
||||||
|
|
||||||
|
pub fn vehicle_to_name(vehicle_id: &str) -> String {
|
||||||
|
match VEHICLE_TO_NAME.get(&vehicle_id) {
|
||||||
|
Some(name) => name.to_string(),
|
||||||
|
None => "unknown".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static LOADOUT_TO_CLASS: Lazy<HashMap<&str, &str>> = Lazy::new(|| {
|
||||||
|
HashMap::from([
|
||||||
|
("1", "infiltrator"),
|
||||||
|
("3", "light_assault"),
|
||||||
|
("4", "combat_medic"),
|
||||||
|
("5", "engineer"),
|
||||||
|
("6", "heavy_assault"),
|
||||||
|
("7", "max"),
|
||||||
|
("8", "infiltrator"),
|
||||||
|
("10", "light_assault"),
|
||||||
|
("11", "combat_medic"),
|
||||||
|
("12", "engineer"),
|
||||||
|
("13", "heavy_assault"),
|
||||||
|
("14", "max"),
|
||||||
|
("15", "infiltrator"),
|
||||||
|
("17", "light_assault"),
|
||||||
|
("18", "combat_medic"),
|
||||||
|
("19", "engineer"),
|
||||||
|
("20", "heavy_assault"),
|
||||||
|
("21", "max"),
|
||||||
|
("28", "infiltrator"),
|
||||||
|
("29", "light_assault"),
|
||||||
|
("30", "combat_medic"),
|
||||||
|
("31", "engineer"),
|
||||||
|
("32", "heavy_assault"),
|
||||||
|
("45", "max"),
|
||||||
|
])
|
||||||
|
});
|
||||||
|
|
||||||
|
pub fn loadout_to_class(loadout_id: &str) -> String {
|
||||||
|
match LOADOUT_TO_CLASS.get(&loadout_id) {
|
||||||
|
Some(name) => name.to_string(),
|
||||||
|
None => "unknown".to_string(),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue