forked from feather-rs/feather
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlib.rs
More file actions
142 lines (123 loc) · 4.25 KB
/
lib.rs
File metadata and controls
142 lines (123 loc) · 4.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//! Feather, a Minecraft server implementation in Rust.
//!
//! For extensive developer documentation, please see [the book](https://feather-rs.github.io/book).
use feather_server_chunk::ChunkWorkerHandle;
use feather_server_lighting::LightingWorkerHandle;
use feather_server_types::{Game, ShutdownChannels, TPS};
use fecs::{Executor, OwnedResources, ResourcesProvider, World};
use spin_sleep::LoopHelper;
use std::ops::Deref;
use std::panic::AssertUnwindSafe;
use std::process::exit;
use std::sync::Arc;
use tokio::runtime;
mod event_handlers;
mod init;
mod shutdown;
mod systems;
struct FullState {
resources: Arc<OwnedResources>,
world: World,
executor: Executor,
shutdown_rx: crossbeam::Receiver<()>,
}
pub async fn main(runtime: runtime::Handle) {
log::info!("Starting Feather; please wait");
let (executor, resources, world) = match init::init(runtime).await {
Ok(res) => res,
Err(e) => {
// Logging might not have been initialized yet - init it and ignore errors
let _ = simple_logger::init();
log::error!("Failed to start server: {:?}", e);
log::error!("Exiting");
exit(1);
}
};
// Shutdown channels from wrapper resource are used to notify server thread of shutdown
let (shutdown_tx, shutdown_rx) = {
let channels = resources.get::<ShutdownChannels>();
(channels.tx.clone(), channels.rx.clone())
};
shutdown::init(shutdown_tx);
let state = FullState {
resources,
executor,
world,
shutdown_rx,
};
log::info!("Server started");
let mut state = run_ticking_thread(state).await;
log::info!("Shutting down");
if let Err(e) = shut_down(&state.resources, &mut state.world).await {
log::error!("An error occurred while shutting down: {:?}", e);
log::error!("Exiting.");
exit(1);
}
log::info!("Goodbye");
}
/// Starts the ticking thread. The returned future will complete
/// once the thread has terminated (i.e. the shutdown signal
/// has been received.)
async fn run_ticking_thread(mut state: FullState) -> FullState {
use std::thread;
use tokio::sync::oneshot;
let (tx, rx) = oneshot::channel();
thread::Builder::new()
.name(String::from("feather"))
.spawn(move || {
match std::panic::catch_unwind(AssertUnwindSafe(|| {
run_loop(&mut state);
})) {
Ok(_) => (),
Err(_) => {
log::error!("The server crashed. This is a bug.");
log::error!(
"Please report this at https://github.com/feather-rs/feather/issues"
);
}
}
tx.send(state).ok().expect("failed to exit server thread");
})
.expect("failed to spawn ticking thread");
rx.await.unwrap()
}
/// Runs the main game loop.
fn run_loop(state: &mut FullState) {
let mut loop_helper = LoopHelper::builder().build_with_target_rate(TPS as f64);
loop {
if state.shutdown_rx.try_recv().is_ok() {
// Shut down
return;
}
loop_helper.loop_start();
// Execute all systems
state
.executor
.execute(state.resources.deref(), &mut state.world);
// Clean up world
state.world.defrag(Some(256)); // should this be done at an interval rate?
loop_helper.loop_sleep();
}
}
async fn shut_down(resources: &OwnedResources, world: &mut World) -> anyhow::Result<()> {
log::info!("Disconnecting players");
shutdown::disconnect_players(&world)?;
log::info!("Shutting down workers");
shutdown::shut_down_workers(
&*resources.get::<Game>(),
&*resources.get::<LightingWorkerHandle>(),
)?;
log::info!("Saving chunks");
shutdown::save_chunks(
&*resources.get::<Game>(),
&*resources.get::<ChunkWorkerHandle>(),
&world,
)?;
log::info!("Saving level.dat");
shutdown::save_level(&mut *resources.get_mut::<Game>()).await?;
log::info!("Saving player data");
shutdown::save_player_data(&*resources.get::<Game>(), &world)?;
log::info!("Waiting for tasks to finish");
shutdown::wait_for_task_completion().await?;
Ok(())
}