/* Hurry Curry! - a game about cooking Copyright 2024 metamuffin This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License only. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ use crate::server::Server; use anyhow::{bail, Result}; use hurrycurry_protocol::{registry::Submission, VERSION}; use log::{info, warn}; use rand::random; use reqwest::{header::USER_AGENT, Client, Url}; use std::{ net::{IpAddr, Ipv4Addr, Ipv6Addr}, str::FromStr, sync::Arc, time::Duration, }; use tokio::{sync::RwLock, time::interval}; const REGISTRY_URI: &'static str = "https://hurrycurry-registry.metamuffin.org"; pub struct Register { name: String, port: u16, register_uri: Option, state: Arc>, inet_client: Client, ip4_client: Client, ip6_client: Client, secret: u128, players: usize, } impl Register { pub fn new( name: String, port: u16, register_uri: Option, state: Arc>, ) -> Self { Self { name, register_uri, players: 0, port, secret: random(), state, inet_client: Client::new(), ip4_client: Client::builder() .local_address(IpAddr::V4(Ipv4Addr::UNSPECIFIED)) .build() .unwrap(), ip6_client: Client::builder() .local_address(IpAddr::V6(Ipv6Addr::UNSPECIFIED)) .build() .unwrap(), } } pub async fn register_loop(mut self) { let mut interval = interval(Duration::from_secs(60)); loop { interval.tick().await; self.players = self.state.read().await.count_chefs(); if let Err(e) = self.register().await { warn!("register error: {e}") } } } pub async fn register(&self) -> Result<()> { info!("register update"); if let Some(uri) = &self.register_uri { self.register_with("uri", &self.inet_client, uri.to_owned()) .await?; } else { let (v4, v6) = tokio::join!( self.register_with( "ip4", &self.ip4_client, format!("ws://0.0.0.0:{}", self.port), ), self.register_with( "ip6", &self.ip6_client, format!("ws://0.0.0.0:{}", self.port), ) ); info!("v4: {v4:?}"); info!("v6: {v6:?}"); } Ok(()) } // TODO ip v6 pub async fn register_with(&self, label: &str, client: &Client, uri: String) -> Result<()> { let res = client .post(Url::from_str(&format!("{REGISTRY_URI}/v1/register")).unwrap()) .header( USER_AGENT, format!("hurrycurry-server {}", env!("CARGO_PKG_VERSION")), ) .json(&Submission { last_game: 0, name: self.name.clone(), uri, players: self.players, secret: self.secret, version: VERSION, }) .send() .await?; let r = res.text().await?; if r == "ok" { info!("register ok ({label})"); Ok(()) } else { bail!("{r}"); } } }