api skeleton and docs
This commit is contained in:
parent
3100948f3a
commit
aff10840fd
2 changed files with 124 additions and 43 deletions
91
README.md
91
README.md
|
@ -2,56 +2,73 @@
|
||||||
|
|
||||||
PlanetSide 2 live population API. This API is free and open for anyone to use.
|
PlanetSide 2 live population API. This API is free and open for anyone to use.
|
||||||
|
|
||||||
The one and only goal of this app is to provide a current "point-in-time" population status for PlanetSide 2, per world, per faction, (and later, per continent.) Historical info is _not_ a goal.
|
|
||||||
|
|
||||||
https://saerro.harasse.rs
|
https://saerro.harasse.rs
|
||||||
|
|
||||||
|
Our methodology is to add any player ID seen on the Census websockets to a time-sorted set, and returning the number of player IDs seen within 15 minutes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
The one and only goal of this app is to provide a current "point-in-time" population status for PlanetSide 2, per world, per faction, (and later, per continent.) Historical info is _not_ a goal; you may implement this on your end.
|
||||||
|
|
||||||
|
Please open an issue here or get in touch with Pomf (okano#0001) on the PS2 Discord if you have complex use cases for this data; it may be trivial/easy to implement APIs tailored to your needs.
|
||||||
|
|
||||||
|
The main use case is for [Medkit](https://github.com/kayteh/medkit2) bot to have an in-house source of population data, without relying too heavily on any third-party stats service, like Fisu, Honu, or Voidwell; which all have different population tracking needs and goals (and thus, different data.)
|
||||||
|
|
||||||
## API Reference
|
## API Reference
|
||||||
|
|
||||||
- [`/`](https://saerro.harasse.rs) - Shows a help/usage message.
|
This API will never return a failure unless the app itself is failing. If you request a world ID that doesn't exist, it only sees an empty set, and will not 404. Since 0 can both mean a server is down and it doesn't exist, be mindful of the data. It could even mean the Websockets have failed.
|
||||||
- [`/w/{worldID}`](https://saerro.harasse.rs/w/17) - Shows populations for one world, example:
|
|
||||||
|
|
||||||
```json
|
This API only supports GET, and supports CORS.
|
||||||
{
|
|
||||||
"worldID": 17,
|
- [`/`](https://saerro.harasse.rs) - Shows a help/usage message.
|
||||||
"total": 1000,
|
|
||||||
"factions": {
|
```json
|
||||||
"tr": 334,
|
{
|
||||||
"nc": 333,
|
"worldID": 17,
|
||||||
"vs": 333
|
"total": 1000,
|
||||||
|
"factions": {
|
||||||
|
"tr": 334,
|
||||||
|
"nc": 333,
|
||||||
|
"vs": 333
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
```
|
||||||
```
|
|
||||||
|
|
||||||
- [`/m/?id={worldID1},{worldID2}...`](https://saerro.harasse.rs/m/?id=1,17) - Shows populations for all listed worlds, example:
|
- [`/m/?id={worldID1},{worldID2}...`](https://saerro.harasse.rs/m/?id=1,17) - Shows populations for all listed worlds, example:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"worlds": [
|
"worlds": [
|
||||||
{
|
{
|
||||||
"worldID": 17,
|
"worldID": 17,
|
||||||
"total": 1000,
|
"total": 1000,
|
||||||
"factions": {
|
"factions": {
|
||||||
"tr": 334,
|
"tr": 334,
|
||||||
"nc": 333,
|
"nc": 333,
|
||||||
"vs": 333
|
"vs": 333
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"worldID": 19,
|
||||||
|
"total": 1000,
|
||||||
|
"factions": {
|
||||||
|
"tr": 334,
|
||||||
|
"nc": 333,
|
||||||
|
"vs": 333
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
]
|
||||||
{
|
}
|
||||||
"worldID": 19,
|
```
|
||||||
"total": 1000,
|
|
||||||
"factions": {
|
|
||||||
"tr": 334,
|
|
||||||
"nc": 333,
|
|
||||||
"vs": 333
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
- Websocket processors
|
- Websocket processors
|
||||||
- One pair per PC, PS4US, PS4EU
|
- One pair per PC, PS4US, PS4EU
|
||||||
- Each pair connects to either Census Websocket or NS Websocket, depending on availability.
|
- Each pair connects to either Census Websocket or NS Websocket, depending on availability.
|
||||||
|
- API
|
||||||
|
- Serves https://saerro.harasse.rs
|
||||||
|
- Redis
|
||||||
|
- Using ZADD with score as timestamp, ZCOUNTBYSCORE by timestamp in 15 minute windows, and cleaned up with SCAN+ZREMBYSCORE, population data is tracked.
|
||||||
|
- Redis "Tender"
|
||||||
|
- Cleans up Redis every 5 mins.
|
||||||
|
|
|
@ -5,16 +5,35 @@ use serde::{Deserialize, Serialize};
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct IncomingHeaders {
|
struct IncomingHeaders {
|
||||||
host: String,
|
host: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
struct Factions {
|
||||||
|
tr: u32,
|
||||||
|
nc: u32,
|
||||||
|
vs: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
struct WorldPopulation {
|
||||||
|
world_id: u32,
|
||||||
|
total: u32,
|
||||||
|
factions: Factions,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
struct MultipleWorldPopulation {
|
||||||
|
worlds: Vec<WorldPopulation>,
|
||||||
|
}
|
||||||
|
|
||||||
#[handler]
|
#[handler]
|
||||||
async fn info(req: &mut Request, res: &mut Response) {
|
async fn info(req: &mut Request, res: &mut Response) {
|
||||||
let headers: IncomingHeaders = req.parse_headers().unwrap();
|
let headers: IncomingHeaders = req.parse_headers().unwrap();
|
||||||
let json = json!({
|
let json = json!({
|
||||||
"@": "Saerro Listening Post",
|
"@": "Saerro Listening Post - PlanetSide 2 Live Population API",
|
||||||
"@GitHub": "https://github.com/genudine/saerro",
|
"@GitHub": "https://github.com/genudine/saerro",
|
||||||
"@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",
|
||||||
|
@ -34,15 +53,60 @@ async fn info(req: &mut Request, res: &mut Response) {
|
||||||
res.render(serde_json::to_string_pretty(&json).unwrap());
|
res.render(serde_json::to_string_pretty(&json).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[handler]
|
||||||
|
async fn get_world(req: &mut Request, res: &mut Response) {
|
||||||
|
let world_id: String = req.param("worldID").unwrap();
|
||||||
|
let response = get_world_pop(world_id).await;
|
||||||
|
|
||||||
|
res.render(Json(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[handler]
|
||||||
|
async fn get_world_multi(req: &mut Request, res: &mut Response) {
|
||||||
|
let world_ids_raw = req.query::<String>("ids").unwrap();
|
||||||
|
let world_ids: Vec<&str> = world_ids_raw.split(",").collect();
|
||||||
|
|
||||||
|
let mut response = MultipleWorldPopulation { worlds: Vec::new() };
|
||||||
|
|
||||||
|
for world_id in world_ids {
|
||||||
|
response
|
||||||
|
.worlds
|
||||||
|
.push(get_world_pop(world_id.to_string()).await);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.render(Json(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_world_pop(world_id: String) -> WorldPopulation {
|
||||||
|
let response = WorldPopulation {
|
||||||
|
world_id: world_id.parse().unwrap(),
|
||||||
|
total: 0,
|
||||||
|
factions: Factions {
|
||||||
|
tr: 0,
|
||||||
|
nc: 0,
|
||||||
|
vs: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
response
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
let port = ::std::env::var("PORT").unwrap_or("7878".to_string());
|
||||||
|
let addr = format!("127.0.0.1:{}", port);
|
||||||
|
|
||||||
let cors_handler = Cors::builder()
|
let cors_handler = Cors::builder()
|
||||||
.allow_any_origin()
|
.allow_any_origin()
|
||||||
.allow_method("GET")
|
.allow_method("GET")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let router = Router::new().hoop(cors_handler).get(info);
|
println!("Listening on http://localhost:{}", port);
|
||||||
Server::new(TcpListener::bind("127.0.0.1:7878"))
|
|
||||||
.serve(router)
|
let router = Router::new()
|
||||||
.await;
|
.hoop(cors_handler)
|
||||||
|
.push(Router::with_path("/").get(info))
|
||||||
|
.push(Router::with_path("/w/<worldID>").get(get_world))
|
||||||
|
.push(Router::with_path("/m/").get(get_world_multi));
|
||||||
|
Server::new(TcpListener::bind(&addr)).serve(router).await;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue