mc-tcpmux 0.1.0

A TCP port multiplexer for Minecraft servers.
use std::path::PathBuf;

use clap::{arg, command};
use tokio::{net::TcpListener, sync::mpsc};
use tracing::{Instrument, error, info, info_span};

use crate::{config::Config, handler::handle_connection, watcher::watch_file};

mod config;
mod handler;
mod io;
mod watcher;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
	tracing_subscriber::fmt::init();

	let matches = command!()
		.arg(arg!([config] "Path to the config file").default_value("config.toml"))
		.arg(arg!(-r --reload "Reload the config file when it changes. Note this only applies to new established connections."))
		.get_matches();

	let config_path = PathBuf::from(matches.get_one::<String>("config").unwrap());
	let reload = *matches.get_one::<bool>("reload").unwrap();

	let mut config = Config::load(&config_path)?;
	let listener = TcpListener::bind((config.host.as_str(), config.port)).await?;

	let (tx, mut rx) = mpsc::channel(16);
	if reload {
		info!("Watching for changes in {}", config_path.display());
		let config_path0 = config_path.clone();
		tokio::spawn(async move {
			watch_file(&config_path0, 500, tx).await.unwrap();
		});
	}

	loop {
		let recv_fut = rx.recv();
		tokio::pin!(recv_fut);

		tokio::select! {
			res = listener.accept() => {
				let (socket, addr) = res?;
				info!("Accepted connection from {}", addr);

				let config = config.clone();
				tokio::spawn(async move {
					handle_connection(socket, &config).await;
				}.instrument(info_span!("handle_conn", addr = %addr)));
			}
			_ = recv_fut => {
				match Config::load(&config_path) {
					Ok(new_config) => {
						config = new_config;
						info!("Config reloaded successfully");
					}
					Err(e) => {
						error!("Failed to reload config: {}", e);
					}
				}
			}
		}
	}
}