This commit is contained in:
Aubrey 2024-12-21 23:37:16 -06:00
parent b5c340c104
commit f1194c752c
No known key found for this signature in database
4 changed files with 104 additions and 42 deletions

View file

@ -15,7 +15,7 @@
nixpkgs, nixpkgs,
flake-utils, flake-utils,
fenix, fenix,
crane crane,
}: }:
flake-utils.lib.eachDefaultSystem flake-utils.lib.eachDefaultSystem
( (
@ -30,10 +30,12 @@
strictDeps = true; strictDeps = true;
nativeBuildInputs = with pkgs; [pkg-config cmake]; nativeBuildInputs = with pkgs; [pkg-config cmake];
buildInputs = with pkgs; [ buildInputs = with pkgs;
[
openssl openssl
zlib-ng zlib-ng
] ++ lib.optionals stdenv.isDarwin [ ]
++ lib.optionals stdenv.isDarwin [
libiconv libiconv
]; ];
}; };
@ -47,9 +49,66 @@
]; ];
}; };
formatter = pkgs.alejandra; formatter = pkgs.alejandra;
packages.default = craneLib.buildPackage (commonArgs // { packages.default = craneLib.buildPackage (commonArgs
// {
cargoArtifacts = craneLib.buildDepsOnly commonArgs; cargoArtifacts = craneLib.buildDepsOnly commonArgs;
}); });
nixosModules.default = with lib; { config, ... }: {
options.services.smo-server = {
enable = mkEnableOption "a game server for Super Mario Odyssey Online";
user = mkOption {
type = lib.types.string;
description = "The user to start the server with";
};
enableFaker = mkOption {
type = lib.types.bool;
default = false;
example = true;
description = "Whether to enable the test bot for solo development.";
};
tcpPort = mkOption {
type = lib.types.port;
default = 1027;
description = "The TCP port to host the server on";
};
udpPort = mkOption {
type = lib.types.port;
default = 1027;
description = "The UDP port to host the server on";
};
proximity = {
type = lib.types.attrs;
port = {
type = lib.types.port;
example = 4433;
description = "The UDP port to host the proximity chat server on";
};
certPath = {
type = lib.types.path;
example = "cert.pem";
description = "The certificate used for encrypting the WebTransport stream";
};
keyPath = {
type = lib.types.path;
example = "cert.pem";
description = "The certificate used for encrypting the WebTransport stream";
};
};
};
config = mkIf config.services.smo-server.enable {
systemd.services.smo-server = with services.smo-server; {
wantedBy = ["multi-user.target"];
after = ["network.target"];
description = "Start smo-server";
serviceConfig = {
WorkingDirectory = "${packages.default.outPath}";
Type = "simple";
ExecStart = let proxRes = 5;
in ''${packages.default.outPath}/bin/smo-server -t ${tcpPort} -u ${udpPort} ${proxRes}'';
};
};
};
};
} }
); );
} }

View file

@ -39,17 +39,18 @@ pub fn manager() -> &'static Address<Manager> {
} }
#[derive(clap::Parser)] #[derive(clap::Parser)]
#[command(about = "A game server for Super Mario Odyssey Online")]
struct Arguments { struct Arguments {
#[arg(short, long, default_value_t = 1027)] #[arg(short, long, default_value_t = 1027)]
tcp_port: u16, tcp_port: u16,
#[arg(short, long, default_value_t = 1027)] #[arg(short, long, default_value_t = 1027)]
udp_port: u16, udp_port: u16,
#[arg(short, long, default_value_t = 4433)] #[arg(short, long, requires = "prox_cert", requires = "prox_key")]
prox_port: u16, prox_port: Option<u16>,
#[arg(long, default_value = "./cert.pem")] #[arg(long)]
prox_cert: PathBuf, prox_cert: Option<PathBuf>,
#[arg(long, default_value = "./key.pem")] #[arg(long)]
prox_key: PathBuf, prox_key: Option<PathBuf>,
#[arg(long)] #[arg(long)]
enable_faker: bool, enable_faker: bool,
} }

View file

@ -22,22 +22,32 @@ fn listeners() -> &'static RwLock<HashMap<UuidString, Address<ProximityPlayer>>>
&LISTENERS &LISTENERS
} }
pub async fn web_main(port: u16, cert: PathBuf, key: PathBuf) -> Address<Manager> { pub async fn web_main(port: Option<u16>, cert: Option<PathBuf>, key: Option<PathBuf>) -> Address<Manager> {
let span = info_span!("prox"); let span = info_span!("prox");
let endpoint = if let Some(port) = port {
let (cert, key) = (cert.unwrap(), key.unwrap()); // guarded and enforced to be fine by clap
info!(parent: &span, "reading pems from {{ cert: {cert:?}, key: {key:?} }}"); info!(parent: &span, "reading pems from {{ cert: {cert:?}, key: {key:?} }}");
let identity = Identity::load_pemfiles(cert, key).await.expect("failed to create identity from proximity pems"); let identity = Identity::load_pemfiles(cert, key).await.expect("failed to create identity from proximity pems");
let config = ServerConfig::builder().with_bind_default(port).with_identity(identity).build(); let config = ServerConfig::builder().with_bind_default(port).with_identity(identity).build();
let endpoint = Endpoint::server(config).expect("failed to build proximity endpoint"); let endpoint = Endpoint::server(config).expect("failed to build proximity endpoint");
info!("listening on webtransport port {port}"); info!(parent: &span, "listening on webtransport port {port}");
Some(endpoint)
} else {
info!(parent: &span, "no port specified, not starting server");
None
};
let manager = xtra::spawn_tokio( let manager = xtra::spawn_tokio(
Manager { Manager {
players: HashMap::new(), players: HashMap::new(),
next_id: 0, next_id: 0,
}, },
Mailbox::bounded(8), Mailbox::bounded(32),
); );
if let Some(endpoint) = endpoint {
tokio::spawn({ tokio::spawn({
let manager = manager.clone(); let manager = manager.clone();
async move { async move {
@ -49,6 +59,7 @@ pub async fn web_main(port: u16, cert: PathBuf, key: PathBuf) -> Address<Manager
} }
.instrument(span) .instrument(span)
}); });
}
manager manager
} }

View file

@ -33,7 +33,6 @@ impl ProximityPlayer {
); );
let (mut send, mut recv) = connection.accept_bi().await.expect("failed to start channel"); let (mut send, mut recv) = connection.accept_bi().await.expect("failed to start channel");
trace!("getting uuid and name");
let mut id = UuidString::new_zeroed(); let mut id = UuidString::new_zeroed();
recv.read_exact(id.as_mut_bytes()).await.expect("failed to read uuid"); recv.read_exact(id.as_mut_bytes()).await.expect("failed to read uuid");
let mut name = String::new_zeroed(); let mut name = String::new_zeroed();
@ -46,7 +45,7 @@ impl ProximityPlayer {
send.write_u8(state.len() as u8).await.expect("failed to write length"); send.write_u8(state.len() as u8).await.expect("failed to write length");
for player in state.values() { for player in state.values() {
trace!("sending player {player:?}"); trace!(parent: &span, "sending player {player:?}");
send.write_all(player.as_bytes()).await.expect("failed to write player"); send.write_all(player.as_bytes()).await.expect("failed to write player");
} }
@ -78,7 +77,6 @@ impl ProximityPlayer {
loop { loop {
match packet::Event::deserialize(&mut recv).await { match packet::Event::deserialize(&mut recv).await {
Ok(event) => { Ok(event) => {
info!("deserialized event: {event:?}");
let _ = address.send(event).detach().await; let _ = address.send(event).detach().await;
} }
Err(error) => { Err(error) => {
@ -359,7 +357,6 @@ impl Handler<ChangedStage> for ProximityPlayer {
type Return = (); type Return = ();
async fn handle(&mut self, message: ChangedStage, ctx: &mut xtra::Context<Self>) -> Self::Return { async fn handle(&mut self, message: ChangedStage, ctx: &mut xtra::Context<Self>) -> Self::Return {
warn!("todo: implement changed stage");
let event = packet::Packet { let event = packet::Packet {
kind: packet::Kind::StageChanged, kind: packet::Kind::StageChanged,
data: packet::StageChanged { data: packet::StageChanged {
@ -381,7 +378,6 @@ pub mod packet {
use anyhow::bail; use anyhow::bail;
use newtype_enum::newtype_enum; use newtype_enum::newtype_enum;
use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tracing::info;
use wtransport::{RecvStream, SendStream}; use wtransport::{RecvStream, SendStream};
use zerocopy::{FromZeros, Immutable, IntoBytes}; use zerocopy::{FromZeros, Immutable, IntoBytes};
use Event_variants::{Answer, Candidate, Offer, TargetChanged}; use Event_variants::{Answer, Candidate, Offer, TargetChanged};
@ -478,8 +474,6 @@ pub mod packet {
Ok(()) Ok(())
} }
info!("writing event: {self:?}");
match self { match self {
Self::Offer(offer) => { Self::Offer(offer) => {
send.write_u8(Kind::Offer as u8).await?; send.write_u8(Kind::Offer as u8).await?;
@ -504,7 +498,6 @@ pub mod packet {
pub async fn deserialize(recv: &mut RecvStream) -> anyhow::Result<Self> { pub async fn deserialize(recv: &mut RecvStream) -> anyhow::Result<Self> {
async fn read_string(recv: &mut RecvStream) -> anyhow::Result<StdString> { async fn read_string(recv: &mut RecvStream) -> anyhow::Result<StdString> {
let size = recv.read_u32_le().await?; let size = recv.read_u32_le().await?;
info!("string size: {size}");
let mut data = vec![0; size as usize]; let mut data = vec![0; size as usize];
recv.read_exact(&mut data).await?; recv.read_exact(&mut data).await?;
Ok(StdString::from_utf8(data)?) Ok(StdString::from_utf8(data)?)
@ -512,12 +505,10 @@ pub mod packet {
async fn read_fixed_string<const N: usize>(recv: &mut RecvStream) -> anyhow::Result<String<N>> { async fn read_fixed_string<const N: usize>(recv: &mut RecvStream) -> anyhow::Result<String<N>> {
let mut str = String::new_zeroed(); let mut str = String::new_zeroed();
recv.read_exact(str.as_mut_bytes()).await?; recv.read_exact(str.as_mut_bytes()).await?;
info!("read id: {str}");
Ok(str) Ok(str)
} }
let kind = recv.read_u8().await?; let kind = recv.read_u8().await?;
info!("reading kind: {kind}");
let event = match kind { let event = match kind {
0 => Self::Offer(Offer { 0 => Self::Offer(Offer {
id: read_fixed_string(recv).await?, id: read_fixed_string(recv).await?,