Disconnect all clients on server shutdown, shutdown on ctrl-c
This commit is contained in:
parent
17d953fc0c
commit
f73fdae2c7
29
Cargo.lock
generated
29
Cargo.lock
generated
@ -46,6 +46,12 @@ version = "1.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
|
checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.67"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "0.1.10"
|
version = "0.1.10"
|
||||||
@ -88,6 +94,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"ctrlc",
|
||||||
"fern",
|
"fern",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
@ -98,6 +105,16 @@ dependencies = [
|
|||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ctrlc"
|
||||||
|
version = "3.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c15b8ec3b5755a188c141c1f6a98e76de31b936209bf066b647979e2a84764a9"
|
||||||
|
dependencies = [
|
||||||
|
"nix",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fern"
|
name = "fern"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
@ -191,6 +208,18 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.20.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cc",
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ntapi"
|
name = "ntapi"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
|
@ -15,6 +15,7 @@ radix64 = "0.3.0"
|
|||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
async-trait = "0.1.48"
|
async-trait = "0.1.48"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
ctrlc = "3.1.8"
|
||||||
# colorful = "0.2.1"
|
# colorful = "0.2.1"
|
||||||
# ozelot = "0.9.0" # Ozelot 0.9.0 supports protocol version 578 (1.15.2)
|
# ozelot = "0.9.0" # Ozelot 0.9.0 supports protocol version 578 (1.15.2)
|
||||||
# toml = "0.5.6"
|
# toml = "0.5.6"
|
||||||
|
10
src/lib.rs
10
src/lib.rs
@ -13,6 +13,7 @@ pub mod world;
|
|||||||
use log::warn;
|
use log::warn;
|
||||||
pub use mctypes::*;
|
pub use mctypes::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::sync::mpsc::{self, Receiver};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
@ -60,7 +61,7 @@ lazy_static! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set up logging, read the config file, etc.
|
/// Set up logging, read the config file, etc.
|
||||||
pub fn init() {
|
pub fn init() -> Receiver<()> {
|
||||||
// Set up fern logging.
|
// Set up fern logging.
|
||||||
fern::Dispatch::new()
|
fern::Dispatch::new()
|
||||||
.format(move |out, message, record| {
|
.format(move |out, message, record| {
|
||||||
@ -81,6 +82,13 @@ pub fn init() {
|
|||||||
.chain(fern::log_file("output.log").unwrap())
|
.chain(fern::log_file("output.log").unwrap())
|
||||||
.apply()
|
.apply()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
// Set up the ctrl-c handler.
|
||||||
|
let (ctrlc_tx, ctrlc_rx) = mpsc::channel();
|
||||||
|
ctrlc::set_handler(move || {
|
||||||
|
ctrlc_tx.send(()).expect("Ctrl-C receiver disconnected");
|
||||||
|
})
|
||||||
|
.expect("Error setting Ctrl-C handler");
|
||||||
|
ctrlc_rx
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start the server.
|
/// Start the server.
|
||||||
|
11
src/main.rs
11
src/main.rs
@ -1,16 +1,25 @@
|
|||||||
use log::info;
|
use log::info;
|
||||||
|
use std::sync::mpsc::TryRecvError;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
pub async fn main() {
|
pub async fn main() {
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
composition::init();
|
let ctrlc_rx = composition::init();
|
||||||
info!("Starting server...");
|
info!("Starting server...");
|
||||||
let mut server = composition::start_server().await;
|
let mut server = composition::start_server().await;
|
||||||
info!("Done! Start took {:?}", start_time.elapsed());
|
info!("Done! Start took {:?}", start_time.elapsed());
|
||||||
|
|
||||||
// The main server loop.
|
// The main server loop.
|
||||||
loop {
|
loop {
|
||||||
|
match ctrlc_rx.try_recv() {
|
||||||
|
Ok(_) => {
|
||||||
|
server.shutdown().await;
|
||||||
|
break; // Exit the loop.
|
||||||
|
}
|
||||||
|
Err(TryRecvError::Empty) => {} // Doesn't matter if there's nothing for us
|
||||||
|
Err(TryRecvError::Disconnected) => panic!("Ctrl-C sender disconnected"),
|
||||||
|
}
|
||||||
server.update().await.unwrap();
|
server.update().await.unwrap();
|
||||||
std::thread::sleep(Duration::from_millis(2));
|
std::thread::sleep(Duration::from_millis(2));
|
||||||
}
|
}
|
||||||
|
@ -108,14 +108,12 @@ pub mod other {
|
|||||||
impl TryFrom<Vec<u8>> for MCBoolean {
|
impl TryFrom<Vec<u8>> for MCBoolean {
|
||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||||
if bytes.len() < 1 {
|
if bytes.is_empty() {
|
||||||
Err("Not enough bytes")
|
Err("Not enough bytes")
|
||||||
|
} else if bytes[0] == 1u8 {
|
||||||
|
Ok(MCBoolean::True)
|
||||||
} else {
|
} else {
|
||||||
if bytes[0] == 1u8 {
|
Ok(MCBoolean::False)
|
||||||
Ok(MCBoolean::True)
|
|
||||||
} else {
|
|
||||||
Ok(MCBoolean::False)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,7 +146,7 @@ pub mod other {
|
|||||||
}
|
}
|
||||||
impl From<String> for MCString {
|
impl From<String> for MCString {
|
||||||
fn from(s: String) -> MCString {
|
fn from(s: String) -> MCString {
|
||||||
MCString { value: s.clone() }
|
MCString { value: s }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Into<String> for MCString {
|
impl Into<String> for MCString {
|
||||||
@ -227,9 +225,7 @@ pub mod other {
|
|||||||
}
|
}
|
||||||
impl From<String> for MCChat {
|
impl From<String> for MCChat {
|
||||||
fn from(s: String) -> MCChat {
|
fn from(s: String) -> MCChat {
|
||||||
MCChat {
|
MCChat { text: s.into() }
|
||||||
text: s.clone().into(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Into<String> for MCChat {
|
impl Into<String> for MCChat {
|
||||||
@ -345,6 +341,15 @@ pub mod other {
|
|||||||
Err(io_error("Cannot read MCPosition from stream"))
|
Err(io_error("Cannot read MCPosition from stream"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Default for MCPosition {
|
||||||
|
fn default() -> Self {
|
||||||
|
MCPosition {
|
||||||
|
x: 0.into(),
|
||||||
|
y: 0.into(),
|
||||||
|
z: 0.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All the numbers, from `i8` and `u8` to `i64` and `u64`, plus `VarInt`s.
|
/// All the numbers, from `i8` and `u8` to `i64` and `u64`, plus `VarInt`s.
|
||||||
@ -394,7 +399,7 @@ pub mod numbers {
|
|||||||
impl TryFrom<Vec<u8>> for MCByte {
|
impl TryFrom<Vec<u8>> for MCByte {
|
||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||||
if bytes.len() < 1 {
|
if bytes.is_empty() {
|
||||||
Err("Not enough bytes")
|
Err("Not enough bytes")
|
||||||
} else {
|
} else {
|
||||||
let mut a = [0u8; 1];
|
let mut a = [0u8; 1];
|
||||||
@ -452,7 +457,7 @@ pub mod numbers {
|
|||||||
impl TryFrom<Vec<u8>> for MCUnsignedByte {
|
impl TryFrom<Vec<u8>> for MCUnsignedByte {
|
||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
|
||||||
if bytes.len() < 1 {
|
if bytes.is_empty() {
|
||||||
Err("Not enough bytes")
|
Err("Not enough bytes")
|
||||||
} else {
|
} else {
|
||||||
let mut a = [0u8; 1];
|
let mut a = [0u8; 1];
|
||||||
@ -1065,7 +1070,7 @@ pub mod numbers {
|
|||||||
}
|
}
|
||||||
out.push(temp);
|
out.push(temp);
|
||||||
}
|
}
|
||||||
return out;
|
out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,17 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shut down the server.
|
||||||
|
///
|
||||||
|
/// Disconnects all clients.
|
||||||
|
pub async fn shutdown(&mut self) {
|
||||||
|
info!("Server shutting down.");
|
||||||
|
for client in self.network_clients.iter_mut() {
|
||||||
|
let _ = client.disconnect(Some("The server is shutting down")).await;
|
||||||
|
// We don't care if it doesn't succeed in sending the packet.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Update the network server.
|
/// Update the network server.
|
||||||
///
|
///
|
||||||
/// Update each client in `self.network_clients`.
|
/// Update each client in `self.network_clients`.
|
||||||
@ -67,7 +78,9 @@ impl Server {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
for client in self.network_clients.iter_mut() {
|
for client in self.network_clients.iter_mut() {
|
||||||
client.update(num_players).await?;
|
if client.update(num_players).await.is_err() {
|
||||||
|
client.force_disconnect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Remove disconnected clients.
|
// Remove disconnected clients.
|
||||||
self.network_clients
|
self.network_clients
|
||||||
@ -87,7 +100,7 @@ impl Server {
|
|||||||
|
|
||||||
/// The network client can only be in a few states,
|
/// The network client can only be in a few states,
|
||||||
/// this enum keeps track of that.
|
/// this enum keeps track of that.
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub enum NetworkClientState {
|
pub enum NetworkClientState {
|
||||||
Handshake,
|
Handshake,
|
||||||
Status,
|
Status,
|
||||||
@ -98,6 +111,7 @@ pub enum NetworkClientState {
|
|||||||
|
|
||||||
/// A wrapper to contain everything related
|
/// A wrapper to contain everything related
|
||||||
/// to networking for the client.
|
/// to networking for the client.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct NetworkClient {
|
pub struct NetworkClient {
|
||||||
pub id: u128,
|
pub id: u128,
|
||||||
pub connected: bool,
|
pub connected: bool,
|
||||||
@ -126,6 +140,7 @@ impl NetworkClient {
|
|||||||
/// Updating could mean connecting new clients, reading packets,
|
/// Updating could mean connecting new clients, reading packets,
|
||||||
/// writing packets, or disconnecting clients.
|
/// writing packets, or disconnecting clients.
|
||||||
pub async fn update(&mut self, num_players: usize) -> tokio::io::Result<()> {
|
pub async fn update(&mut self, num_players: usize) -> tokio::io::Result<()> {
|
||||||
|
// println!("{:?}", self);
|
||||||
match self.state {
|
match self.state {
|
||||||
NetworkClientState::Handshake => {
|
NetworkClientState::Handshake => {
|
||||||
let handshake = self.get_packet::<Handshake>().await?;
|
let handshake = self.get_packet::<Handshake>().await?;
|
||||||
@ -284,14 +299,12 @@ impl NetworkClient {
|
|||||||
let mut disconnect = Disconnect::new();
|
let mut disconnect = Disconnect::new();
|
||||||
disconnect.reason.text = reason.unwrap_or("Disconnected").into();
|
disconnect.reason.text = reason.unwrap_or("Disconnected").into();
|
||||||
self.send_packet(disconnect).await?;
|
self.send_packet(disconnect).await?;
|
||||||
// Give the client 10 seconds to disconnect before forcing it.
|
|
||||||
tokio::time::sleep(Duration::from_secs(10)).await;
|
|
||||||
self.force_disconnect();
|
self.force_disconnect();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Force disconnect the client by marking it for cleanup as disconnected.
|
/// Force disconnect the client by marking it for cleanup as disconnected.
|
||||||
async fn force_disconnect(&mut self) {
|
fn force_disconnect(&mut self) {
|
||||||
self.connected = false;
|
self.connected = false;
|
||||||
self.state = NetworkClientState::Disconnected;
|
self.state = NetworkClientState::Disconnected;
|
||||||
}
|
}
|
||||||
|
@ -37,9 +37,14 @@ macro_rules! register_packets {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Default for Packet {
|
||||||
|
fn default() -> Self {
|
||||||
|
Packet::Null
|
||||||
|
}
|
||||||
|
}
|
||||||
$(
|
$(
|
||||||
impl $name {
|
impl $name {
|
||||||
pub fn into_packet(&self) -> Packet {
|
pub fn as_packet(&self) -> Packet {
|
||||||
Packet::$name(self.clone())
|
Packet::$name(self.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user