veloren/voxygen/src/singleplayer.rs

192 lines
5.8 KiB
Rust
Raw Normal View History

use client::Client;
2019-06-05 15:22:06 +00:00
use common::clock::Clock;
use crossbeam::channel::{bounded, unbounded, Receiver, Sender, TryRecvError};
use server::{Error as ServerError, Event, Input, Server};
use std::{
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
thread::{self, JoinHandle},
time::Duration,
};
use tracing::{error, info, warn};
const TPS: u64 = 30;
enum Msg {
Stop,
}
/// Used to start and stop the background thread running the server
/// when in singleplayer mode.
pub struct Singleplayer {
2019-07-02 21:29:38 +00:00
_server_thread: JoinHandle<()>,
sender: Sender<Msg>,
pub receiver: Receiver<Result<(), ServerError>>,
// Wether the server is stopped or not
paused: Arc<AtomicBool>,
}
impl Singleplayer {
pub fn new(client: Option<&Client>) -> (Self, server::Settings) {
let (sender, receiver) = unbounded();
// Determine folder to save server data in
let server_data_dir = {
let mut path = common::userdata_dir_workspace!();
path.push("singleplayer");
path
};
// Copy saves from old folder if they don't exist in the new location
(|| {
let new_path = server_data_dir.join("saves");
if new_path.exists() {
return;
}
let working_dir = std::path::PathBuf::from("saves");
let config_dir = directories_next::ProjectDirs::from("net", "veloren", "voxygen")
.expect("System's $HOME directory path not found!")
.config_dir()
.join("saves");
let old_path = if working_dir.exists() {
working_dir
} else if config_dir.exists() {
config_dir
} else {
return;
};
info!(
"Saves folder doesn't exist, but there is one in the old saves location, copying \
it to the new location"
);
if let Some(parent) = new_path.parent() {
if let Err(e) = std::fs::create_dir_all(parent) {
error!(?e, "Could not create folder to hold saves folder.");
return;
}
}
if let Err(e) = copy_dir::copy_dir(old_path, new_path) {
error!(?e, "Failed to copy saves from the old location");
}
})();
// Create server
let settings = server::Settings::singleplayer(&server_data_dir);
let editable_settings = server::EditableSettings::singleplayer(&server_data_dir);
let thread_pool = client.map(|c| c.thread_pool().clone());
let settings2 = settings.clone();
let paused = Arc::new(AtomicBool::new(false));
let paused1 = Arc::clone(&paused);
let (result_sender, result_receiver) = bounded(1);
let thread = thread::spawn(move || {
2020-08-12 14:43:08 +00:00
let mut server = None;
if let Err(e) = result_sender.send(
match Server::new(settings2, editable_settings, &server_data_dir) {
Ok(s) => {
server = Some(s);
Ok(())
},
Err(e) => Err(e),
},
) {
2020-08-12 14:43:08 +00:00
warn!(
?e,
"Failed to send singleplayer server initialization result. Most likely the \
channel was closed by cancelling server creation. Stopping Server"
);
return;
};
let server = match server {
Some(s) => s,
None => return,
};
let server = match thread_pool {
Some(pool) => server.with_thread_pool(pool),
None => server,
};
run_server(server, receiver, paused1);
});
(
Singleplayer {
2019-07-02 21:29:38 +00:00
_server_thread: thread,
sender,
receiver: result_receiver,
paused,
},
settings,
)
}
/// Returns wether or not the server is paused
2020-03-03 21:26:59 +00:00
pub fn is_paused(&self) -> bool { self.paused.load(Ordering::SeqCst) }
/// Pauses if true is passed and unpauses if false (Does nothing if in that
/// state already)
pub fn pause(&self, state: bool) { self.paused.store(state, Ordering::SeqCst); }
}
impl Drop for Singleplayer {
fn drop(&mut self) {
// Ignore the result
let _ = self.sender.send(Msg::Stop);
}
}
fn run_server(mut server: Server, rec: Receiver<Msg>, paused: Arc<AtomicBool>) {
info!("Starting server-cli...");
// Set up an fps clock
let mut clock = Clock::start();
2019-07-05 11:58:52 +00:00
loop {
// Check any event such as stopping and pausing
match rec.try_recv() {
Ok(msg) => match msg {
Msg::Stop => break,
},
Err(err) => match err {
TryRecvError::Empty => (),
TryRecvError::Disconnected => break,
},
}
// Wait for the next tick.
clock.tick(Duration::from_millis(1000 / TPS));
// Skip updating the server if it's paused
if paused.load(Ordering::SeqCst) && server.number_of_players() < 2 {
continue;
} else if server.number_of_players() > 1 {
paused.store(false, Ordering::SeqCst);
}
let events = server
.tick(Input::default(), clock.get_last_delta())
.expect("Failed to tick server!");
for event in events {
match event {
2019-07-02 21:29:38 +00:00
Event::ClientConnected { .. } => info!("Client connected!"),
Event::ClientDisconnected { .. } => info!("Client disconnected!"),
Event::Chat { entity: _, msg } => info!("[Client] {}", msg),
}
}
// Clean up the server after a tick.
server.cleanup();
}
}