diff options
Diffstat (limited to 'server/replaytool/src/render.rs')
-rw-r--r-- | server/replaytool/src/render.rs | 97 |
1 files changed, 77 insertions, 20 deletions
diff --git a/server/replaytool/src/render.rs b/server/replaytool/src/render.rs index bb5e8a57..2314de79 100644 --- a/server/replaytool/src/render.rs +++ b/server/replaytool/src/render.rs @@ -17,40 +17,93 @@ */ use crate::replay::replay; -use anyhow::Result; +use anyhow::{anyhow, Result}; use log::info; -use std::path::Path; -use tokio::{net::TcpListener, process::Command}; +use rand::random; +use std::{path::PathBuf, str::FromStr}; +use tokio::{ + fs::{create_dir_all, remove_dir, remove_file, File}, + io::AsyncWriteExt, + net::TcpListener, + process::Command, +}; -pub async fn render( - input: &Path, - output: &Path, +#[derive(clap::Parser)] +pub struct RenderArgs { + /// Replay file path + input: PathBuf, + /// Output video file path passed to godot (must end in either .avi for MJPEG or .png for PNG sequence) + output: PathBuf, + #[arg(short = 'r', long, default_value = "30")] framerate: usize, + #[arg(short = 'R', long, default_value = "1280x720")] + resolution: String, + /// Render without display server; Requires wlheadless-run and mutter + #[arg(short = 'H', long)] headless: bool, - rendering_driver: &str, - video_driver: &str, - headless_compositor: &str, -) -> Result<()> { + #[arg(long, default_value = "wayland")] + video_driver: String, + #[arg(long, default_value = "vulkan")] + rendering_driver: String, + #[arg(long, default_value = "mutter")] + headless_compositor: String, + #[arg(long, default_value = "/usr/share/hurrycurry/client.pck")] + client_pck: PathBuf, +} + +pub async fn render(a: RenderArgs) -> Result<()> { let port = 27090; let ws_listener = TcpListener::bind(("127.0.0.1", port)).await?; + let cwd = PathBuf::from_str("/tmp") + .unwrap() + .join(format!("hurrycurry-render-cfg-{:016x}", random::<u64>())); + + let config = { + let (width, height) = a + .resolution + .split_once("x") + .ok_or(anyhow!("resolution malformed"))?; + format!( + r#"config_version=5 +[display] +window/size/viewport_width={width} +window/size/viewport_height={height} +"#, + ) + }; + + create_dir_all(&cwd).await?; + File::create(cwd.join("override.cfg")) + .await? + .write_all(config.as_bytes()) + .await?; + + #[cfg(unix)] + tokio::fs::symlink(&a.client_pck, cwd.join("client.pck")).await?; + #[cfg(not(unix))] + tokio::fs::copy(&a.client_pck, cwd.join("client.pck")).await?; + let mut args = Vec::new(); - if headless { + if a.headless { args.push("wlheadless-run"); - args.extend(["-c", headless_compositor]); + args.extend(["-c", &a.headless_compositor]); args.push("--"); } - if headless && video_driver == "x11" { + if a.headless && a.video_driver == "x11" { args.push("xwayland-run"); args.push("--"); } - args.push("hurrycurry-client"); - if headless { - args.extend(["--video-driver", video_driver]); - args.extend(["--rendering-driver", rendering_driver]); + args.push("godot"); + let main_pack = cwd.join("client.pck"); + let main_pack = main_pack.to_str().unwrap(); + args.extend(["--main-pack", main_pack]); + if a.headless { + args.extend(["--video-driver", &a.video_driver]); + args.extend(["--rendering-driver", &a.rendering_driver]); } - args.extend(["--write-movie", output.to_str().unwrap()]); - let fps = framerate.to_string(); + args.extend(["--write-movie", a.output.to_str().unwrap()]); + let fps = a.framerate.to_string(); args.extend(["--fixed-fps", &fps]); args.push("--print-fps"); args.push("--"); @@ -61,9 +114,13 @@ pub async fn render( let mut client = Command::new(args[0]).args(&args[1..]).spawn()?; info!("listening for websockets on {}", ws_listener.local_addr()?); - replay(&ws_listener, &input).await?; + replay(&ws_listener, &a.input).await?; client.wait().await?.exit_ok()?; + remove_file(cwd.join("override.cfg")).await?; + remove_file(cwd.join("client.pck")).await?; + remove_dir(cwd).await?; + Ok(()) } |