Dynamically build lobby dimension from probed server dimension codec

This commit is contained in:
timvisee 2021-11-22 23:53:54 +01:00
parent f513957bff
commit cf6bd526d9
No known key found for this signature in database
GPG Key ID: B8DB720BC383E172
3 changed files with 119 additions and 27 deletions

View File

@ -30,7 +30,7 @@ use tokio::time;
use crate::config::*;
use crate::forge;
use crate::mc::{self, uuid};
use crate::mc::{self, dimension, uuid};
use crate::net;
use crate::proto;
use crate::proto::client::{Client, ClientInfo, ClientState};
@ -273,13 +273,14 @@ async fn send_lobby_join_game(
writer: &mut WriteHalf<'_>,
server: &Server,
) -> Result<(), ()> {
// Grab probed dimension codec
// Get dimension codec and build lobby dimension
let dimension_codec: CompoundTag =
if let Some(ref join_game) = server.probed_join_game.lock().await.as_ref() {
join_game.dimension_codec.clone()
} else {
snbt_to_compound_tag(include_str!("../res/dimension_codec.snbt"))
dimension::lobby_default_dimension_codec()
};
let dimension: CompoundTag = dimension::lobby_dimension(&dimension_codec);
// Send Minecrafts default states, slightly customised for lobby world
packet::write_packet(
@ -299,7 +300,7 @@ async fn send_lobby_join_game(
"minecraft:the_end".into(),
],
dimension_codec,
dimension: snbt_to_compound_tag(include_str!("../res/dimension.snbt")),
dimension,
world_name: "lazymc:lobby".into(),
hashed_seed: 0,
max_players: status.as_ref().map(|s| s.players.max as i32).unwrap_or(20),
@ -858,26 +859,3 @@ async fn drain_stream(reader: &mut ReadHalf<'_>) -> Result<(), ()> {
}
}
}
/// Read NBT CompoundTag from SNBT.
fn snbt_to_compound_tag(data: &str) -> CompoundTag {
use quartz_nbt::io::{write_nbt, Flavor};
use quartz_nbt::snbt;
// Parse SNBT data
let compound = snbt::parse(data).expect("failed to parse SNBT");
// Encode to binary
let mut binary = Vec::new();
write_nbt(&mut binary, None, &compound, Flavor::Uncompressed)
.expect("failed to encode NBT CompoundTag as binary");
// Parse binary with usable NBT create
bin_to_compound_tag(&mut &*binary)
}
/// Read NBT CompoundTag from SNBT.
fn bin_to_compound_tag(data: &[u8]) -> CompoundTag {
use nbt::decode::read_compound_tag;
read_compound_tag(&mut &*data).unwrap()
}

112
src/mc/dimension.rs Normal file
View File

@ -0,0 +1,112 @@
use nbt::CompoundTag;
/// Create lobby dimension from the given codec.
///
/// This creates a dimension suitable for the lobby that should be suitable for the current server
/// version.
pub fn lobby_dimension(codec: &CompoundTag) -> CompoundTag {
// Retrieve dimension types from codec
let dimension_types = match codec.get_compound_tag("minecraft:dimension_type") {
Ok(types) => types,
Err(_) => return lobby_default_dimension(),
};
// Get base dimension
let mut base = lobby_base_dimension(dimension_types);
// Change known properties on base to get more desirable dimension
base.insert_i8("piglin_safe", 1);
base.insert_f32("ambient_light", 0.0);
// base.insert_str("infiniburn", "minecraft:infiniburn_end");
base.insert_i8("respawn_anchor_works", 0);
base.insert_i8("has_skylight", 0);
base.insert_i8("bed_works", 0);
base.insert_str("effects", "minecraft:the_end");
base.insert_i64("fixed_time", 0);
base.insert_i8("has_raids", 0);
base.insert_i32("min_y", 0);
base.insert_i32("height", 1);
base.insert_i32("logical_height", 1);
base.insert_f64("coordinate_scale", 1.0);
base.insert_i8("ultrawarm", 0);
base.insert_i8("has_ceiling", 0);
base
}
/// Get lobby base dimension.
///
/// This retrieves the most desirable dimension to use as base for the lobby from the given list of
/// `dimension_types`.
///
/// If no dimension is found in the given tag, a default one will be returned.
fn lobby_base_dimension(dimension_types: &CompoundTag) -> CompoundTag {
// The dimension types we prefer the most, in order
let preferred = vec![
"minecraft:the_end",
"minecraft:the_nether",
"minecraft:the_overworld",
];
let dimensions = dimension_types.get_compound_tag_vec("value").unwrap();
for name in preferred {
if let Some(dimension) = dimensions
.iter()
.find(|d| d.get_str("name").map(|n| n == name).unwrap_or(false))
{
if let Ok(dimension) = dimension.get_compound_tag("element") {
return dimension.clone();
}
}
}
// Return first dimension
if let Some(dimension) = dimensions.first() {
if let Ok(dimension) = dimension.get_compound_tag("element") {
return dimension.clone();
}
}
// Fall back to default dimension
lobby_default_dimension()
}
/// Default lobby dimension codec from resource file.
///
/// This likely breaks if the Minecraft version doesn't match exactly.
/// Please use an up-to-date coded from the server instead.
pub fn lobby_default_dimension_codec() -> CompoundTag {
snbt_to_compound_tag(include_str!("../../res/dimension_codec.snbt"))
}
/// Default lobby dimension from resource file.
///
/// This likely breaks if the Minecraft version doesn't match exactly.
/// Please use `lobby_dimension` with an up-to-date coded from the server instead.
fn lobby_default_dimension() -> CompoundTag {
snbt_to_compound_tag(include_str!("../../res/dimension.snbt"))
}
/// Read NBT CompoundTag from SNBT.
fn snbt_to_compound_tag(data: &str) -> CompoundTag {
use quartz_nbt::io::{write_nbt, Flavor};
use quartz_nbt::snbt;
// Parse SNBT data
let compound = snbt::parse(data).expect("failed to parse SNBT");
// Encode to binary
let mut binary = Vec::new();
write_nbt(&mut binary, None, &compound, Flavor::Uncompressed)
.expect("failed to encode NBT CompoundTag as binary");
// Parse binary with usable NBT create
bin_to_compound_tag(&mut &*binary)
}
/// Read NBT CompoundTag from SNBT.
fn bin_to_compound_tag(data: &[u8]) -> CompoundTag {
use nbt::decode::read_compound_tag;
read_compound_tag(&mut &*data).unwrap()
}

View File

@ -1,4 +1,6 @@
pub mod ban;
#[cfg(feature = "lobby")]
pub mod dimension;
pub mod favicon;
#[cfg(feature = "rcon")]
pub mod rcon;