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,
flake-utils,
fenix,
crane
crane,
}:
flake-utils.lib.eachDefaultSystem
(
@ -30,12 +30,14 @@
strictDeps = true;
nativeBuildInputs = with pkgs; [pkg-config cmake];
buildInputs = with pkgs; [
openssl
zlib-ng
] ++ lib.optionals stdenv.isDarwin [
libiconv
];
buildInputs = with pkgs;
[
openssl
zlib-ng
]
++ lib.optionals stdenv.isDarwin [
libiconv
];
};
in
with pkgs; {
@ -47,9 +49,66 @@
];
};
formatter = pkgs.alejandra;
packages.default = craneLib.buildPackage (commonArgs // {
cargoArtifacts = craneLib.buildDepsOnly commonArgs;
});
packages.default = craneLib.buildPackage (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)]
#[command(about = "A game server for Super Mario Odyssey Online")]
struct Arguments {
#[arg(short, long, default_value_t = 1027)]
tcp_port: u16,
#[arg(short, long, default_value_t = 1027)]
udp_port: u16,
#[arg(short, long, default_value_t = 4433)]
prox_port: u16,
#[arg(long, default_value = "./cert.pem")]
prox_cert: PathBuf,
#[arg(long, default_value = "./key.pem")]
prox_key: PathBuf,
#[arg(short, long, requires = "prox_cert", requires = "prox_key")]
prox_port: Option<u16>,
#[arg(long)]
prox_cert: Option<PathBuf>,
#[arg(long)]
prox_key: Option<PathBuf>,
#[arg(long)]
enable_faker: bool,
}

View file

@ -22,33 +22,44 @@ fn listeners() -> &'static RwLock<HashMap<UuidString, Address<ProximityPlayer>>>
&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");
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 config = ServerConfig::builder().with_bind_default(port).with_identity(identity).build();
let endpoint = Endpoint::server(config).expect("failed to build proximity endpoint");
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:?} }}");
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 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(
Manager {
players: HashMap::new(),
next_id: 0,
},
Mailbox::bounded(8),
Mailbox::bounded(32),
);
tokio::spawn({
let manager = manager.clone();
async move {
loop {
let connection = endpoint.accept().await;
ProximityPlayer::spawn(connection, manager.clone());
if let Some(endpoint) = endpoint {
tokio::spawn({
let manager = manager.clone();
async move {
loop {
let connection = endpoint.accept().await;
ProximityPlayer::spawn(connection, manager.clone());
}
}
}
.instrument(span)
});
.instrument(span)
});
}
manager
}

View file

@ -33,7 +33,6 @@ impl ProximityPlayer {
);
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();
recv.read_exact(id.as_mut_bytes()).await.expect("failed to read uuid");
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");
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");
}
@ -78,7 +77,6 @@ impl ProximityPlayer {
loop {
match packet::Event::deserialize(&mut recv).await {
Ok(event) => {
info!("deserialized event: {event:?}");
let _ = address.send(event).detach().await;
}
Err(error) => {
@ -359,7 +357,6 @@ impl Handler<ChangedStage> for ProximityPlayer {
type Return = ();
async fn handle(&mut self, message: ChangedStage, ctx: &mut xtra::Context<Self>) -> Self::Return {
warn!("todo: implement changed stage");
let event = packet::Packet {
kind: packet::Kind::StageChanged,
data: packet::StageChanged {
@ -381,7 +378,6 @@ pub mod packet {
use anyhow::bail;
use newtype_enum::newtype_enum;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tracing::info;
use wtransport::{RecvStream, SendStream};
use zerocopy::{FromZeros, Immutable, IntoBytes};
use Event_variants::{Answer, Candidate, Offer, TargetChanged};
@ -478,8 +474,6 @@ pub mod packet {
Ok(())
}
info!("writing event: {self:?}");
match self {
Self::Offer(offer) => {
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> {
async fn read_string(recv: &mut RecvStream) -> anyhow::Result<StdString> {
let size = recv.read_u32_le().await?;
info!("string size: {size}");
let mut data = vec![0; size as usize];
recv.read_exact(&mut data).await?;
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>> {
let mut str = String::new_zeroed();
recv.read_exact(str.as_mut_bytes()).await?;
info!("read id: {str}");
Ok(str)
}
let kind = recv.read_u8().await?;
info!("reading kind: {kind}");
let event = match kind {
0 => Self::Offer(Offer {
id: read_fixed_string(recv).await?,