nil_server/server/
local.rs1use crate::app::App;
5use crate::error::{CoreError, Result};
6use crate::router;
7use nil_core::world::config::WorldId;
8use nil_core::world::{World, WorldOptions};
9use serde::Serialize;
10use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
11use std::path::{Path, PathBuf};
12use tokio::fs;
13use tokio::task::{AbortHandle, spawn, spawn_blocking};
14use uuid::Uuid;
15
16#[derive(Clone, Debug, Serialize)]
17#[serde(rename_all = "camelCase")]
18pub struct LocalServer {
19 world: WorldId,
20 addr: SocketAddrV4,
21
22 #[serde(skip_serializing)]
23 handle: AbortHandle,
24}
25
26impl LocalServer {
27 async fn serve(world: World) -> Result<Self> {
28 let (listener, mut addr) = super::bind(0).await?;
29 if addr.ip().is_unspecified() {
30 addr.set_ip(Ipv4Addr::LOCALHOST);
31 }
32
33 let world_id = world.id();
34 let router = router::create()
35 .with_state(App::new_local(world))
36 .into_make_service_with_connect_info::<SocketAddr>();
37
38 let task = spawn(async move {
39 axum::serve(listener, router)
40 .await
41 .expect("Failed to start Call of Nil server");
42 });
43
44 Ok(Self {
45 world: world_id,
46 addr,
47 handle: task.abort_handle(),
48 })
49 }
50
51 #[inline]
52 pub fn world(&self) -> WorldId {
53 self.world
54 }
55
56 #[inline]
57 pub fn addr(&self) -> SocketAddrV4 {
58 self.addr
59 }
60
61 #[inline]
62 pub fn stop(self) {
63 self.handle.abort();
64 }
65}
66
67pub async fn start(options: &WorldOptions) -> Result<LocalServer> {
68 LocalServer::serve(options.try_into()?).await
69}
70
71pub async fn load(path: impl AsRef<Path>) -> Result<LocalServer> {
72 let bytes = fs::read(path).await?;
73 let world = spawn_blocking(move || World::load(&bytes))
74 .await
75 .map_err(|_| CoreError::FailedToReadSavedata)??;
76
77 LocalServer::serve(world).await
78}
79
80pub(crate) async fn save(mut dir: PathBuf, bytes: Vec<u8>) {
81 let result = try {
82 fs::create_dir_all(&dir).await?;
83 dir.push(format!("{}.nil", Uuid::now_v7()));
84 fs::write(&dir, bytes).await?;
85 };
86
87 if let Err(err) = result {
88 tracing::error!(message = %err, error = ?err);
89 }
90}