#![feature( generators, generator_trait, type_alias_impl_trait, impl_trait_in_assoc_type )] mod core; mod prelude; mod projections; mod protos; mod util; use std::future::Future; use figment::providers::Format; use figment::{providers::Toml, Figment}; use prometheus::Registry as MetricsRegistry; use serde::Deserialize; use crate::core::player::PlayerRegistry; use crate::core::repo::Storage; use crate::core::room::RoomRegistry; use crate::prelude::*; #[derive(Deserialize, Debug)] struct ServerConfig { telemetry: util::telemetry::ServerConfig, irc: projections::irc::ServerConfig, xmpp: projections::xmpp::ServerConfig, storage: core::repo::StorageConfig, } fn load_config() -> Result { let raw_config = Figment::new().merge(Toml::file("config.toml")); let config: ServerConfig = raw_config.extract()?; Ok(config) } #[tokio::main] async fn main() -> Result<()> { set_up_logging()?; let sleep = ctrl_c()?; let config = load_config()?; tracing::info!("Booting up"); tracing::info!("Loaded config: {config:?}"); let ServerConfig { telemetry: telemetry_config, irc: irc_config, xmpp: xmpp_config, storage: storage_config, } = config; let mut metrics = MetricsRegistry::new(); let storage = Storage::open(storage_config).await?; let rooms = RoomRegistry::new(&mut metrics, storage.clone())?; let mut players = PlayerRegistry::empty(&rooms, &metrics)?; // unsafe: outer future is never dropped, scope is joined on `scope.collect` let mut scope = unsafe { Scope::create() }; let telemetry_terminator = util::telemetry::launch(telemetry_config, &metrics, &rooms, &mut scope).await?; let irc = projections::irc::launch(&irc_config, &players, &rooms, &metrics, &storage, &mut scope).await?; let xmpp = projections::xmpp::launch(xmpp_config, &players, &rooms, &metrics, &mut scope).await?; tracing::info!("Started"); sleep.await; tracing::info!("Begin shutdown"); let _ = xmpp.send(()); let _ = irc.send(()); let _ = telemetry_terminator.send(()); let _ = scope.collect().await; drop(scope); players.shutdown_all().await?; drop(players); drop(rooms); storage.close().await?; tracing::info!("Shutdown complete"); Ok(()) } #[cfg(windows)] fn ctrl_c() -> Result> { use tokio::signal::windows::*; let chan = ctrl_c()?; async fn recv(mut chan: CtrlC) { let _ = chan.recv().await; } Ok(recv(chan)) } #[cfg(unix)] fn ctrl_c() -> Result> { use tokio::signal::unix::*; let chan = signal(SignalKind::interrupt())?; async fn recv(mut chan: Signal) { let _ = chan.recv().await; } Ok(recv(chan)) } fn set_up_logging() -> Result<()> { tracing_subscriber::fmt::init(); Ok(()) }