Reorganize registry codec stuff and fix some bugs

This commit is contained in:
Ryan 2022-09-23 04:51:26 -07:00
parent 36b63e777e
commit 28b26b2aae
5 changed files with 123 additions and 99 deletions

View file

@ -1,5 +1,8 @@
//! Biome configuration and identification.
use std::collections::HashSet;
use anyhow::ensure;
use valence_nbt::{compound, Compound};
use crate::ident;
@ -136,6 +139,27 @@ impl Biome {
}
}
pub(crate) fn validate_biomes(biomes: &[Biome]) -> anyhow::Result<()> {
ensure!(!biomes.is_empty(), "at least one biome must be present");
ensure!(
biomes.len() <= u16::MAX as _,
"more than u16::MAX biomes present"
);
let mut names = HashSet::new();
for biome in biomes {
ensure!(
names.insert(biome.name.clone()),
"biome \"{}\" already exists",
biome.name
);
}
Ok(())
}
impl Default for Biome {
fn default() -> Self {
Self {

View file

@ -10,10 +10,8 @@ pub use event::*;
use flume::{Receiver, Sender, TrySendError};
use rayon::iter::ParallelIterator;
use uuid::Uuid;
use valence_nbt::{compound, Compound, List};
use vek::Vec3;
use crate::biome::Biome;
use crate::block_pos::BlockPos;
use crate::chunk_pos::ChunkPos;
use crate::config::Config;
@ -1069,7 +1067,7 @@ impl<C: Config> Client<C> {
gamemode: self.new_game_mode,
previous_gamemode: self.old_game_mode,
dimension_names,
registry_codec: make_registry_codec(shared),
registry_codec: shared.registry_codec().clone(),
dimension_type_name: world.meta.dimension().dimension_type_name(),
dimension_name: world.meta.dimension().dimension_name(),
hashed_seed: 0,
@ -1536,41 +1534,3 @@ fn send_entity_events(send_opt: &mut SendOpt, entity_id: i32, events: &[entity::
}
}
}
fn make_registry_codec<C: Config>(shared: &SharedServer<C>) -> Compound {
compound! {
ident!("dimension_type") => compound! {
"type" => ident!("dimension_type"),
"value" => List::Compound(shared.dimensions().map(|(id, dim)| compound! {
"name" => id.dimension_type_name(),
"id" => id.0 as i32,
"element" => dim.to_dimension_registry_item(),
}).collect()),
},
ident!("worldgen/biome") => compound! {
"type" => ident!("worldgen/biome"),
"value" => {
let mut biomes: Vec<_> = shared
.biomes()
.map(|(id, biome)| biome.to_biome_registry_item(id.0 as i32))
.collect();
// The client needs a biome named "minecraft:plains" in the registry to
// connect. This is probably a bug in the client.
//
// If the issue is resolved, remove this if.
if !biomes.iter().any(|b| b["name"] == "plains".into()) {
let biome = Biome::default();
assert_eq!(biome.name, ident!("plains"));
biomes.push(biome.to_biome_registry_item(biomes.len() as i32));
}
List::Compound(biomes)
}
},
ident!("chat_type_registry") => compound! {
"type" => ident!("chat_type"),
"value" => List::Compound(Vec::new()),
},
}
}

View file

@ -165,7 +165,7 @@ pub trait Config: Sized + Send + Sync + UnwindSafe + RefUnwindSafe + 'static {
///
/// # Default Implementation
///
/// Returns `vec![Dimension::default()]`.
/// Returns `vec![Biome::default()]`.
fn biomes(&self) -> Vec<Biome> {
vec![Biome::default()]
}

View file

@ -1,5 +1,6 @@
//! Dimension configuration and identification.
use anyhow::ensure;
use valence_nbt::{compound, Compound};
use crate::ident::Ident;
@ -117,6 +118,46 @@ impl Dimension {
}
}
pub(crate) fn validate_dimensions(dimensions: &[Dimension]) -> anyhow::Result<()> {
ensure!(
!dimensions.is_empty(),
"at least one dimension must be present"
);
ensure!(
dimensions.len() <= u16::MAX as usize,
"more than u16::MAX dimensions present"
);
for (i, dim) in dimensions.iter().enumerate() {
ensure!(
dim.min_y % 16 == 0 && (-2032..=2016).contains(&dim.min_y),
"invalid min_y in dimension #{i}",
);
ensure!(
dim.height % 16 == 0
&& (0..=4064).contains(&dim.height)
&& dim.min_y.saturating_add(dim.height) <= 2032,
"invalid height in dimension #{i}",
);
ensure!(
(0.0..=1.0).contains(&dim.ambient_light),
"ambient_light is out of range in dimension #{i}",
);
if let Some(fixed_time) = dim.fixed_time {
ensure!(
(0..=24_000).contains(&fixed_time),
"fixed_time is out of range in dimension #{i}",
);
}
}
Ok(())
}
impl Default for Dimension {
fn default() -> Self {
Self {

View file

@ -1,6 +1,5 @@
//! The heart of the server.
use std::collections::HashSet;
use std::error::Error;
use std::iter::FusedIterator;
use std::net::SocketAddr;
@ -26,11 +25,12 @@ use tokio::net::{TcpListener, TcpStream};
use tokio::runtime::{Handle, Runtime};
use tokio::sync::{oneshot, Semaphore};
use uuid::Uuid;
use valence_nbt::{compound, Compound, List};
use crate::biome::{Biome, BiomeId};
use crate::biome::{validate_biomes, Biome, BiomeId};
use crate::client::{Client, Clients};
use crate::config::{Config, ServerListPing};
use crate::dimension::{Dimension, DimensionId};
use crate::dimension::{validate_dimensions, Dimension, DimensionId};
use crate::entity::Entities;
use crate::player_list::PlayerLists;
use crate::player_textures::SignedPlayerTextures;
@ -48,7 +48,7 @@ use crate::protocol::packets::Property;
use crate::protocol::{BoundedArray, BoundedString, VarInt};
use crate::util::valid_username;
use crate::world::Worlds;
use crate::{Ticks, PROTOCOL_VERSION, VERSION_NAME};
use crate::{ident, Ticks, PROTOCOL_VERSION, VERSION_NAME};
/// Contains the entire state of a running Minecraft server, accessible from
/// within the [update](crate::config::Config::update) loop.
@ -95,6 +95,9 @@ struct SharedServerInner<C: Config> {
_tokio_runtime: Option<Runtime>,
dimensions: Vec<Dimension>,
biomes: Vec<Biome>,
/// Contains info about dimensions, biomes, and chats.
/// Sent to all clients when joining.
registry_codec: Compound,
/// The instant the server was started.
start_instant: Instant,
/// Receiver for new clients past the login stage.
@ -241,6 +244,10 @@ impl<C: Config> SharedServer<C> {
.map(|(i, b)| (BiomeId(i as u16), b))
}
pub(crate) fn registry_codec(&self) -> &Compound {
&self.0.registry_codec
}
/// Returns the instant the server was started.
pub fn start_instant(&self) -> Instant {
self.0.start_instant
@ -315,62 +322,12 @@ fn setup_server<C: Config>(cfg: C) -> anyhow::Result<SharedServer<C>> {
);
let tokio_handle = cfg.tokio_handle();
let dimensions = cfg.dimensions();
ensure!(
!dimensions.is_empty(),
"at least one dimension must be added"
);
ensure!(
dimensions.len() <= u16::MAX as usize,
"more than u16::MAX dimensions added"
);
for (i, dim) in dimensions.iter().enumerate() {
ensure!(
dim.min_y % 16 == 0 && (-2032..=2016).contains(&dim.min_y),
"invalid min_y in dimension #{i}",
);
ensure!(
dim.height % 16 == 0
&& (0..=4064).contains(&dim.height)
&& dim.min_y.saturating_add(dim.height) <= 2032,
"invalid height in dimension #{i}",
);
ensure!(
(0.0..=1.0).contains(&dim.ambient_light),
"ambient_light is out of range in dimension #{i}",
);
if let Some(fixed_time) = dim.fixed_time {
assert!(
(0..=24_000).contains(&fixed_time),
"fixed_time is out of range in dimension #{i}",
);
}
}
validate_dimensions(&dimensions)?;
let biomes = cfg.biomes();
ensure!(!biomes.is_empty(), "at least one biome must be added");
ensure!(
biomes.len() <= u16::MAX as usize,
"more than u16::MAX biomes added"
);
let mut names = HashSet::new();
for biome in biomes.iter() {
ensure!(
names.insert(biome.name.clone()),
"biome \"{}\" already added",
biome.name
);
}
validate_biomes(&biomes)?;
let rsa_key = RsaPrivateKey::new(&mut OsRng, 1024)?;
@ -391,6 +348,8 @@ fn setup_server<C: Config>(cfg: C) -> anyhow::Result<SharedServer<C>> {
None => tokio_handle.unwrap(),
};
let registry_codec = make_registry_codec(&dimensions, &biomes);
let server = SharedServerInner {
cfg,
address,
@ -403,6 +362,7 @@ fn setup_server<C: Config>(cfg: C) -> anyhow::Result<SharedServer<C>> {
_tokio_runtime: runtime,
dimensions,
biomes,
registry_codec,
start_instant: Instant::now(),
new_clients_rx,
new_clients_tx,
@ -417,6 +377,45 @@ fn setup_server<C: Config>(cfg: C) -> anyhow::Result<SharedServer<C>> {
Ok(SharedServer(Arc::new(server)))
}
fn make_registry_codec(dimensions: &[Dimension], biomes: &[Biome]) -> Compound {
compound! {
ident!("dimension_type") => compound! {
"type" => ident!("dimension_type"),
"value" => List::Compound(dimensions.iter().enumerate().map(|(id, dim)| compound! {
"name" => DimensionId(id as u16).dimension_type_name(),
"id" => id as i32,
"element" => dim.to_dimension_registry_item(),
}).collect()),
},
ident!("worldgen/biome") => compound! {
"type" => ident!("worldgen/biome"),
"value" => {
let mut biomes: Vec<_> = biomes
.iter()
.enumerate()
.map(|(id, biome)| biome.to_biome_registry_item(id as i32))
.collect();
// The client needs a biome named "minecraft:plains" in the registry to
// connect. This is probably a bug in the client.
//
// If the issue is resolved, remove this if.
if !biomes.iter().any(|b| b["name"] == "plains".into()) {
let biome = Biome::default();
assert_eq!(biome.name, ident!("plains"));
biomes.push(biome.to_biome_registry_item(biomes.len() as i32));
}
List::Compound(biomes)
}
},
ident!("chat_type_registry") => compound! {
"type" => ident!("chat_type"),
"value" => List::Compound(Vec::new()),
},
}
}
fn do_update_loop<C: Config>(server: &mut Server<C>) -> ShutdownResult {
let mut tick_start = Instant::now();