finish service

This commit is contained in:
41666 2023-06-09 05:46:11 -04:00
parent 83ad349f30
commit 6aac273b9a
10 changed files with 214 additions and 41 deletions

2
.dockerignore Normal file
View file

@ -0,0 +1,2 @@
target
.git

23
.github/workflows/ci.yaml vendored Normal file
View file

@ -0,0 +1,23 @@
name: "CI"
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Log in to the Container registry
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- run: docker buildx create --use --driver=docker-container
- run: |
TAG_LATEST_IF_MASTER=$(if [ "$GITHUB_REF_NAME" = "main" ]; then echo "-t ghcr.io/${{ github.repository }}/agg-population:latest"; else echo ""; fi)
docker buildx build . \
-t ghcr.io/${{ github.repository }}/agg-population:${{ github.sha }} $TAG_LATEST_IF_MASTER \
--push \
--cache-to type=gha,scope=$GITHUB_REF_NAME-agg-population \
--cache-from type=gha,scope=$GITHUB_REF_NAME-agg-population

3
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
"editor.tabSize": 4
}

10
Cargo.lock generated
View file

@ -102,6 +102,15 @@ version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bitflags"
version = "1.3.2"
@ -604,6 +613,7 @@ name = "metagame"
version = "0.1.0"
dependencies = [
"axum",
"bincode",
"chrono",
"lazy_static",
"openssl",

View file

@ -18,4 +18,5 @@ tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
chrono = { version = "0.4", features = ["serde"] }
lazy_static = "1.4"
serde-aux = "4"
serde-aux = "4"
bincode = "1.3"

27
nomad/metagame.nomad.hcl Normal file
View file

@ -0,0 +1,27 @@
job "metagame" {
type = "service"
update {
max_parallel = 1
stagger = "10s"
}
group "api" {
count = 1
network {
port "http" {
static = 8067
}
}
task "api" {
driver = "docker"
config {
image = "ghcr.io/genudine/metagame/metagame:latest"
ports = ["http"]
}
}
}
}

View file

@ -1,19 +1,13 @@
use crate::{alert_types::alert_type, misc};
use chrono::{DateTime, TimeZone, Utc};
use serde::{Deserialize, Serialize};
use crate::{
alert_types::alert_type,
misc,
types::{Alert, FactionPercents},
};
use chrono::{TimeZone, Utc};
use serde::Deserialize;
use serde_aux::prelude::*;
use std::collections::HashMap;
#[derive(Serialize)]
pub struct Alert {
pub id: i32,
pub zone: i32,
pub end_time: Option<DateTime<Utc>>,
pub start_time: Option<DateTime<Utc>>,
pub alert_type: String,
pub ps2alerts: String,
}
pub async fn get_alerts(world_id: i32) -> Result<Vec<Alert>, ()> {
let response = reqwest::get(format!(
"https://census.daybreakgames.com/s:{}/get/{}/world_event/?world_id={}&type=METAGAME&c:limit=10",
@ -42,6 +36,11 @@ pub async fn get_alerts(world_id: i32) -> Result<Vec<Alert>, ()> {
"https://ps2alerts.com/alert/{}-{}",
world_id, world_event.id
),
percentages: FactionPercents {
nc: world_event.faction_nc,
tr: world_event.faction_tr,
vs: world_event.faction_vs,
},
});
if world_event.metagame_event_state_name == "started" {
@ -81,4 +80,10 @@ struct WorldEvent {
timestamp: i64,
#[serde(deserialize_with = "deserialize_number_from_string")]
zone_id: i32,
#[serde(deserialize_with = "deserialize_number_from_string")]
faction_nc: f32,
#[serde(deserialize_with = "deserialize_number_from_string")]
faction_tr: f32,
#[serde(deserialize_with = "deserialize_number_from_string")]
faction_vs: f32,
}

View file

@ -1,8 +1,16 @@
#![feature(slice_group_by)]
use axum::{extract::Path, response::Html, routing::get, Json, Router};
use alerts::get_alerts;
use axum::{
extract::{Path, State},
response::Html,
routing::get,
Json, Router,
};
use std::{env, net::SocketAddr};
use types::{FactionPercents, World, Zone};
use tokio::task::JoinSet;
use tracing::Level;
use types::{World, Zone};
use zones::get_zone_states;
mod alert_types;
@ -13,12 +21,16 @@ mod zones;
#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();
tracing_subscriber::fmt()
.with_max_level(Level::DEBUG)
.init();
let app = Router::new()
.layer(tower_http::trace::TraceLayer::new_for_http())
.route("/:world", get(get_world))
.route("/", get(root));
.route("/:world", get(get_one_world))
// .route("/all", get(get_all_worlds))
.route("/", get(root))
.with_state(sled::open("/tmp/metagame").expect("open"));
let addr = SocketAddr::from((
[127, 0, 0, 1],
@ -38,10 +50,79 @@ async fn root() -> Html<&'static str> {
Html(include_str!("./html/index.html"))
}
#[axum::debug_handler]
pub async fn get_world(Path(world): Path<i32>) -> Json<World> {
Json(World {
id: world,
zones: get_zone_states(world).await.unwrap(),
})
pub async fn get_one_world(State(db): State<sled::Db>, Path(world): Path<i32>) -> Json<World> {
Json(get_world(db, world).await)
}
pub async fn get_all_worlds(State(db): State<sled::Db>) -> Json<Vec<World>> {
let mut set = JoinSet::new();
let mut worlds = vec![World::default(); 8];
for world in vec![1, 10, 13, 17, 19, 40, 1000, 2000] {
set.spawn(get_world(db.clone(), world));
}
let mut i = 0;
while let Some(response) = set.join_next().await {
worlds[i] = response.unwrap_or_default();
i += 1;
}
Json(worlds)
}
pub async fn get_world(db: sled::Db, world: i32) -> World {
match world_from_cache(db.clone(), world) {
Ok(response) => return response,
_ => {}
}
let alerts = get_alerts(world).await.unwrap();
let zones = get_zone_states(world).await.unwrap();
let converged_zones: Vec<Zone> = zones
.into_iter()
.map(|zone| {
let mut zone = zone;
let alert = alerts.iter().find(|alert| alert.zone == zone.id);
zone.alert = alert.cloned();
zone
})
.collect();
let response = World {
id: world,
zones: converged_zones,
cached_at: chrono::Utc::now(),
};
world_to_cache(db, world, &response);
response
}
fn world_from_cache(db: sled::Db, world: i32) -> Result<World, ()> {
let key = format!("world:{}", world);
let value = match db.get(key) {
Ok(Some(value)) => value,
_ => return Err(()),
};
match bincode::deserialize::<World>(&value) {
Ok(response) => {
if response.cached_at + chrono::Duration::minutes(3) < chrono::Utc::now() {
return Err(());
}
Ok(response)
}
_ => Err(()),
}
}
fn world_to_cache(db: sled::Db, world: i32, response: &World) {
let key = format!("world:{}", world);
let value = bincode::serialize(response).unwrap();
db.insert(key, value).unwrap();
}

View file

@ -1,31 +1,33 @@
use serde::Serialize;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Serialize)]
#[derive(Deserialize, Serialize, Clone, Default)]
pub struct World {
pub id: i32,
pub zones: Vec<Zone>,
pub cached_at: DateTime<Utc>,
}
#[derive(Serialize)]
#[derive(Deserialize, Serialize, Clone, Default)]
pub struct Zone {
pub id: i32,
pub locked: bool,
pub alert: Option<Alert>,
pub faction_control: FactionPercents,
pub territory: FactionPercents,
}
#[derive(Serialize)]
#[derive(Deserialize, Serialize, Clone, Default)]
pub struct Alert {
pub id: i32,
#[serde(rename = "type")]
pub zone: i32,
pub end_time: Option<DateTime<Utc>>,
pub start_time: Option<DateTime<Utc>>,
pub alert_type: String,
pub start: i64,
pub end: i64,
pub score: FactionPercents,
pub ps2alerts: String,
pub percentages: FactionPercents,
}
#[derive(Serialize)]
#[derive(Deserialize, Serialize, Clone, Default)]
pub struct FactionPercents {
pub vs: f32,
pub nc: f32,

View file

@ -3,7 +3,7 @@ use crate::{
types::{FactionPercents, Zone},
};
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
use serde::Deserialize;
use serde_aux::prelude::*;
use std::collections::HashMap;
@ -48,7 +48,7 @@ pub async fn get_zone_states(world_id: i32) -> Result<Vec<Zone>, ()> {
id: map_zone.zone_id,
locked: warpgate_factions[0] == warpgate_factions[1]
&& warpgate_factions[1] == warpgate_factions[2],
faction_control: calculate_faction_percents(&map_zone.regions.row),
territory: calculate_faction_percents(&map_zone.regions.row),
alert: None,
};
@ -61,17 +61,36 @@ pub async fn get_zone_states(world_id: i32) -> Result<Vec<Zone>, ()> {
fn calculate_faction_percents(regions: &Vec<MapRegionRowData>) -> FactionPercents {
let groups = regions.group_by(|a, b| a.row_data.faction_id == b.row_data.faction_id);
let mut faction_percents = FactionPercents {
struct FactionTotals {
vs: f32,
nc: f32,
tr: f32,
}
let mut faction_totals = FactionTotals {
vs: 0.0,
nc: 0.0,
tr: 0.0,
};
for faction in groups {
faction.
for row in groups {
let faction_id = row[0].row_data.faction_id;
match faction_id {
1 => faction_totals.vs += 1.0,
2 => faction_totals.nc += 1.0,
3 => faction_totals.tr += 1.0,
_ => (),
}
}
faction_percents
let total = faction_totals.vs + faction_totals.nc + faction_totals.tr;
FactionPercents {
vs: faction_totals.vs / total * 100.0,
nc: faction_totals.nc / total * 100.0,
tr: faction_totals.tr / total * 100.0,
}
}
#[derive(Deserialize)]