flakage
This commit is contained in:
parent
b5c340c104
commit
f1194c752c
79
flake.nix
79
flake.nix
|
@ -15,7 +15,7 @@
|
||||||
nixpkgs,
|
nixpkgs,
|
||||||
flake-utils,
|
flake-utils,
|
||||||
fenix,
|
fenix,
|
||||||
crane
|
crane,
|
||||||
}:
|
}:
|
||||||
flake-utils.lib.eachDefaultSystem
|
flake-utils.lib.eachDefaultSystem
|
||||||
(
|
(
|
||||||
|
@ -30,12 +30,14 @@
|
||||||
strictDeps = true;
|
strictDeps = true;
|
||||||
|
|
||||||
nativeBuildInputs = with pkgs; [pkg-config cmake];
|
nativeBuildInputs = with pkgs; [pkg-config cmake];
|
||||||
buildInputs = with pkgs; [
|
buildInputs = with pkgs;
|
||||||
openssl
|
[
|
||||||
zlib-ng
|
openssl
|
||||||
] ++ lib.optionals stdenv.isDarwin [
|
zlib-ng
|
||||||
libiconv
|
]
|
||||||
];
|
++ lib.optionals stdenv.isDarwin [
|
||||||
|
libiconv
|
||||||
|
];
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
with pkgs; {
|
with pkgs; {
|
||||||
|
@ -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}'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
13
src/main.rs
13
src/main.rs
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,33 +22,44 @@ 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");
|
||||||
info!(parent: &span, "reading pems from {{ cert: {cert:?}, key: {key:?} }}");
|
let endpoint = if let Some(port) = port {
|
||||||
let identity = Identity::load_pemfiles(cert, key).await.expect("failed to create identity from proximity pems");
|
let (cert, key) = (cert.unwrap(), key.unwrap()); // guarded and enforced to be fine by clap
|
||||||
let config = ServerConfig::builder().with_bind_default(port).with_identity(identity).build();
|
info!(parent: &span, "reading pems from {{ cert: {cert:?}, key: {key:?} }}");
|
||||||
let endpoint = Endpoint::server(config).expect("failed to build proximity endpoint");
|
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(
|
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),
|
||||||
);
|
);
|
||||||
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
|
manager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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?,
|
||||||
|
|
Loading…
Reference in a new issue