Move server to subcommand

This commit is contained in:
Garen Tyler 2024-12-04 19:52:00 -07:00
parent c85b9a4bc2
commit e72f19a814
Signed by: garentyler
SSH Key Fingerprint: SHA256:G4ke7blZMdpWPbkescyZ7IQYE4JAtwpI85YoJdq+S7U
17 changed files with 293 additions and 166 deletions

View File

@ -9,7 +9,7 @@ RUN cargo install cargo-watch --locked --version 8.5.3
VOLUME /app VOLUME /app
VOLUME /app/.git VOLUME /app/.git
EXPOSE 25565 EXPOSE 25565
CMD ["cargo", "watch", "-x", "run"] CMD ["cargo", "watch", "-x", "run -- server"]
FROM base AS planner FROM base AS planner
COPY Cargo.toml . COPY Cargo.toml .
@ -36,3 +36,4 @@ COPY --from=builder /app/target/release/composition /app
EXPOSE 25565 EXPOSE 25565
USER composition USER composition
ENTRYPOINT ["tini", "--", "/app/composition"] ENTRYPOINT ["tini", "--", "/app/composition"]
CMD [ "server" ]

View File

@ -13,6 +13,7 @@ pub static CONFIG: OnceCell<Config> = OnceCell::new();
/// On program startup, Args::load() should be called to initialize it. /// On program startup, Args::load() should be called to initialize it.
pub static ARGS: OnceCell<Args> = OnceCell::new(); pub static ARGS: OnceCell<Args> = OnceCell::new();
static DEFAULT_ARGS: Lazy<Args> = Lazy::new(Args::default); static DEFAULT_ARGS: Lazy<Args> = Lazy::new(Args::default);
static DEFAULT_SERVER_ARGS: Lazy<ServerArgs> = Lazy::new(ServerArgs::default);
/// Helper function to read a file from a `Path` /// Helper function to read a file from a `Path`
/// and return its bytes as a `Vec<u8>`. /// and return its bytes as a `Vec<u8>`.
@ -99,7 +100,11 @@ impl Config {
} }
// Load the server icon // Load the server icon
config.server_icon = args.server_icon.clone(); config.server_icon = args
.server
.as_ref()
.map(|s| s.server_icon.clone())
.unwrap_or(DEFAULT_SERVER_ARGS.server_icon.clone());
let server_icon_path = Path::new(&config.server_icon); let server_icon_path = Path::new(&config.server_icon);
if server_icon_path.exists() { if server_icon_path.exists() {
@ -156,24 +161,31 @@ impl Config {
} }
} }
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Subcommand {
None,
Server,
}
/// All of the valid command line arguments for the composition binary. /// All of the valid command line arguments for the composition binary.
/// ///
/// Arguments will always override the config options specified in `composition.toml` or `Config::default()`. /// Arguments will always override the config options specified in `composition.toml` or `Config::default()`.
#[derive(Debug)] #[derive(Debug)]
pub struct Args { pub struct Args {
config_file: PathBuf, config_file: PathBuf,
server_icon: PathBuf,
pub log_level: Option<tracing::Level>, pub log_level: Option<tracing::Level>,
pub log_dir: PathBuf, pub log_dir: PathBuf,
pub subcommand: Subcommand,
server: Option<ServerArgs>,
} }
impl Default for Args { impl Default for Args {
fn default() -> Self { fn default() -> Self {
let config = Config::default();
Args { Args {
config_file: PathBuf::from("composition.toml"), config_file: PathBuf::from("composition.toml"),
server_icon: config.server_icon,
log_level: None, log_level: None,
log_dir: PathBuf::from("logs"), log_dir: PathBuf::from("logs"),
subcommand: Subcommand::None,
server: None,
} }
} }
} }
@ -188,10 +200,9 @@ impl Args {
ARGS.set(Self::parse()).expect("could not set ARGS"); ARGS.set(Self::parse()).expect("could not set ARGS");
Self::instance() Self::instance()
} }
fn parse() -> Self { fn command() -> clap::Command {
use std::ffi::OsStr; use std::ffi::OsStr;
clap::Command::new("composition")
let m = clap::Command::new("composition")
.about(env!("CARGO_PKG_DESCRIPTION")) .about(env!("CARGO_PKG_DESCRIPTION"))
.disable_version_flag(true) .disable_version_flag(true)
.arg( .arg(
@ -215,21 +226,16 @@ impl Args {
.short('c') .short('c')
.long("config-file") .long("config-file")
.help("Configuration file path") .help("Configuration file path")
.global(true)
.value_hint(clap::ValueHint::FilePath) .value_hint(clap::ValueHint::FilePath)
.default_value(OsStr::new(&DEFAULT_ARGS.config_file)), .default_value(OsStr::new(&DEFAULT_ARGS.config_file)),
) )
.arg(
Arg::new("server-icon")
.long("server-icon")
.help("Server icon file path")
.value_hint(clap::ValueHint::FilePath)
.default_value(OsStr::new(&DEFAULT_ARGS.server_icon)),
)
.arg( .arg(
Arg::new("log-level") Arg::new("log-level")
.short('l') .short('l')
.long("log-level") .long("log-level")
.help("Set the log level") .help("Set the log level")
.global(true)
.conflicts_with("verbose") .conflicts_with("verbose")
.value_name("level") .value_name("level")
.value_parser(["trace", "debug", "info", "warn", "error"]), .value_parser(["trace", "debug", "info", "warn", "error"]),
@ -238,19 +244,30 @@ impl Args {
Arg::new("log-dir") Arg::new("log-dir")
.long("log-dir") .long("log-dir")
.help("Set the log output directory") .help("Set the log output directory")
.global(true)
.value_name("dir") .value_name("dir")
.value_hint(clap::ValueHint::DirPath) .value_hint(clap::ValueHint::DirPath)
.default_value(OsStr::new(&DEFAULT_ARGS.log_dir)), .default_value(OsStr::new(&DEFAULT_ARGS.log_dir)),
) )
.get_matches(); .subcommand(
clap::Command::new("server")
.about("Run composition in server mode")
.arg(
Arg::new("server-icon")
.long("server-icon")
.help("Server icon file path")
.value_hint(clap::ValueHint::FilePath)
.default_value(OsStr::new(&DEFAULT_SERVER_ARGS.server_icon)),
),
)
}
fn parse() -> Self {
let mut args = Self::default(); let mut args = Self::default();
let m = Self::command().get_matches();
args.config_file = m args.config_file = m
.get_one::<String>("config-file") .get_one::<String>("config-file")
.map_or(args.config_file, PathBuf::from); .map_or(args.config_file, PathBuf::from);
args.server_icon = m
.get_one::<String>("server-icon")
.map_or(args.server_icon, PathBuf::from);
args.log_dir = m args.log_dir = m
.get_one::<String>("log-dir") .get_one::<String>("log-dir")
.map_or(args.log_dir, PathBuf::from); .map_or(args.log_dir, PathBuf::from);
@ -276,6 +293,43 @@ impl Args {
std::process::exit(0); std::process::exit(0);
} }
match m.subcommand() {
Some(("server", m)) => {
args.subcommand = Subcommand::Server;
let mut server_args = ServerArgs::default();
server_args.server_icon = m
.get_one::<String>("server-icon")
.map_or(server_args.server_icon, PathBuf::from);
args.server = Some(server_args);
}
None => {
let _ = Self::command().print_help();
std::process::exit(0);
}
_ => unreachable!(),
}
args args
} }
} }
#[derive(Debug)]
pub struct ServerArgs {
server_icon: PathBuf,
}
impl Default for ServerArgs {
fn default() -> Self {
let config = Config::default();
ServerArgs {
server_icon: config.server_icon,
}
}
}
impl ServerArgs {
pub fn instance() -> Option<&'static Self> {
Args::instance().server.as_ref()
}
pub fn load() -> Option<&'static Self> {
Args::load().server.as_ref()
}
}

View File

@ -1,9 +1,5 @@
/// Server configuration and cli options. /// Server configuration and cli options.
pub mod config; pub mod config;
/// When managing the server encounters errors.
pub(crate) mod error;
/// Network operations.
pub(crate) mod net;
/// The Minecraft protocol implemented in a network-agnostic way. /// The Minecraft protocol implemented in a network-agnostic way.
pub mod protocol; pub mod protocol;
/// The core server implementation. /// The core server implementation.
@ -11,17 +7,18 @@ pub(crate) mod server;
/// A Minecraft world generator implementation that allows for custom worlds. /// A Minecraft world generator implementation that allows for custom worlds.
pub mod world; pub mod world;
use crate::config::Config; use config::Subcommand;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use std::time::Instant; use std::time::Instant;
/// A globally accessible instant of the server's start time. /// A globally accessible instant of the composition's start time.
/// ///
/// This should be set immediately on startup. /// This should be set immediately on startup.
pub static START_TIME: OnceCell<Instant> = OnceCell::new(); pub static START_TIME: OnceCell<Instant> = OnceCell::new();
/// Start the server. pub async fn run(command: Subcommand) {
#[tracing::instrument] match command {
pub async fn start_server() -> (server::Server, tokio_util::sync::CancellationToken) { Subcommand::Server => server::Server::run().await,
server::Server::new(format!("0.0.0.0:{}", Config::instance().port)).await Subcommand::None => unreachable!(),
}
} }

View File

@ -36,7 +36,7 @@ pub fn main() {
.init(); .init();
// Load the config. // Load the config.
let config = composition::config::Config::load(); let config = composition::config::Config::instance();
match config.server_threads { match config.server_threads {
Some(1) => { Some(1) => {
@ -58,23 +58,7 @@ pub fn main() {
} }
.unwrap() .unwrap()
.block_on(async move { .block_on(async move {
info!("Starting {} on port {}", config.server_version, config.port); let args = composition::config::Args::instance();
let (mut server, running) = composition::start_server().await; composition::run(args.subcommand).await;
info!(
"Done! Start took {:?}",
composition::START_TIME.get().unwrap().elapsed()
);
// The main server loop.
loop {
tokio::select! {
_ = running.cancelled() => {
break;
}
_ = server.update() => {}
}
}
let _ = tokio::time::timeout(std::time::Duration::from_secs(10), server.shutdown()).await;
}); });
} }

View File

@ -6,8 +6,6 @@ pub mod entities;
pub mod error; pub mod error;
/// Implementation of Minecraft's items and inventories. /// Implementation of Minecraft's items and inventories.
pub mod inventory; pub mod inventory;
/// Useful types for representing the Minecraft protocol.
pub mod types;
/// Network packets. /// Network packets.
/// ///
/// The packet naming convention used is "DSIDName" where /// The packet naming convention used is "DSIDName" where
@ -19,6 +17,8 @@ pub mod types;
pub mod packets; pub mod packets;
/// Useful shared parsing functions. /// Useful shared parsing functions.
pub mod parsing; pub mod parsing;
/// Useful types for representing the Minecraft protocol.
pub mod types;
pub use error::{Error, Result}; pub use error::{Error, Result};

View File

@ -1,5 +1,5 @@
use crate::protocol::types::{Chat, Json, Uuid, VarInt};
use crate::protocol::parsing::Parsable; use crate::protocol::parsing::Parsable;
use crate::protocol::types::{Chat, Json, Uuid, VarInt};
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct CL00Disconnect { pub struct CL00Disconnect {
@ -33,11 +33,14 @@ crate::protocol::packets::packet!(
let (data, public_key) = u8::parse_vec(data)?; let (data, public_key) = u8::parse_vec(data)?;
let (data, verify_token) = u8::parse_vec(data)?; let (data, verify_token) = u8::parse_vec(data)?;
Ok((data, CL01EncryptionRequest { Ok((
server_id, data,
public_key, CL01EncryptionRequest {
verify_token, server_id,
})) public_key,
verify_token,
},
))
}, },
|packet: &CL01EncryptionRequest| -> Vec<u8> { |packet: &CL01EncryptionRequest| -> Vec<u8> {
let mut output = vec![]; let mut output = vec![];
@ -66,11 +69,14 @@ impl Parsable for CL02LoginSuccessProperty {
let (data, name) = String::parse(data)?; let (data, name) = String::parse(data)?;
let (data, value) = String::parse(data)?; let (data, value) = String::parse(data)?;
let (data, signature) = String::parse_optional(data)?; let (data, signature) = String::parse_optional(data)?;
Ok((data, CL02LoginSuccessProperty { Ok((
name, data,
value, CL02LoginSuccessProperty {
signature, name,
})) value,
signature,
},
))
} }
#[tracing::instrument] #[tracing::instrument]
fn serialize(&self) -> Vec<u8> { fn serialize(&self) -> Vec<u8> {
@ -91,11 +97,14 @@ crate::protocol::packets::packet!(
let (data, username) = String::parse(data)?; let (data, username) = String::parse(data)?;
let (data, properties) = CL02LoginSuccessProperty::parse_vec(data)?; let (data, properties) = CL02LoginSuccessProperty::parse_vec(data)?;
Ok((data, CL02LoginSuccess { Ok((
uuid, data,
username, CL02LoginSuccess {
properties, uuid,
})) username,
properties,
},
))
}, },
|packet: &CL02LoginSuccess| -> Vec<u8> { |packet: &CL02LoginSuccess| -> Vec<u8> {
let mut output = vec![]; let mut output = vec![];
@ -136,11 +145,14 @@ crate::protocol::packets::packet!(
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CL04LoginPluginRequest> { |data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CL04LoginPluginRequest> {
let (data, message_id) = VarInt::parse(data)?; let (data, message_id) = VarInt::parse(data)?;
let (data, channel) = String::parse(data)?; let (data, channel) = String::parse(data)?;
Ok((data, CL04LoginPluginRequest { Ok((
message_id, data,
channel, CL04LoginPluginRequest {
data: data.to_vec(), message_id,
})) channel,
data: data.to_vec(),
},
))
}, },
|packet: &CL04LoginPluginRequest| -> Vec<u8> { |packet: &CL04LoginPluginRequest| -> Vec<u8> {
let mut output = vec![]; let mut output = vec![];

View File

@ -29,16 +29,19 @@ crate::protocol::packets::packet!(
let (data, d) = VarInt::parse(data)?; let (data, d) = VarInt::parse(data)?;
let (data, velocity) = EntityVelocity::parse(data)?; let (data, velocity) = EntityVelocity::parse(data)?;
Ok((data, CP00SpawnEntity { Ok((
id, data,
uuid, CP00SpawnEntity {
kind, id,
position, uuid,
rotation, kind,
head_yaw, position,
data: d, rotation,
velocity, head_yaw,
})) data: d,
velocity,
},
))
}, },
|packet: &CP00SpawnEntity| -> Vec<u8> { |packet: &CP00SpawnEntity| -> Vec<u8> {
let mut output = vec![]; let mut output = vec![];
@ -67,10 +70,13 @@ crate::protocol::packets::packet!(
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CP0BChangeDifficulty> { |data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CP0BChangeDifficulty> {
let (data, difficulty) = Difficulty::parse(data)?; let (data, difficulty) = Difficulty::parse(data)?;
let (data, is_locked) = bool::parse(data)?; let (data, is_locked) = bool::parse(data)?;
Ok((data, CP0BChangeDifficulty { Ok((
difficulty, data,
is_locked, CP0BChangeDifficulty {
})) difficulty,
is_locked,
},
))
}, },
|packet: &CP0BChangeDifficulty| -> Vec<u8> { |packet: &CP0BChangeDifficulty| -> Vec<u8> {
let mut output = vec![]; let mut output = vec![];
@ -129,12 +135,15 @@ crate::protocol::packets::packet!(
let (data, location) = Position::parse(data)?; let (data, location) = Position::parse(data)?;
let (data, d) = i32::parse(data)?; let (data, d) = i32::parse(data)?;
let (data, disable_relative_volume) = bool::parse(data)?; let (data, disable_relative_volume) = bool::parse(data)?;
Ok((data, CP21WorldEvent { Ok((
event, data,
location, CP21WorldEvent {
data: d, event,
disable_relative_volume, location,
})) data: d,
disable_relative_volume,
},
))
}, },
|packet: &CP21WorldEvent| -> Vec<u8> { |packet: &CP21WorldEvent| -> Vec<u8> {
let mut output = vec![]; let mut output = vec![];
@ -159,10 +168,13 @@ crate::protocol::packets::packet!(
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CP50SetEntityVelocity> { |data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], CP50SetEntityVelocity> {
let (data, entity_id) = VarInt::parse(data)?; let (data, entity_id) = VarInt::parse(data)?;
let (data, entity_velocity) = EntityVelocity::parse(data)?; let (data, entity_velocity) = EntityVelocity::parse(data)?;
Ok((data, CP50SetEntityVelocity { Ok((
entity_id, data,
entity_velocity, CP50SetEntityVelocity {
})) entity_id,
entity_velocity,
},
))
}, },
|packet: &CP50SetEntityVelocity| -> Vec<u8> { |packet: &CP50SetEntityVelocity| -> Vec<u8> {
let mut output = vec![]; let mut output = vec![];
@ -187,11 +199,14 @@ crate::protocol::packets::packet!(
let (data, experience_bar) = f32::parse(data)?; let (data, experience_bar) = f32::parse(data)?;
let (data, total_experience) = VarInt::parse(data)?; let (data, total_experience) = VarInt::parse(data)?;
let (data, level) = VarInt::parse(data)?; let (data, level) = VarInt::parse(data)?;
Ok((data, CP52SetExperience { Ok((
experience_bar, data,
total_experience, CP52SetExperience {
level, experience_bar,
})) total_experience,
level,
},
))
}, },
|packet: &CP52SetExperience| -> Vec<u8> { |packet: &CP52SetExperience| -> Vec<u8> {
let mut output = vec![]; let mut output = vec![];
@ -231,16 +246,19 @@ crate::protocol::packets::packet!(
let (data, has_factor_data) = bool::parse(data)?; let (data, has_factor_data) = bool::parse(data)?;
// TODO: factor_codec // TODO: factor_codec
Ok((data, CP68EntityEffect { Ok((
entity_id, data,
effect_id, CP68EntityEffect {
amplifier, entity_id,
duration, effect_id,
is_ambient, amplifier,
show_particles, duration,
show_icon, is_ambient,
has_factor_data, show_particles,
})) show_icon,
has_factor_data,
},
))
}, },
|packet: &CP68EntityEffect| -> Vec<u8> { |packet: &CP68EntityEffect| -> Vec<u8> {
let mut output = vec![]; let mut output = vec![];

View File

@ -3,7 +3,7 @@ pub mod clientbound;
/// Packets that are heading to the server. /// Packets that are heading to the server.
pub mod serverbound; pub mod serverbound;
use crate::protocol::parsing::{VarInt, Parsable}; use crate::protocol::parsing::{Parsable, VarInt};
/// Alias for a `VarInt`. /// Alias for a `VarInt`.
pub type PacketId = VarInt; pub type PacketId = VarInt;

View File

@ -1,4 +1,4 @@
use crate::protocol::{ClientState, types::VarInt}; use crate::protocol::{types::VarInt, ClientState};
use nom::combinator::map_res; use nom::combinator::map_res;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@ -24,12 +24,15 @@ crate::protocol::packets::packet!(
_ => Err(()), _ => Err(()),
})(data)?; })(data)?;
Ok((data, SH00Handshake { Ok((
protocol_version, data,
server_address, SH00Handshake {
server_port, protocol_version,
next_state, server_address,
})) server_port,
next_state,
},
))
}, },
|packet: &SH00Handshake| -> Vec<u8> { |packet: &SH00Handshake| -> Vec<u8> {
let mut output = vec![]; let mut output = vec![];

View File

@ -16,10 +16,13 @@ crate::protocol::packets::packet!(
let (data, has_uuid) = bool::parse(data)?; let (data, has_uuid) = bool::parse(data)?;
if has_uuid { if has_uuid {
let (data, uuid) = Uuid::parse(data)?; let (data, uuid) = Uuid::parse(data)?;
Ok((data, SL00LoginStart { Ok((
name, data,
uuid: Some(uuid), SL00LoginStart {
})) name,
uuid: Some(uuid),
},
))
} else { } else {
Ok((data, SL00LoginStart { name, uuid: None })) Ok((data, SL00LoginStart { name, uuid: None }))
} }
@ -51,10 +54,13 @@ crate::protocol::packets::packet!(
let (data, verify_token_len) = VarInt::parse(data)?; let (data, verify_token_len) = VarInt::parse(data)?;
let (data, verify_token) = take(*verify_token_len as usize)(data)?; let (data, verify_token) = take(*verify_token_len as usize)(data)?;
Ok((data, SL01EncryptionResponse { Ok((
shared_secret: shared_secret.to_vec(), data,
verify_token: verify_token.to_vec(), SL01EncryptionResponse {
})) shared_secret: shared_secret.to_vec(),
verify_token: verify_token.to_vec(),
},
))
}, },
|packet: &SL01EncryptionResponse| -> Vec<u8> { |packet: &SL01EncryptionResponse| -> Vec<u8> {
let mut output = vec![]; let mut output = vec![];
@ -81,17 +87,23 @@ crate::protocol::packets::packet!(
let (data, message_id) = VarInt::parse(data)?; let (data, message_id) = VarInt::parse(data)?;
let (data, successful) = bool::parse(data)?; let (data, successful) = bool::parse(data)?;
if successful { if successful {
Ok((&[], SL02LoginPluginResponse { Ok((
message_id, &[],
successful, SL02LoginPluginResponse {
data: data.to_vec(), message_id,
})) successful,
data: data.to_vec(),
},
))
} else { } else {
Ok((data, SL02LoginPluginResponse { Ok((
message_id, data,
successful, SL02LoginPluginResponse {
data: vec![], message_id,
})) successful,
data: vec![],
},
))
} }
}, },
|packet: &SL02LoginPluginResponse| -> Vec<u8> { |packet: &SL02LoginPluginResponse| -> Vec<u8> {

View File

@ -58,10 +58,13 @@ crate::protocol::packets::packet!(
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], SP13SetPlayerPosition> { |data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], SP13SetPlayerPosition> {
let (data, position) = EntityPosition::parse(data)?; let (data, position) = EntityPosition::parse(data)?;
let (data, on_ground) = bool::parse(data)?; let (data, on_ground) = bool::parse(data)?;
Ok((data, SP13SetPlayerPosition { Ok((
position, data,
on_ground, SP13SetPlayerPosition {
})) position,
on_ground,
},
))
}, },
|packet: &SP13SetPlayerPosition| -> Vec<u8> { |packet: &SP13SetPlayerPosition| -> Vec<u8> {
let mut output = vec![]; let mut output = vec![];
@ -114,10 +117,13 @@ crate::protocol::packets::packet!(
|data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], SP15SetPlayerRotation> { |data: &'data [u8]| -> crate::protocol::parsing::IResult<&'data [u8], SP15SetPlayerRotation> {
let (data, rotation) = EntityRotation::parse(data)?; let (data, rotation) = EntityRotation::parse(data)?;
let (data, on_ground) = bool::parse(data)?; let (data, on_ground) = bool::parse(data)?;
Ok((data, SP15SetPlayerRotation { Ok((
rotation, data,
on_ground, SP15SetPlayerRotation {
})) rotation,
on_ground,
},
))
}, },
|packet: &SP15SetPlayerRotation| -> Vec<u8> { |packet: &SP15SetPlayerRotation| -> Vec<u8> {
let mut output = vec![]; let mut output = vec![];

View File

@ -1,5 +1,9 @@
pub use nom::IResult; pub use nom::IResult;
use nom::{bytes::streaming::{take, take_while_m_n}, number::streaming as nom_nums, combinator::map_res}; use nom::{
bytes::streaming::{take, take_while_m_n},
combinator::map_res,
number::streaming as nom_nums,
};
/// Implementation of the protocol's VarInt type. /// Implementation of the protocol's VarInt type.
/// ///
@ -317,7 +321,11 @@ impl Parsable for bool {
} }
#[tracing::instrument] #[tracing::instrument]
fn serialize(&self) -> Vec<u8> { fn serialize(&self) -> Vec<u8> {
if *self { vec![0x01] } else { vec![0x00] } if *self {
vec![0x01]
} else {
vec![0x00]
}
} }
} }
@ -380,4 +388,3 @@ mod tests {
} }
} }
} }

View File

@ -83,10 +83,13 @@ mod tests {
fn get_positions() -> Vec<(Position, Vec<u8>)> { fn get_positions() -> Vec<(Position, Vec<u8>)> {
vec![ vec![
// x: 01000110000001110110001100 z: 10110000010101101101001000 y: 001100111111 // x: 01000110000001110110001100 z: 10110000010101101101001000 y: 001100111111
(Position::new(18357644, 831, -20882616), vec![ (
0b01000110, 0b00000111, 0b01100011, 0b00101100, 0b00010101, 0b10110100, 0b10000011, Position::new(18357644, 831, -20882616),
0b00111111, vec![
]), 0b01000110, 0b00000111, 0b01100011, 0b00101100, 0b00010101, 0b10110100,
0b10000011, 0b00111111,
],
),
] ]
} }
#[test] #[test]

View File

@ -1,7 +1,12 @@
/// When managing the server encounters errors.
pub mod error;
/// Network operations.
pub mod net;
use crate::config::Config; use crate::config::Config;
use crate::error::Result;
use crate::net::{NetworkClient, NetworkClientState};
use crate::protocol::ClientState; use crate::protocol::ClientState;
use error::Result;
use net::{NetworkClient, NetworkClientState};
use std::sync::Arc; use std::sync::Arc;
use tokio::net::{TcpListener, ToSocketAddrs}; use tokio::net::{TcpListener, ToSocketAddrs};
use tokio::{sync::RwLock, task::JoinHandle}; use tokio::{sync::RwLock, task::JoinHandle};
@ -15,6 +20,40 @@ pub struct Server {
net_tasks_handle: JoinHandle<()>, net_tasks_handle: JoinHandle<()>,
} }
impl Server { impl Server {
/// Start the server.
#[tracing::instrument]
pub async fn run() {
let config = crate::config::Config::instance();
info!("Starting {} on port {}", config.server_version, config.port);
let (mut server, running) = Self::new(format!("0.0.0.0:{}", Config::instance().port)).await;
info!(
"Done! Start took {:?}",
crate::START_TIME.get().unwrap().elapsed()
);
// Spawn the ctrl-c task.
let r = running.clone();
tokio::spawn(async move {
tokio::signal::ctrl_c().await.unwrap();
info!("Ctrl-C received, shutting down");
r.cancel();
});
// The main server loop.
loop {
tokio::select! {
_ = running.cancelled() => {
break;
}
_ = server.update() => {}
}
}
match tokio::time::timeout(std::time::Duration::from_secs(10), server.shutdown()).await {
Ok(_) => std::process::exit(0),
Err(_) => std::process::exit(1),
}
}
#[tracing::instrument] #[tracing::instrument]
pub async fn new<A: 'static + ToSocketAddrs + Send + std::fmt::Debug>( pub async fn new<A: 'static + ToSocketAddrs + Send + std::fmt::Debug>(
bind_address: A, bind_address: A,
@ -34,15 +73,6 @@ impl Server {
net_tasks_handle, net_tasks_handle,
}; };
// let (shutdown_tx, shutdown_rx) = oneshot::channel();
let r = running.clone();
tokio::spawn(async move {
tokio::signal::ctrl_c().await.unwrap();
info!("Ctrl-C received, shutting down");
r.cancel();
// shutdown_tx.send(()).unwrap();
});
(server, running) (server, running)
} }
#[tracing::instrument] #[tracing::instrument]

View File

@ -1,7 +1,7 @@
use crate::protocol::{ use crate::protocol::{
ClientState, packets::{serverbound::SL00LoginStart, GenericPacket},
packets::{GenericPacket, serverbound::SL00LoginStart},
parsing::Parsable, parsing::Parsable,
ClientState,
}; };
use std::{collections::VecDeque, sync::Arc, time::Instant}; use std::{collections::VecDeque, sync::Arc, time::Instant};
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;

View File

@ -6,7 +6,7 @@ pub mod error;
pub mod generators; pub mod generators;
/// Useful re-exports. /// Useful re-exports.
pub mod prelude { pub mod prelude {
pub use super::{World, chunks::Chunk}; pub use super::{chunks::Chunk, World};
} }
pub use crate::protocol::{blocks, entities}; pub use crate::protocol::{blocks, entities};