use concinnity_cook::authoring::world::find_world_jsonl;
use concinnity_engine::app::run::LaunchRequest;
use concinnity_engine::app::runtime::Runtime;
use crate::debug::hot_reload::WorldPathHandle;
use crate::debug_hook::DebugHook;
pub fn run_debug(launch: LaunchRequest, json_path: Option<&str>, port: u16) -> std::io::Result<()> {
concinnity_engine::app::run::init_logging();
let world_path = resolve_world_path(json_path)?;
let debug_hook: Box<dyn DebugHook> = match crate::debug::DebugServer::start(port) {
Ok(srv) => Box::new(srv.with_world_path(WorldPathHandle::new(world_path.as_str()))),
Err(e) => {
eprintln!("error: could not start debug server: {e}");
return Err(e);
}
};
run_interpreted(launch, &world_path, Some(debug_hook))
}
fn resolve_world_path(json_path: Option<&str>) -> std::io::Result<String> {
match json_path {
Some(p) if std::path::Path::new(p).exists() => Ok(p.to_string()),
Some(p) => Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("world file not found: {p}"),
)),
None => find_world_jsonl(crate::project::worlds_dir().as_deref(), None),
}
}
pub(crate) fn run_interpreted(
launch: LaunchRequest,
json_path: &str,
debug: Option<Box<dyn DebugHook>>,
) -> std::io::Result<()> {
let mut runtime = crate::project::runtime().with_launch(launch);
*runtime.world_mut() = crate::authoring::build_world_from_path(json_path).map_err(|e| {
tracing::error!("Could not build world from {json_path}: {e}");
e
})?;
start_app(runtime, debug)
}
pub(crate) fn start_app(
mut runtime: Runtime,
mut debug: Option<Box<dyn DebugHook>>,
) -> std::io::Result<()> {
use concinnity_engine::app::runloop;
let shutdown = runtime.shutdown_token();
runloop::install_ctrlc_handler(&runtime);
if let Some(hook) = debug.as_mut() {
hook.attach_shutdown(shutdown.clone());
}
#[cfg(target_os = "macos")]
let renders = runtime.render_mode().renders();
#[cfg(target_os = "macos")]
if renders {
runloop::activate_app_macos();
}
if let Err(e) = runtime.start() {
tracing::error!("failed to start app: {e}");
return Err(std::io::Error::other(format!("failed to start app: {e}")));
}
let on_tick = |runtime: &mut Runtime| {
if let Some(hook) = debug.as_deref_mut() {
hook.tick(runtime.world_mut());
hook.apply_world_swap(runtime);
}
};
#[cfg(target_os = "macos")]
runloop::run_loop(&mut runtime, renders, on_tick);
#[cfg(not(target_os = "macos"))]
runloop::run_loop(&mut runtime, false, on_tick);
Ok(())
}
#[cfg(test)]
mod tests {
use super::resolve_world_path;
#[test]
fn an_existing_path_is_taken_as_given() {
let dir = tempfile::tempdir().expect("temp dir");
let world = dir.path().join("scene.jsonl");
std::fs::write(&world, "").expect("write");
let given = world.to_string_lossy().into_owned();
assert_eq!(resolve_world_path(Some(&given)).expect("resolves"), given);
}
#[test]
fn a_missing_path_errors_rather_than_falling_back() {
let dir = tempfile::tempdir().expect("temp dir");
let missing = dir.path().join("absent.jsonl");
let given = missing.to_string_lossy().into_owned();
let err = resolve_world_path(Some(&given)).expect_err("missing world is an error");
assert_eq!(err.kind(), std::io::ErrorKind::NotFound);
assert!(err.to_string().contains("absent.jsonl"), "{err}");
}
}