Skip to main content

nil_server/server/
local.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4use 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")]
18#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
19#[cfg_attr(feature = "typescript", ts(export))]
20pub struct LocalServer {
21  world: WorldId,
22  addr: SocketAddrV4,
23
24  #[serde(skip_serializing)]
25  #[cfg_attr(feature = "typescript", ts(skip))]
26  handle: AbortHandle,
27}
28
29impl LocalServer {
30  async fn serve(world: World) -> Result<Self> {
31    let (listener, mut addr) = super::bind(0).await?;
32    if addr.ip().is_unspecified() {
33      addr.set_ip(Ipv4Addr::LOCALHOST);
34    }
35
36    let world_id = world.id();
37    let router = router::create()
38      .with_state(App::new_local(world))
39      .into_make_service_with_connect_info::<SocketAddr>();
40
41    let task = spawn(async move {
42      axum::serve(listener, router)
43        .await
44        .expect("Failed to start Call of Nil server");
45    });
46
47    Ok(Self {
48      world: world_id,
49      addr,
50      handle: task.abort_handle(),
51    })
52  }
53
54  #[inline]
55  pub fn world(&self) -> WorldId {
56    self.world
57  }
58
59  #[inline]
60  pub fn addr(&self) -> SocketAddrV4 {
61    self.addr
62  }
63
64  #[inline]
65  pub fn stop(self) {
66    self.handle.abort();
67  }
68}
69
70pub async fn start(options: WorldOptions) -> Result<LocalServer> {
71  LocalServer::serve(options.try_into()?).await
72}
73
74pub async fn load(path: impl AsRef<Path>) -> Result<LocalServer> {
75  let bytes = fs::read(path).await?;
76  let world = spawn_blocking(move || World::load(&bytes))
77    .await
78    .map_err(|_| CoreError::FailedToReadSavedata)??;
79
80  LocalServer::serve(world).await
81}
82
83pub(crate) async fn save(mut dir: PathBuf, bytes: Vec<u8>) {
84  let result = try {
85    fs::create_dir_all(&dir).await?;
86    dir.push(format!("{}.nil", Uuid::now_v7()));
87    fs::write(&dir, bytes).await?;
88  };
89
90  if let Err(err) = result {
91    tracing::error!(message = %err, error = ?err);
92  }
93}