feat: inital mob spawn rules
This commit is contained in:
parent
c23c7c429d
commit
2bfe2335f0
@ -23,9 +23,7 @@ pub fn build() -> TokenStream {
|
|||||||
|
|
||||||
let replaceable_str = config["replaceable"].as_str().unwrap_or("");
|
let replaceable_str = config["replaceable"].as_str().unwrap_or("");
|
||||||
let replaceable = if let Some(tag_name) = replaceable_str.strip_prefix('#') {
|
let replaceable = if let Some(tag_name) = replaceable_str.strip_prefix('#') {
|
||||||
let tag_name = tag_name
|
let tag_name = tag_name.to_uppercase().replace([':', '.', '-'], "_");
|
||||||
.to_uppercase()
|
|
||||||
.replace([':', '.', '-'], "_");
|
|
||||||
let tag_ident = format_ident!("{}", tag_name);
|
let tag_ident = format_ident!("{}", tag_name);
|
||||||
quote! { crate::tag::Block::#tag_ident }
|
quote! { crate::tag::Block::#tag_ident }
|
||||||
} else {
|
} else {
|
||||||
@ -70,10 +68,12 @@ pub fn build() -> TokenStream {
|
|||||||
let width_smoothness = shape["width_smoothness"].as_i64().unwrap_or(0) as i32;
|
let width_smoothness = shape["width_smoothness"].as_i64().unwrap_or(0) as i32;
|
||||||
let horizontal_radius_factor =
|
let horizontal_radius_factor =
|
||||||
value_to_float_provider(&shape["horizontal_radius_factor"]);
|
value_to_float_provider(&shape["horizontal_radius_factor"]);
|
||||||
let vertical_radius_default_factor =
|
let vertical_radius_default_factor = shape["vertical_radius_default_factor"]
|
||||||
shape["vertical_radius_default_factor"].as_f64().unwrap_or(0.0) as f32;
|
.as_f64()
|
||||||
let vertical_radius_center_factor =
|
.unwrap_or(0.0) as f32;
|
||||||
shape["vertical_radius_center_factor"].as_f64().unwrap_or(0.0) as f32;
|
let vertical_radius_center_factor = shape["vertical_radius_center_factor"]
|
||||||
|
.as_f64()
|
||||||
|
.unwrap_or(0.0) as f32;
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
CarverAdditionalConfig::Canyon(CanyonCarverConfig {
|
CarverAdditionalConfig::Canyon(CanyonCarverConfig {
|
||||||
|
|||||||
@ -641,10 +641,10 @@ pub fn value_to_configured_feature(v: &Value) -> TokenStream {
|
|||||||
let root_radius = config["root_radius"].as_i64().unwrap_or(0) as i32;
|
let root_radius = config["root_radius"].as_i64().unwrap_or(0) as i32;
|
||||||
let root_replaceable = value_to_block_predicate(&config["root_replaceable"]);
|
let root_replaceable = value_to_block_predicate(&config["root_replaceable"]);
|
||||||
let root_state_provider = value_to_block_state_provider(&config["root_state_provider"]);
|
let root_state_provider = value_to_block_state_provider(&config["root_state_provider"]);
|
||||||
let root_placement_attempts = config["root_placement_attempts"].as_i64().unwrap_or(0)
|
let root_placement_attempts =
|
||||||
as i32;
|
config["root_placement_attempts"].as_i64().unwrap_or(0) as i32;
|
||||||
let root_column_max_height = config["root_column_max_height"].as_i64().unwrap_or(0)
|
let root_column_max_height =
|
||||||
as i32;
|
config["root_column_max_height"].as_i64().unwrap_or(0) as i32;
|
||||||
let hanging_root_radius = config["hanging_root_radius"].as_i64().unwrap_or(0) as i32;
|
let hanging_root_radius = config["hanging_root_radius"].as_i64().unwrap_or(0) as i32;
|
||||||
let hanging_roots_vertical_span = config["hanging_roots_vertical_span"]
|
let hanging_roots_vertical_span = config["hanging_roots_vertical_span"]
|
||||||
.as_i64()
|
.as_i64()
|
||||||
@ -1108,8 +1108,9 @@ fn value_to_trunk_placer(v: &Value) -> TokenStream {
|
|||||||
}
|
}
|
||||||
"minecraft:upwards_branching_trunk_placer" => {
|
"minecraft:upwards_branching_trunk_placer" => {
|
||||||
let extra_branch_steps = value_to_int_provider(&v["extra_branch_steps"]);
|
let extra_branch_steps = value_to_int_provider(&v["extra_branch_steps"]);
|
||||||
let place_branch_per_log_probability =
|
let place_branch_per_log_probability = v["place_branch_per_log_probability"]
|
||||||
v["place_branch_per_log_probability"].as_f64().unwrap_or(0.0) as f32;
|
.as_f64()
|
||||||
|
.unwrap_or(0.0) as f32;
|
||||||
let extra_branch_length = value_to_int_provider(&v["extra_branch_length"]);
|
let extra_branch_length = value_to_int_provider(&v["extra_branch_length"]);
|
||||||
let can_grow_through = value_to_block_list(&v["can_grow_through"]);
|
let can_grow_through = value_to_block_list(&v["can_grow_through"]);
|
||||||
quote! {
|
quote! {
|
||||||
@ -1127,7 +1128,8 @@ fn value_to_trunk_placer(v: &Value) -> TokenStream {
|
|||||||
let branch_start_offset_v = &v["branch_start_offset_from_top"];
|
let branch_start_offset_v = &v["branch_start_offset_from_top"];
|
||||||
let min = branch_start_offset_v["min_inclusive"].as_i64().unwrap_or(0) as i32;
|
let min = branch_start_offset_v["min_inclusive"].as_i64().unwrap_or(0) as i32;
|
||||||
let max = branch_start_offset_v["max_inclusive"].as_i64().unwrap_or(0) as i32;
|
let max = branch_start_offset_v["max_inclusive"].as_i64().unwrap_or(0) as i32;
|
||||||
let branch_end_offset_from_top = value_to_int_provider(&v["branch_end_offset_from_top"]);
|
let branch_end_offset_from_top =
|
||||||
|
value_to_int_provider(&v["branch_end_offset_from_top"]);
|
||||||
quote! {
|
quote! {
|
||||||
TrunkType::Cherry(CherryTrunkPlacer {
|
TrunkType::Cherry(CherryTrunkPlacer {
|
||||||
branch_count: #branch_count,
|
branch_count: #branch_count,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
use std::{collections::BTreeMap, fs};
|
use std::{collections::BTreeMap, fs};
|
||||||
|
|
||||||
|
use crate::placed_feature::value_to_int_provider;
|
||||||
use heck::ToShoutySnakeCase;
|
use heck::ToShoutySnakeCase;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
@ -36,6 +37,8 @@ pub struct Dimension {
|
|||||||
pub logical_height: i32,
|
pub logical_height: i32,
|
||||||
/// Tag key for blocks that act as infinite burn sources (e.g. `"minecraft:infiniburn_overworld"`).
|
/// Tag key for blocks that act as infinite burn sources (e.g. `"minecraft:infiniburn_overworld"`).
|
||||||
pub infiniburn: String,
|
pub infiniburn: String,
|
||||||
|
pub monster_spawn_light_level: serde_json::Value,
|
||||||
|
pub monster_spawn_block_light_limit: u8,
|
||||||
/// Fixed day-time value in this dimension, or `None` if time progresses normally.
|
/// Fixed day-time value in this dimension, or `None` if time progresses normally.
|
||||||
#[serde(rename = "fixed_time")]
|
#[serde(rename = "fixed_time")]
|
||||||
pub fixed_time: Option<i64>,
|
pub fixed_time: Option<i64>,
|
||||||
@ -47,26 +50,6 @@ pub struct Dimension {
|
|||||||
pub timelines: Option<String>,
|
pub timelines: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[derive(Clone, PartialEq, Deserialize)]
|
|
||||||
// #[serde(untagged)]
|
|
||||||
// pub enum MonsterSpawnLightLevel {
|
|
||||||
// Int(i32),
|
|
||||||
// Tagged(MonsterSpawnLightLevelTagged),
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[derive(Clone, PartialEq, Deserialize)]
|
|
||||||
// pub struct MonsterSpawnLightLevelTagged {
|
|
||||||
// min_inclusive: i32,
|
|
||||||
// max_inclusive: i32,
|
|
||||||
// r#type: String,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl From<i32> for MonsterSpawnLightLevel {
|
|
||||||
// fn from(value: i32) -> Self {
|
|
||||||
// Self::Int(value)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// Generates the `TokenStream` for the `Dimension` struct, its constants, and `from_name` lookup.
|
/// Generates the `TokenStream` for the `Dimension` struct, its constants, and `from_name` lookup.
|
||||||
pub fn build() -> TokenStream {
|
pub fn build() -> TokenStream {
|
||||||
let dimensions: BTreeMap<String, Dimension> = serde_json::from_str(
|
let dimensions: BTreeMap<String, Dimension> = serde_json::from_str(
|
||||||
@ -113,6 +96,9 @@ pub fn build() -> TokenStream {
|
|||||||
quote! { None }
|
quote! { None }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let monster_spawn_light_level = value_to_int_provider(&dim.monster_spawn_light_level);
|
||||||
|
|
||||||
|
let monster_spawn_block_light_limit = dim.monster_spawn_block_light_limit;
|
||||||
let ambient_light = dim.ambient_light;
|
let ambient_light = dim.ambient_light;
|
||||||
let coordinate_scale = dim.coordinate_scale;
|
let coordinate_scale = dim.coordinate_scale;
|
||||||
let height = dim.height;
|
let height = dim.height;
|
||||||
@ -174,6 +160,8 @@ pub fn build() -> TokenStream {
|
|||||||
logical_height: #logical_height,
|
logical_height: #logical_height,
|
||||||
infiniburn: #infiniburn,
|
infiniburn: #infiniburn,
|
||||||
ambient_light: #ambient_light,
|
ambient_light: #ambient_light,
|
||||||
|
monster_spawn_light_level: #monster_spawn_light_level,
|
||||||
|
monster_spawn_block_light_limit: #monster_spawn_block_light_limit,
|
||||||
sky_color: #sky_color_literal,
|
sky_color: #sky_color_literal,
|
||||||
fog_color: #fog_color_literal,
|
fog_color: #fog_color_literal,
|
||||||
cloud_color: #cloud_color_literal,
|
cloud_color: #cloud_color_literal,
|
||||||
@ -187,7 +175,13 @@ pub fn build() -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
#[derive(Debug, Clone, Copy)]
|
use pumpkin_util::math::int_provider::{
|
||||||
|
BiasedToBottomIntProvider, ClampedIntProvider, TrapezoidIntProvider, ClampedNormalIntProvider,
|
||||||
|
ConstantIntProvider, IntProvider, NormalIntProvider, UniformIntProvider,
|
||||||
|
WeightedEntry, WeightedListIntProvider,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Dimension {
|
pub struct Dimension {
|
||||||
pub id: u8,
|
pub id: u8,
|
||||||
pub minecraft_name: &'static str,
|
pub minecraft_name: &'static str,
|
||||||
@ -200,6 +194,8 @@ pub fn build() -> TokenStream {
|
|||||||
pub logical_height: i32,
|
pub logical_height: i32,
|
||||||
pub infiniburn: &'static str,
|
pub infiniburn: &'static str,
|
||||||
pub ambient_light: f32,
|
pub ambient_light: f32,
|
||||||
|
pub monster_spawn_light_level: IntProvider,
|
||||||
|
pub monster_spawn_block_light_limit: u8,
|
||||||
pub sky_color: Option<i32>,
|
pub sky_color: Option<i32>,
|
||||||
pub fog_color: Option<i32>,
|
pub fog_color: Option<i32>,
|
||||||
pub cloud_color: Option<i32>,
|
pub cloud_color: Option<i32>,
|
||||||
|
|||||||
@ -121,7 +121,12 @@ pub fn build() -> TokenStream {
|
|||||||
|
|
||||||
let slots = enchantment.slots;
|
let slots = enchantment.slots;
|
||||||
let slots = slots.iter().map(AttributeModifierSlot::to_tokens);
|
let slots = slots.iter().map(AttributeModifierSlot::to_tokens);
|
||||||
let Translate { translate, bedrock_translate: _, with: _ } = &*enchantment.description.0.content else {
|
let Translate {
|
||||||
|
translate,
|
||||||
|
bedrock_translate: _,
|
||||||
|
with: _,
|
||||||
|
} = &*enchantment.description.0.content
|
||||||
|
else {
|
||||||
panic!()
|
panic!()
|
||||||
};
|
};
|
||||||
let translate = translate.to_string();
|
let translate = translate.to_string();
|
||||||
|
|||||||
@ -13,7 +13,10 @@ pub fn build() -> TokenStream {
|
|||||||
let mut match_variants = TokenStream::new();
|
let mut match_variants = TokenStream::new();
|
||||||
|
|
||||||
for (i, (raw_name, parameter)) in json.iter().enumerate() {
|
for (i, (raw_name, parameter)) in json.iter().enumerate() {
|
||||||
let name = raw_name.strip_prefix("minecraft:").unwrap().replace("/", "_");
|
let name = raw_name
|
||||||
|
.strip_prefix("minecraft:")
|
||||||
|
.unwrap()
|
||||||
|
.replace("/", "_");
|
||||||
let name_ident = format_ident!("{}", name.to_uppercase());
|
let name_ident = format_ident!("{}", name.to_uppercase());
|
||||||
|
|
||||||
// 1. MD5 Pre-calculation
|
// 1. MD5 Pre-calculation
|
||||||
|
|||||||
@ -148,7 +148,6 @@ impl ParsedMappings {
|
|||||||
for (mapped_id, id) in (shifts_to[index]..).zip(*from..to) {
|
for (mapped_id, id) in (shifts_to[index]..).zip(*from..to) {
|
||||||
result[id as usize] = mapped_id;
|
result[id as usize] = mapped_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
|
|||||||
@ -22,7 +22,8 @@ pub fn build() -> TokenStream {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let bedrock_content = fs::read_to_string("../assets/en_us_bedrock.lang").expect("en_us_bedrock is missing");
|
let bedrock_content =
|
||||||
|
fs::read_to_string("../assets/en_us_bedrock.lang").expect("en_us_bedrock is missing");
|
||||||
let mut bedrock_constants = TokenStream::new();
|
let mut bedrock_constants = TokenStream::new();
|
||||||
|
|
||||||
for line in bedrock_content.lines() {
|
for line in bedrock_content.lines() {
|
||||||
|
|||||||
@ -70,7 +70,12 @@ pub fn build() -> TokenStream {
|
|||||||
let format_trade = |trade: &TradeJson| {
|
let format_trade = |trade: &TradeJson| {
|
||||||
let wants_item = format_ident!(
|
let wants_item = format_ident!(
|
||||||
"{}",
|
"{}",
|
||||||
trade.wants.id.strip_prefix("minecraft:").unwrap_or(&trade.wants.id).to_shouty_snake_case()
|
trade
|
||||||
|
.wants
|
||||||
|
.id
|
||||||
|
.strip_prefix("minecraft:")
|
||||||
|
.unwrap_or(&trade.wants.id)
|
||||||
|
.to_shouty_snake_case()
|
||||||
);
|
);
|
||||||
let wants_count = trade.wants.count.unwrap_or(1.0) as i32;
|
let wants_count = trade.wants.count.unwrap_or(1.0) as i32;
|
||||||
let wants = quote! { VillagerTradeItem { item: &crate::item::Item::#wants_item, count: #wants_count } };
|
let wants = quote! { VillagerTradeItem { item: &crate::item::Item::#wants_item, count: #wants_count } };
|
||||||
@ -78,7 +83,9 @@ pub fn build() -> TokenStream {
|
|||||||
let wants_b = if let Some(b) = &trade.wants_b {
|
let wants_b = if let Some(b) = &trade.wants_b {
|
||||||
let item = format_ident!(
|
let item = format_ident!(
|
||||||
"{}",
|
"{}",
|
||||||
b.id.strip_prefix("minecraft:").unwrap_or(&b.id).to_shouty_snake_case()
|
b.id.strip_prefix("minecraft:")
|
||||||
|
.unwrap_or(&b.id)
|
||||||
|
.to_shouty_snake_case()
|
||||||
);
|
);
|
||||||
let count = b.count.unwrap_or(1.0) as i32;
|
let count = b.count.unwrap_or(1.0) as i32;
|
||||||
quote! { Some(VillagerTradeItem { item: &crate::item::Item::#item, count: #count }) }
|
quote! { Some(VillagerTradeItem { item: &crate::item::Item::#item, count: #count }) }
|
||||||
@ -88,7 +95,12 @@ pub fn build() -> TokenStream {
|
|||||||
|
|
||||||
let gives_item = format_ident!(
|
let gives_item = format_ident!(
|
||||||
"{}",
|
"{}",
|
||||||
trade.gives.id.strip_prefix("minecraft:").unwrap_or(&trade.gives.id).to_shouty_snake_case()
|
trade
|
||||||
|
.gives
|
||||||
|
.id
|
||||||
|
.strip_prefix("minecraft:")
|
||||||
|
.unwrap_or(&trade.gives.id)
|
||||||
|
.to_shouty_snake_case()
|
||||||
);
|
);
|
||||||
let gives_count = trade.gives.count.unwrap_or(1.0) as i32;
|
let gives_count = trade.gives.count.unwrap_or(1.0) as i32;
|
||||||
let gives = quote! { VillagerTradeItem { item: &crate::item::Item::#gives_item, count: #gives_count } };
|
let gives = quote! { VillagerTradeItem { item: &crate::item::Item::#gives_item, count: #gives_count } };
|
||||||
@ -132,7 +144,9 @@ pub fn build() -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fallback for smiths
|
// Fallback for smiths
|
||||||
if matching_trades.is_empty() && (prof == "armorer" || prof == "toolsmith" || prof == "weaponsmith") {
|
if matching_trades.is_empty()
|
||||||
|
&& (prof == "armorer" || prof == "toolsmith" || prof == "weaponsmith")
|
||||||
|
{
|
||||||
let smith_prefix = format!("smith/{level_str}/");
|
let smith_prefix = format!("smith/{level_str}/");
|
||||||
for (key, trade) in &data.villager_trades {
|
for (key, trade) in &data.villager_trades {
|
||||||
if key.starts_with(&smith_prefix) {
|
if key.starts_with(&smith_prefix) {
|
||||||
@ -199,7 +213,11 @@ pub fn build() -> TokenStream {
|
|||||||
for (level_str, set_key) in &prof_data.trade_sets {
|
for (level_str, set_key) in &prof_data.trade_sets {
|
||||||
let level = level_str.parse::<i32>().unwrap();
|
let level = level_str.parse::<i32>().unwrap();
|
||||||
let set_key_clean = set_key.strip_prefix("minecraft:").unwrap_or(set_key);
|
let set_key_clean = set_key.strip_prefix("minecraft:").unwrap_or(set_key);
|
||||||
if let Some(trades_ident) = data.trade_sets.get(set_key_clean).and_then(|set| generated_trade_sets.get(&set.trades)) {
|
if let Some(trades_ident) = data
|
||||||
|
.trade_sets
|
||||||
|
.get(set_key_clean)
|
||||||
|
.and_then(|set| generated_trade_sets.get(&set.trades))
|
||||||
|
{
|
||||||
let set = data.trade_sets.get(set_key_clean).unwrap();
|
let set = data.trade_sets.get(set_key_clean).unwrap();
|
||||||
let amount = set.amount as i32;
|
let amount = set.amount as i32;
|
||||||
level_matches.push(quote! { #level => Some(VillagerTradeSet { trades: #trades_ident, amount: #amount }) });
|
level_matches.push(quote! { #level => Some(VillagerTradeSet { trades: #trades_ident, amount: #amount }) });
|
||||||
|
|||||||
@ -1,24 +1,45 @@
|
|||||||
use crate::wit::utils::map_type;
|
use crate::wit::utils::map_type;
|
||||||
|
use heck::ToKebabCase;
|
||||||
|
use semver::Version;
|
||||||
use std::{fs, path::Path};
|
use std::{fs, path::Path};
|
||||||
use syn::{Fields, Item};
|
use syn::{Fields, Item};
|
||||||
use wit_encoder::{Field, Interface, Package, PackageName, Record, Type as WitType, TypeDef, TypeDefKind, Variant, VariantCase};
|
use wit_encoder::{
|
||||||
use semver::Version;
|
Field, Interface, Package, PackageName, Record, Type as WitType, TypeDef, TypeDefKind, Variant,
|
||||||
use heck::ToKebabCase;
|
VariantCase,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn build() -> String {
|
pub fn build() -> String {
|
||||||
let mut package = Package::new(PackageName::new("pumpkin", "plugin", Some(Version::new(0, 1, 0))));
|
let mut package = Package::new(PackageName::new(
|
||||||
|
"pumpkin",
|
||||||
|
"plugin",
|
||||||
|
Some(Version::new(0, 1, 0)),
|
||||||
|
));
|
||||||
let mut interface = Interface::new("bedrock-packets");
|
let mut interface = Interface::new("bedrock-packets");
|
||||||
|
|
||||||
let mut serverbound_variant = Variant::empty();
|
let mut serverbound_variant = Variant::empty();
|
||||||
let mut clientbound_variant = Variant::empty();
|
let mut clientbound_variant = Variant::empty();
|
||||||
|
|
||||||
// Process serverbound packets
|
// Process serverbound packets
|
||||||
process_packets("../pumpkin-protocol/src/bedrock/server", &mut interface, &mut serverbound_variant);
|
process_packets(
|
||||||
|
"../pumpkin-protocol/src/bedrock/server",
|
||||||
|
&mut interface,
|
||||||
|
&mut serverbound_variant,
|
||||||
|
);
|
||||||
// Process clientbound packets
|
// Process clientbound packets
|
||||||
process_packets("../pumpkin-protocol/src/bedrock/client", &mut interface, &mut clientbound_variant);
|
process_packets(
|
||||||
|
"../pumpkin-protocol/src/bedrock/client",
|
||||||
|
&mut interface,
|
||||||
|
&mut clientbound_variant,
|
||||||
|
);
|
||||||
|
|
||||||
interface.type_def(TypeDef::new("serverbound-packet", TypeDefKind::Variant(serverbound_variant)));
|
interface.type_def(TypeDef::new(
|
||||||
interface.type_def(TypeDef::new("clientbound-packet", TypeDefKind::Variant(clientbound_variant)));
|
"serverbound-packet",
|
||||||
|
TypeDefKind::Variant(serverbound_variant),
|
||||||
|
));
|
||||||
|
interface.type_def(TypeDef::new(
|
||||||
|
"clientbound-packet",
|
||||||
|
TypeDefKind::Variant(clientbound_variant),
|
||||||
|
));
|
||||||
|
|
||||||
package.interface(interface);
|
package.interface(interface);
|
||||||
package.to_string()
|
package.to_string()
|
||||||
@ -26,7 +47,9 @@ pub fn build() -> String {
|
|||||||
|
|
||||||
fn process_packets(dir: &str, interface: &mut Interface, variant: &mut Variant) {
|
fn process_packets(dir: &str, interface: &mut Interface, variant: &mut Variant) {
|
||||||
let paths = fs::read_dir(dir).expect("Failed to read packet directory");
|
let paths = fs::read_dir(dir).expect("Failed to read packet directory");
|
||||||
let mut sorted_paths: Vec<_> = paths.map(|e| e.expect("Failed to read entry").path()).collect();
|
let mut sorted_paths: Vec<_> = paths
|
||||||
|
.map(|e| e.expect("Failed to read entry").path())
|
||||||
|
.collect();
|
||||||
sorted_paths.sort();
|
sorted_paths.sort();
|
||||||
|
|
||||||
for path in sorted_paths {
|
for path in sorted_paths {
|
||||||
@ -34,7 +57,9 @@ fn process_packets(dir: &str, interface: &mut Interface, variant: &mut Variant)
|
|||||||
process_packets(path.to_str().unwrap(), interface, variant);
|
process_packets(path.to_str().unwrap(), interface, variant);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if path.extension().is_some_and(|ext| ext == "rs") && path.file_name().is_some_and(|name| name != "mod.rs") {
|
if path.extension().is_some_and(|ext| ext == "rs")
|
||||||
|
&& path.file_name().is_some_and(|name| name != "mod.rs")
|
||||||
|
{
|
||||||
parse_packet_file(&path, interface, variant);
|
parse_packet_file(&path, interface, variant);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,8 +88,14 @@ fn parse_packet_file(path: &Path, interface: &mut Interface, variant: &mut Varia
|
|||||||
}
|
}
|
||||||
|
|
||||||
let wit_name = struct_name.to_kebab_case();
|
let wit_name = struct_name.to_kebab_case();
|
||||||
interface.type_def(TypeDef::new(wit_name.clone(), TypeDefKind::Record(Record::new(fields_list))));
|
interface.type_def(TypeDef::new(
|
||||||
variant.case(VariantCase::value(wit_name.clone(), WitType::named(wit_name)));
|
wit_name.clone(),
|
||||||
|
TypeDefKind::Record(Record::new(fields_list)),
|
||||||
|
));
|
||||||
|
variant.case(VariantCase::value(
|
||||||
|
wit_name.clone(),
|
||||||
|
WitType::named(wit_name),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
|
use semver::Version;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use semver::Version;
|
|
||||||
use wit_encoder::{Enum, Interface, Package, PackageName, TypeDef, TypeDefKind};
|
use wit_encoder::{Enum, Interface, Package, PackageName, TypeDef, TypeDefKind};
|
||||||
|
|
||||||
pub fn build() -> String {
|
pub fn build() -> String {
|
||||||
@ -8,7 +8,11 @@ pub fn build() -> String {
|
|||||||
serde_json::from_str(&fs::read_to_string("../assets/entities.json").unwrap())
|
serde_json::from_str(&fs::read_to_string("../assets/entities.json").unwrap())
|
||||||
.expect("Failed to parse entities.json");
|
.expect("Failed to parse entities.json");
|
||||||
|
|
||||||
let mut package = Package::new(PackageName::new("pumpkin", "plugin", Some(Version::new(0, 1, 0))));
|
let mut package = Package::new(PackageName::new(
|
||||||
|
"pumpkin",
|
||||||
|
"plugin",
|
||||||
|
Some(Version::new(0, 1, 0)),
|
||||||
|
));
|
||||||
let mut interface = Interface::new("entity-types");
|
let mut interface = Interface::new("entity-types");
|
||||||
|
|
||||||
let mut entity_type_enum = Enum::empty();
|
let mut entity_type_enum = Enum::empty();
|
||||||
@ -16,7 +20,10 @@ pub fn build() -> String {
|
|||||||
entity_type_enum.case(name.replace('_', "-"));
|
entity_type_enum.case(name.replace('_', "-"));
|
||||||
}
|
}
|
||||||
|
|
||||||
interface.type_def(TypeDef::new("entity-type", TypeDefKind::Enum(entity_type_enum)));
|
interface.type_def(TypeDef::new(
|
||||||
|
"entity-type",
|
||||||
|
TypeDefKind::Enum(entity_type_enum),
|
||||||
|
));
|
||||||
package.interface(interface);
|
package.interface(interface);
|
||||||
|
|
||||||
package.to_string()
|
package.to_string()
|
||||||
|
|||||||
@ -1,24 +1,45 @@
|
|||||||
use crate::wit::utils::map_type;
|
use crate::wit::utils::map_type;
|
||||||
|
use heck::ToKebabCase;
|
||||||
|
use semver::Version;
|
||||||
use std::{fs, path::Path};
|
use std::{fs, path::Path};
|
||||||
use syn::{Fields, Item};
|
use syn::{Fields, Item};
|
||||||
use wit_encoder::{Field, Interface, Package, PackageName, Record, Type as WitType, TypeDef, TypeDefKind, Variant, VariantCase};
|
use wit_encoder::{
|
||||||
use semver::Version;
|
Field, Interface, Package, PackageName, Record, Type as WitType, TypeDef, TypeDefKind, Variant,
|
||||||
use heck::ToKebabCase;
|
VariantCase,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn build() -> String {
|
pub fn build() -> String {
|
||||||
let mut package = Package::new(PackageName::new("pumpkin", "plugin", Some(Version::new(0, 1, 0))));
|
let mut package = Package::new(PackageName::new(
|
||||||
|
"pumpkin",
|
||||||
|
"plugin",
|
||||||
|
Some(Version::new(0, 1, 0)),
|
||||||
|
));
|
||||||
let mut interface = Interface::new("java-packets");
|
let mut interface = Interface::new("java-packets");
|
||||||
|
|
||||||
let mut serverbound_variant = Variant::empty();
|
let mut serverbound_variant = Variant::empty();
|
||||||
let mut clientbound_variant = Variant::empty();
|
let mut clientbound_variant = Variant::empty();
|
||||||
|
|
||||||
// Process serverbound packets
|
// Process serverbound packets
|
||||||
process_packets("../pumpkin-protocol/src/java/server/play", &mut interface, &mut serverbound_variant);
|
process_packets(
|
||||||
|
"../pumpkin-protocol/src/java/server/play",
|
||||||
|
&mut interface,
|
||||||
|
&mut serverbound_variant,
|
||||||
|
);
|
||||||
// Process clientbound packets
|
// Process clientbound packets
|
||||||
process_packets("../pumpkin-protocol/src/java/client/play", &mut interface, &mut clientbound_variant);
|
process_packets(
|
||||||
|
"../pumpkin-protocol/src/java/client/play",
|
||||||
|
&mut interface,
|
||||||
|
&mut clientbound_variant,
|
||||||
|
);
|
||||||
|
|
||||||
interface.type_def(TypeDef::new("serverbound-packet", TypeDefKind::Variant(serverbound_variant)));
|
interface.type_def(TypeDef::new(
|
||||||
interface.type_def(TypeDef::new("clientbound-packet", TypeDefKind::Variant(clientbound_variant)));
|
"serverbound-packet",
|
||||||
|
TypeDefKind::Variant(serverbound_variant),
|
||||||
|
));
|
||||||
|
interface.type_def(TypeDef::new(
|
||||||
|
"clientbound-packet",
|
||||||
|
TypeDefKind::Variant(clientbound_variant),
|
||||||
|
));
|
||||||
|
|
||||||
package.interface(interface);
|
package.interface(interface);
|
||||||
package.to_string()
|
package.to_string()
|
||||||
@ -26,11 +47,15 @@ pub fn build() -> String {
|
|||||||
|
|
||||||
fn process_packets(dir: &str, interface: &mut Interface, variant: &mut Variant) {
|
fn process_packets(dir: &str, interface: &mut Interface, variant: &mut Variant) {
|
||||||
let paths = fs::read_dir(dir).expect("Failed to read packet directory");
|
let paths = fs::read_dir(dir).expect("Failed to read packet directory");
|
||||||
let mut sorted_paths: Vec<_> = paths.map(|e| e.expect("Failed to read entry").path()).collect();
|
let mut sorted_paths: Vec<_> = paths
|
||||||
|
.map(|e| e.expect("Failed to read entry").path())
|
||||||
|
.collect();
|
||||||
sorted_paths.sort();
|
sorted_paths.sort();
|
||||||
|
|
||||||
for path in sorted_paths {
|
for path in sorted_paths {
|
||||||
if path.extension().is_some_and(|ext| ext == "rs") && path.file_name().is_some_and(|name| name != "mod.rs") {
|
if path.extension().is_some_and(|ext| ext == "rs")
|
||||||
|
&& path.file_name().is_some_and(|name| name != "mod.rs")
|
||||||
|
{
|
||||||
parse_packet_file(&path, interface, variant);
|
parse_packet_file(&path, interface, variant);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -43,7 +68,11 @@ fn parse_packet_file(path: &Path, interface: &mut Interface, variant: &mut Varia
|
|||||||
for item in file.items {
|
for item in file.items {
|
||||||
if let Item::Struct(s) = item {
|
if let Item::Struct(s) = item {
|
||||||
// Only process structs with #[java_packet] attribute
|
// Only process structs with #[java_packet] attribute
|
||||||
if !s.attrs.iter().any(|attr| attr.path().is_ident("java_packet")) {
|
if !s
|
||||||
|
.attrs
|
||||||
|
.iter()
|
||||||
|
.any(|attr| attr.path().is_ident("java_packet"))
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,9 +88,14 @@ fn parse_packet_file(path: &Path, interface: &mut Interface, variant: &mut Varia
|
|||||||
}
|
}
|
||||||
|
|
||||||
let wit_name = struct_name.to_kebab_case();
|
let wit_name = struct_name.to_kebab_case();
|
||||||
interface.type_def(TypeDef::new(wit_name.clone(), TypeDefKind::Record(Record::new(fields_list))));
|
interface.type_def(TypeDef::new(
|
||||||
variant.case(VariantCase::value(wit_name.clone(), WitType::named(wit_name)));
|
wit_name.clone(),
|
||||||
|
TypeDefKind::Record(Record::new(fields_list)),
|
||||||
|
));
|
||||||
|
variant.case(VariantCase::value(
|
||||||
|
wit_name.clone(),
|
||||||
|
WitType::named(wit_name),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
|
use heck::{ToPascalCase, ToSnakeCase};
|
||||||
use std::{fs, path::Path};
|
use std::{fs, path::Path};
|
||||||
use syn::{Fields, Item};
|
use syn::{Fields, Item};
|
||||||
use heck::{ToPascalCase, ToSnakeCase};
|
|
||||||
|
|
||||||
pub fn build_java_mapping() -> String {
|
pub fn build_java_mapping() -> String {
|
||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
@ -17,7 +17,14 @@ pub fn build_java_mapping() -> String {
|
|||||||
output.push_str("pub fn serialize_java_packet(packet: &ClientboundPacket, version: MinecraftVersion) -> Option<Bytes> {\n");
|
output.push_str("pub fn serialize_java_packet(packet: &ClientboundPacket, version: MinecraftVersion) -> Option<Bytes> {\n");
|
||||||
output.push_str(" match packet {\n");
|
output.push_str(" match packet {\n");
|
||||||
|
|
||||||
process_packets("../pumpkin-protocol/src/java/client/play", &mut output, "java_packet", "ClientboundPacket", "pumpkin_protocol::java::client::play", false);
|
process_packets(
|
||||||
|
"../pumpkin-protocol/src/java/client/play",
|
||||||
|
&mut output,
|
||||||
|
"java_packet",
|
||||||
|
"ClientboundPacket",
|
||||||
|
"pumpkin_protocol::java::client::play",
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
output.push_str(" _ => None,\n");
|
output.push_str(" _ => None,\n");
|
||||||
output.push_str(" }\n");
|
output.push_str(" }\n");
|
||||||
@ -29,10 +36,19 @@ pub fn build_bedrock_mapping() -> String {
|
|||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
output.push_str("\nuse crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::bedrock_packets::ClientboundPacket as BClientboundPacket;\n\n");
|
output.push_str("\nuse crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::bedrock_packets::ClientboundPacket as BClientboundPacket;\n\n");
|
||||||
output.push_str("#[must_use]\n");
|
output.push_str("#[must_use]\n");
|
||||||
output.push_str("pub fn serialize_bedrock_packet(packet: &BClientboundPacket) -> Option<Bytes> {\n");
|
output.push_str(
|
||||||
|
"pub fn serialize_bedrock_packet(packet: &BClientboundPacket) -> Option<Bytes> {\n",
|
||||||
|
);
|
||||||
output.push_str(" match packet {\n");
|
output.push_str(" match packet {\n");
|
||||||
|
|
||||||
process_packets("../pumpkin-protocol/src/bedrock/client", &mut output, "packet", "BClientboundPacket", "pumpkin_protocol::bedrock::client", true);
|
process_packets(
|
||||||
|
"../pumpkin-protocol/src/bedrock/client",
|
||||||
|
&mut output,
|
||||||
|
"packet",
|
||||||
|
"BClientboundPacket",
|
||||||
|
"pumpkin_protocol::bedrock::client",
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
output.push_str(" _ => None,\n");
|
output.push_str(" _ => None,\n");
|
||||||
output.push_str(" }\n");
|
output.push_str(" }\n");
|
||||||
@ -40,9 +56,18 @@ pub fn build_bedrock_mapping() -> String {
|
|||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_packets(dir: &str, output: &mut String, attr_name: &str, variant_prefix: &str, rust_path_prefix: &str, skip_raknet: bool) {
|
fn process_packets(
|
||||||
|
dir: &str,
|
||||||
|
output: &mut String,
|
||||||
|
attr_name: &str,
|
||||||
|
variant_prefix: &str,
|
||||||
|
rust_path_prefix: &str,
|
||||||
|
skip_raknet: bool,
|
||||||
|
) {
|
||||||
let paths = fs::read_dir(dir).expect("Failed to read packet directory");
|
let paths = fs::read_dir(dir).expect("Failed to read packet directory");
|
||||||
let mut sorted_paths: Vec<_> = paths.map(|e| e.expect("Failed to read entry").path()).collect();
|
let mut sorted_paths: Vec<_> = paths
|
||||||
|
.map(|e| e.expect("Failed to read entry").path())
|
||||||
|
.collect();
|
||||||
sorted_paths.sort();
|
sorted_paths.sort();
|
||||||
|
|
||||||
for path in sorted_paths {
|
for path in sorted_paths {
|
||||||
@ -50,16 +75,31 @@ fn process_packets(dir: &str, output: &mut String, attr_name: &str, variant_pref
|
|||||||
if skip_raknet && path.file_name().unwrap() == "raknet" {
|
if skip_raknet && path.file_name().unwrap() == "raknet" {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
process_packets(path.to_str().unwrap(), output, attr_name, variant_prefix, rust_path_prefix, skip_raknet);
|
process_packets(
|
||||||
|
path.to_str().unwrap(),
|
||||||
|
output,
|
||||||
|
attr_name,
|
||||||
|
variant_prefix,
|
||||||
|
rust_path_prefix,
|
||||||
|
skip_raknet,
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if path.extension().is_some_and(|ext| ext == "rs") && path.file_name().is_some_and(|name| name != "mod.rs") {
|
if path.extension().is_some_and(|ext| ext == "rs")
|
||||||
|
&& path.file_name().is_some_and(|name| name != "mod.rs")
|
||||||
|
{
|
||||||
parse_packet_file(&path, output, attr_name, variant_prefix, rust_path_prefix);
|
parse_packet_file(&path, output, attr_name, variant_prefix, rust_path_prefix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_packet_file(path: &Path, output: &mut String, attr_name: &str, variant_prefix: &str, rust_path_prefix: &str) {
|
fn parse_packet_file(
|
||||||
|
path: &Path,
|
||||||
|
output: &mut String,
|
||||||
|
attr_name: &str,
|
||||||
|
variant_prefix: &str,
|
||||||
|
rust_path_prefix: &str,
|
||||||
|
) {
|
||||||
let content = fs::read_to_string(path).expect("Failed to read file");
|
let content = fs::read_to_string(path).expect("Failed to read file");
|
||||||
let file = syn::parse_file(&content).expect("Failed to parse file");
|
let file = syn::parse_file(&content).expect("Failed to parse file");
|
||||||
|
|
||||||
@ -70,7 +110,9 @@ fn parse_packet_file(path: &Path, output: &mut String, attr_name: &str, variant_
|
|||||||
}
|
}
|
||||||
|
|
||||||
let struct_name = s.ident.to_string();
|
let struct_name = s.ident.to_string();
|
||||||
if struct_name == "CHandshake" { continue; } // Skip CHandshake (RakNet)
|
if struct_name == "CHandshake" {
|
||||||
|
continue;
|
||||||
|
} // Skip CHandshake (RakNet)
|
||||||
let wit_case = struct_name.to_pascal_case();
|
let wit_case = struct_name.to_pascal_case();
|
||||||
|
|
||||||
let mut prep_code = String::new();
|
let mut prep_code = String::new();
|
||||||
@ -137,9 +179,15 @@ fn parse_packet_file(path: &Path, output: &mut String, attr_name: &str, variant_
|
|||||||
}
|
}
|
||||||
|
|
||||||
if possible {
|
if possible {
|
||||||
output.push_str(&format!(" {}::{}(data) => {{\n", variant_prefix, wit_case));
|
output.push_str(&format!(
|
||||||
|
" {}::{}(data) => {{\n",
|
||||||
|
variant_prefix, wit_case
|
||||||
|
));
|
||||||
output.push_str(&prep_code);
|
output.push_str(&prep_code);
|
||||||
output.push_str(&format!(" let p = {}::{} {{\n", rust_path_prefix, struct_name));
|
output.push_str(&format!(
|
||||||
|
" let p = {}::{} {{\n",
|
||||||
|
rust_path_prefix, struct_name
|
||||||
|
));
|
||||||
output.push_str(&field_inits);
|
output.push_str(&field_inits);
|
||||||
output.push_str(" };\n");
|
output.push_str(" };\n");
|
||||||
output.push_str(" let mut buf = Vec::new();\n");
|
output.push_str(" let mut buf = Vec::new();\n");
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use std::fs;
|
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
|
use std::fs;
|
||||||
use wit_encoder::{Enum, Interface, Package, PackageName, TypeDef, TypeDefKind};
|
use wit_encoder::{Enum, Interface, Package, PackageName, TypeDef, TypeDefKind};
|
||||||
|
|
||||||
pub fn build() -> String {
|
pub fn build() -> String {
|
||||||
@ -7,7 +7,11 @@ pub fn build() -> String {
|
|||||||
serde_json::from_str(&fs::read_to_string("../assets/particles.json").unwrap())
|
serde_json::from_str(&fs::read_to_string("../assets/particles.json").unwrap())
|
||||||
.expect("Failed to parse particles.json");
|
.expect("Failed to parse particles.json");
|
||||||
|
|
||||||
let mut package = Package::new(PackageName::new("pumpkin", "plugin", Some(Version::new(0, 1, 0))));
|
let mut package = Package::new(PackageName::new(
|
||||||
|
"pumpkin",
|
||||||
|
"plugin",
|
||||||
|
Some(Version::new(0, 1, 0)),
|
||||||
|
));
|
||||||
let mut interface = Interface::new("particles");
|
let mut interface = Interface::new("particles");
|
||||||
|
|
||||||
let mut particle_enum = Enum::empty();
|
let mut particle_enum = Enum::empty();
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use std::fs;
|
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
|
use std::fs;
|
||||||
use wit_encoder::{Enum, Interface, Package, PackageName, TypeDef, TypeDefKind};
|
use wit_encoder::{Enum, Interface, Package, PackageName, TypeDef, TypeDefKind};
|
||||||
|
|
||||||
pub fn build() -> String {
|
pub fn build() -> String {
|
||||||
@ -7,7 +7,11 @@ pub fn build() -> String {
|
|||||||
serde_json::from_str(&fs::read_to_string("../assets/sounds.json").unwrap())
|
serde_json::from_str(&fs::read_to_string("../assets/sounds.json").unwrap())
|
||||||
.expect("Failed to parse sounds.json");
|
.expect("Failed to parse sounds.json");
|
||||||
|
|
||||||
let mut package = Package::new(PackageName::new("pumpkin", "plugin", Some(Version::new(0, 1, 0))));
|
let mut package = Package::new(PackageName::new(
|
||||||
|
"pumpkin",
|
||||||
|
"plugin",
|
||||||
|
Some(Version::new(0, 1, 0)),
|
||||||
|
));
|
||||||
let mut interface = Interface::new("sounds");
|
let mut interface = Interface::new("sounds");
|
||||||
|
|
||||||
let mut sound_enum = Enum::empty();
|
let mut sound_enum = Enum::empty();
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use wit_encoder::Type as WitType;
|
|
||||||
use syn::{Type, TypePath};
|
use syn::{Type, TypePath};
|
||||||
|
use wit_encoder::Type as WitType;
|
||||||
|
|
||||||
pub fn map_type(ty: &Type) -> WitType {
|
pub fn map_type(ty: &Type) -> WitType {
|
||||||
match ty {
|
match ty {
|
||||||
@ -17,14 +17,16 @@ pub fn map_type(ty: &Type) -> WitType {
|
|||||||
"u16" | "i16" => WitType::S32,
|
"u16" | "i16" => WitType::S32,
|
||||||
"Option" => {
|
"Option" => {
|
||||||
if let syn::PathArguments::AngleBracketed(args) = &last_segment.arguments
|
if let syn::PathArguments::AngleBracketed(args) = &last_segment.arguments
|
||||||
&& let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() {
|
&& let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first()
|
||||||
|
{
|
||||||
return WitType::option(map_type(inner_ty));
|
return WitType::option(map_type(inner_ty));
|
||||||
}
|
}
|
||||||
WitType::String
|
WitType::String
|
||||||
}
|
}
|
||||||
"Vec" | "Box" => {
|
"Vec" | "Box" => {
|
||||||
if let syn::PathArguments::AngleBracketed(args) = &last_segment.arguments
|
if let syn::PathArguments::AngleBracketed(args) = &last_segment.arguments
|
||||||
&& let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() {
|
&& let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first()
|
||||||
|
{
|
||||||
return WitType::list(map_type(inner_ty));
|
return WitType::list(map_type(inner_ty));
|
||||||
}
|
}
|
||||||
WitType::String
|
WitType::String
|
||||||
|
|||||||
@ -1,5 +1,10 @@
|
|||||||
/* This file is generated. Do not edit manually. */
|
/* This file is generated. Do not edit manually. */
|
||||||
#[derive(Debug, Clone, Copy)]
|
use pumpkin_util::math::int_provider::{
|
||||||
|
BiasedToBottomIntProvider, ClampedIntProvider, ClampedNormalIntProvider, ConstantIntProvider,
|
||||||
|
IntProvider, NormalIntProvider, TrapezoidIntProvider, UniformIntProvider, WeightedEntry,
|
||||||
|
WeightedListIntProvider,
|
||||||
|
};
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Dimension {
|
pub struct Dimension {
|
||||||
pub id: u8,
|
pub id: u8,
|
||||||
pub minecraft_name: &'static str,
|
pub minecraft_name: &'static str,
|
||||||
@ -12,6 +17,8 @@ pub struct Dimension {
|
|||||||
pub logical_height: i32,
|
pub logical_height: i32,
|
||||||
pub infiniburn: &'static str,
|
pub infiniburn: &'static str,
|
||||||
pub ambient_light: f32,
|
pub ambient_light: f32,
|
||||||
|
pub monster_spawn_light_level: IntProvider,
|
||||||
|
pub monster_spawn_block_light_limit: u8,
|
||||||
pub sky_color: Option<i32>,
|
pub sky_color: Option<i32>,
|
||||||
pub fog_color: Option<i32>,
|
pub fog_color: Option<i32>,
|
||||||
pub cloud_color: Option<i32>,
|
pub cloud_color: Option<i32>,
|
||||||
@ -30,6 +37,13 @@ impl Dimension {
|
|||||||
logical_height: 384i32,
|
logical_height: 384i32,
|
||||||
infiniburn: "#minecraft:infiniburn_overworld",
|
infiniburn: "#minecraft:infiniburn_overworld",
|
||||||
ambient_light: 0f32,
|
ambient_light: 0f32,
|
||||||
|
monster_spawn_light_level: IntProvider::Object(NormalIntProvider::Uniform(
|
||||||
|
UniformIntProvider {
|
||||||
|
min_inclusive: 0i32,
|
||||||
|
max_inclusive: 7i32,
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
monster_spawn_block_light_limit: 0u8,
|
||||||
sky_color: Some(7907327i32),
|
sky_color: Some(7907327i32),
|
||||||
fog_color: Some(12638463i32),
|
fog_color: Some(12638463i32),
|
||||||
cloud_color: None,
|
cloud_color: None,
|
||||||
@ -47,6 +61,13 @@ impl Dimension {
|
|||||||
logical_height: 384i32,
|
logical_height: 384i32,
|
||||||
infiniburn: "#minecraft:infiniburn_overworld",
|
infiniburn: "#minecraft:infiniburn_overworld",
|
||||||
ambient_light: 0f32,
|
ambient_light: 0f32,
|
||||||
|
monster_spawn_light_level: IntProvider::Object(NormalIntProvider::Uniform(
|
||||||
|
UniformIntProvider {
|
||||||
|
min_inclusive: 0i32,
|
||||||
|
max_inclusive: 7i32,
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
monster_spawn_block_light_limit: 0u8,
|
||||||
sky_color: Some(7907327i32),
|
sky_color: Some(7907327i32),
|
||||||
fog_color: Some(12638463i32),
|
fog_color: Some(12638463i32),
|
||||||
cloud_color: None,
|
cloud_color: None,
|
||||||
@ -64,6 +85,8 @@ impl Dimension {
|
|||||||
logical_height: 256i32,
|
logical_height: 256i32,
|
||||||
infiniburn: "#minecraft:infiniburn_end",
|
infiniburn: "#minecraft:infiniburn_end",
|
||||||
ambient_light: 0.25f32,
|
ambient_light: 0.25f32,
|
||||||
|
monster_spawn_light_level: IntProvider::Constant(15i32),
|
||||||
|
monster_spawn_block_light_limit: 0u8,
|
||||||
sky_color: Some(0i32),
|
sky_color: Some(0i32),
|
||||||
fog_color: Some(1577752i32),
|
fog_color: Some(1577752i32),
|
||||||
cloud_color: None,
|
cloud_color: None,
|
||||||
@ -81,6 +104,8 @@ impl Dimension {
|
|||||||
logical_height: 128i32,
|
logical_height: 128i32,
|
||||||
infiniburn: "#minecraft:infiniburn_nether",
|
infiniburn: "#minecraft:infiniburn_nether",
|
||||||
ambient_light: 0.1f32,
|
ambient_light: 0.1f32,
|
||||||
|
monster_spawn_light_level: IntProvider::Constant(7i32),
|
||||||
|
monster_spawn_block_light_limit: 15u8,
|
||||||
sky_color: None,
|
sky_color: None,
|
||||||
fog_color: None,
|
fog_color: None,
|
||||||
cloud_color: None,
|
cloud_color: None,
|
||||||
|
|||||||
@ -12,7 +12,7 @@ fn bench_full_chunk_generation(c: &mut Criterion) {
|
|||||||
let dimension = Dimension::OVERWORLD;
|
let dimension = Dimension::OVERWORLD;
|
||||||
let seed = Seed(42);
|
let seed = Seed(42);
|
||||||
let block_registry = Arc::new(BlockRegistry);
|
let block_registry = Arc::new(BlockRegistry);
|
||||||
let world_gen = get_world_gen(seed, dimension);
|
let world_gen = get_world_gen(seed, dimension.clone());
|
||||||
let biome_mixer_seed = hash_seed(world_gen.random_config.seed);
|
let biome_mixer_seed = hash_seed(world_gen.random_config.seed);
|
||||||
|
|
||||||
c.bench_function("full_chunk_generation", |b| {
|
c.bench_function("full_chunk_generation", |b| {
|
||||||
|
|||||||
@ -110,11 +110,11 @@ struct ChunkBitmap([u8; 128]);
|
|||||||
impl ChunkBitmap {
|
impl ChunkBitmap {
|
||||||
const SIZE: usize = 128;
|
const SIZE: usize = 128;
|
||||||
|
|
||||||
fn new() -> Self {
|
const fn new() -> Self {
|
||||||
Self([0u8; 128])
|
Self([0u8; 128])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(&mut self, index: usize, exists: bool) {
|
const fn set(&mut self, index: usize, exists: bool) {
|
||||||
let byte = index / 8;
|
let byte = index / 8;
|
||||||
let bit = index % 8;
|
let bit = index % 8;
|
||||||
if exists {
|
if exists {
|
||||||
@ -125,7 +125,7 @@ impl ChunkBitmap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn get(&self, index: usize) -> bool {
|
const fn get(&self, index: usize) -> bool {
|
||||||
let byte = index / 8;
|
let byte = index / 8;
|
||||||
let bit = index % 8;
|
let bit = index % 8;
|
||||||
(self.0[byte] >> bit) & 1 == 1
|
(self.0[byte] >> bit) & 1 == 1
|
||||||
@ -315,8 +315,8 @@ impl<S: SingleChunkDataSerializer> LinearV2File<S> {
|
|||||||
AnvilChunkFile::<S>::get_chunk_index(x, z)
|
AnvilChunkFile::<S>::get_chunk_index(x, z)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Number of buckets in the grid (grid_size²).
|
/// Number of buckets in the grid (`grid_size²`).
|
||||||
fn bucket_count(grid_size: u8) -> usize {
|
const fn bucket_count(grid_size: u8) -> usize {
|
||||||
(grid_size as usize) * (grid_size as usize)
|
(grid_size as usize) * (grid_size as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,7 +344,7 @@ impl<S: SingleChunkDataSerializer> LinearV2File<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Inverse of `chunk_bucket_index` + `chunk_local_index`.
|
/// Inverse of `chunk_bucket_index` + `chunk_local_index`.
|
||||||
fn global_chunk_index(bucket_idx: usize, local: usize, grid_size: u8) -> usize {
|
const fn global_chunk_index(bucket_idx: usize, local: usize, grid_size: u8) -> usize {
|
||||||
let stride = 32 / grid_size as usize;
|
let stride = 32 / grid_size as usize;
|
||||||
let bucket_row = bucket_idx / grid_size as usize;
|
let bucket_row = bucket_idx / grid_size as usize;
|
||||||
let bucket_col = bucket_idx % grid_size as usize;
|
let bucket_col = bucket_idx % grid_size as usize;
|
||||||
@ -510,8 +510,8 @@ impl<S: SingleChunkDataSerializer> ChunkSerializer for LinearV2File<S> {
|
|||||||
.map_err(|_| ChunkReadingError::RegionIsInvalid)?;
|
.map_err(|_| ChunkReadingError::RegionIsInvalid)?;
|
||||||
decoder
|
decoder
|
||||||
.read_to_end(&mut decompressed)
|
.read_to_end(&mut decompressed)
|
||||||
.map_err(|e| ChunkReadingError::IoError(e.kind()))?;
|
.map_err(|e| ChunkReadingError::IoError(e.kind()))?
|
||||||
}
|
};
|
||||||
buf.advance(compressed_size);
|
buf.advance(compressed_size);
|
||||||
|
|
||||||
let mut bucket_buf: Bytes = decompressed.into();
|
let mut bucket_buf: Bytes = decompressed.into();
|
||||||
@ -548,8 +548,7 @@ impl<S: SingleChunkDataSerializer> ChunkSerializer for LinearV2File<S> {
|
|||||||
|
|
||||||
self.timestamps[index] = SystemTime::now()
|
self.timestamps[index] = SystemTime::now()
|
||||||
.duration_since(UNIX_EPOCH)
|
.duration_since(UNIX_EPOCH)
|
||||||
.map(|d| d.as_secs())
|
.map_or(0, |d| d.as_secs());
|
||||||
.unwrap_or(0);
|
|
||||||
self.chunks_data[index] = Some(chunk_raw);
|
self.chunks_data[index] = Some(chunk_raw);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -377,9 +377,9 @@ where
|
|||||||
serializer
|
serializer
|
||||||
.write(&path)
|
.write(&path)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ChunkWritingError::IoError(e.kind()))?;
|
.map_err(|e| ChunkWritingError::IoError(e.kind()))?
|
||||||
// Read-lock released here.
|
// Read-lock released here.
|
||||||
}
|
};
|
||||||
|
|
||||||
// Drop our handle so `can_remove` may succeed.
|
// Drop our handle so `can_remove` may succeed.
|
||||||
drop(chunk_serializer);
|
drop(chunk_serializer);
|
||||||
|
|||||||
@ -174,6 +174,7 @@ impl TryFrom<usize> for ChunkHeightmapType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ChunkHeightmapType {
|
impl ChunkHeightmapType {
|
||||||
|
#[must_use]
|
||||||
pub fn is_opaque(&self, block_state: &BlockState) -> bool {
|
pub fn is_opaque(&self, block_state: &BlockState) -> bool {
|
||||||
let block = Block::get_raw_id_from_state_id(block_state.id);
|
let block = Block::get_raw_id_from_state_id(block_state.id);
|
||||||
match self {
|
match self {
|
||||||
|
|||||||
@ -694,6 +694,7 @@ impl BlockPalette {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn to_disk_nbt(&self) -> ChunkSectionBlockStates {
|
pub fn to_disk_nbt(&self) -> ChunkSectionBlockStates {
|
||||||
let bits_per_entry = self.bits_per_entry().max(BLOCK_DISK_MIN_BITS);
|
let bits_per_entry = self.bits_per_entry().max(BLOCK_DISK_MIN_BITS);
|
||||||
let (palette, packed_data) = self.to_palette_and_packed_data(bits_per_entry);
|
let (palette, packed_data) = self.to_palette_and_packed_data(bits_per_entry);
|
||||||
|
|||||||
@ -124,7 +124,10 @@ impl LevelChannel {
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
pub fn notify(&self) {
|
pub fn notify(&self) {
|
||||||
let val = self.value.lock().unwrap_or_else(|e| e.into_inner());
|
let val = self
|
||||||
|
.value
|
||||||
|
.lock()
|
||||||
|
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||||
drop(val);
|
drop(val);
|
||||||
self.notify.notify_one();
|
self.notify.notify_one();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -330,7 +330,7 @@ impl Chunk {
|
|||||||
fluid_ticks: Default::default(),
|
fluid_ticks: Default::default(),
|
||||||
pending_block_entities: Mutex::new(pending_block_entities),
|
pending_block_entities: Mutex::new(pending_block_entities),
|
||||||
status: proto_chunk.stage.into(),
|
status: proto_chunk.stage.into(),
|
||||||
blending_data: proto_chunk.blending_data.clone(),
|
blending_data: proto_chunk.blending_data,
|
||||||
};
|
};
|
||||||
|
|
||||||
chunk.heightmap = Mutex::new(chunk.calculate_heightmap());
|
chunk.heightmap = Mutex::new(chunk.calculate_heightmap());
|
||||||
|
|||||||
@ -97,7 +97,7 @@ mod tests {
|
|||||||
let dimension = Dimension::OVERWORLD;
|
let dimension = Dimension::OVERWORLD;
|
||||||
let seed = Seed(42);
|
let seed = Seed(42);
|
||||||
let block_registry = Arc::new(BlockRegistry);
|
let block_registry = Arc::new(BlockRegistry);
|
||||||
let world_gen = get_world_gen(seed, dimension);
|
let world_gen = get_world_gen(seed, dimension.clone());
|
||||||
let biome_mixer_seed = hash_seed(world_gen.random_config.seed);
|
let biome_mixer_seed = hash_seed(world_gen.random_config.seed);
|
||||||
|
|
||||||
let _ = generate_single_chunk(
|
let _ = generate_single_chunk(
|
||||||
|
|||||||
@ -137,10 +137,7 @@ impl GenerationSchedule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let max_in_flight = if gen_pool.is_some() {
|
let max_in_flight = if gen_pool.is_some() {
|
||||||
(thread::available_parallelism()
|
(thread::available_parallelism().map_or(1, std::num::NonZero::get) * 4) as u16
|
||||||
.map(|n| n.get())
|
|
||||||
.unwrap_or(1)
|
|
||||||
* 4) as u16
|
|
||||||
} else {
|
} else {
|
||||||
gen_thread_count as u16
|
gen_thread_count as u16
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,6 +5,7 @@ use pumpkin_data::dimension::Dimension;
|
|||||||
|
|
||||||
use crate::level::Level;
|
use crate::level::Level;
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn into_level(
|
pub fn into_level(
|
||||||
dimension: Dimension,
|
dimension: Dimension,
|
||||||
level_config: &LevelConfig,
|
level_config: &LevelConfig,
|
||||||
|
|||||||
@ -14,6 +14,7 @@ pub struct BlendingData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BlendingData {
|
impl BlendingData {
|
||||||
|
#[must_use]
|
||||||
pub fn get_height(&self, cell_x: i32, _cell_y: i32, cell_z: i32) -> f64 {
|
pub fn get_height(&self, cell_x: i32, _cell_y: i32, cell_z: i32) -> f64 {
|
||||||
if !(0..16).contains(&cell_x) || !(0..16).contains(&cell_z) {
|
if !(0..16).contains(&cell_x) || !(0..16).contains(&cell_z) {
|
||||||
return f64::MAX;
|
return f64::MAX;
|
||||||
@ -21,6 +22,7 @@ impl BlendingData {
|
|||||||
self.heights[(cell_z * 16 + cell_x) as usize]
|
self.heights[(cell_z * 16 + cell_x) as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn get_density(&self, cell_x: i32, _cell_y: i32, cell_z: i32) -> f64 {
|
pub fn get_density(&self, cell_x: i32, _cell_y: i32, cell_z: i32) -> f64 {
|
||||||
// cell_y is block_y / 8 in Blender.java
|
// cell_y is block_y / 8 in Blender.java
|
||||||
// We'll need a better storage for this if we want full fidelity
|
// We'll need a better storage for this if we want full fidelity
|
||||||
|
|||||||
@ -30,6 +30,7 @@ const DENSITY_BLENDING_RANGE_CELLS: i32 = 2;
|
|||||||
const DENSITY_BLENDING_RANGE_CHUNKS: i32 = 5 >> 2; // QuartPos.toSection(5)
|
const DENSITY_BLENDING_RANGE_CHUNKS: i32 = 5 >> 2; // QuartPos.toSection(5)
|
||||||
|
|
||||||
impl Blender {
|
impl Blender {
|
||||||
|
#[must_use]
|
||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
Self {
|
Self {
|
||||||
height_and_biome_blending_data: FxHashMap::default(),
|
height_and_biome_blending_data: FxHashMap::default(),
|
||||||
@ -79,16 +80,22 @@ impl Blender {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.height_and_biome_blending_data.is_empty() && self.density_blending_data.is_empty()
|
self.height_and_biome_blending_data.is_empty() && self.density_blending_data.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn blend_offset_and_factor(&self, block_x: i32, block_z: i32) -> BlendingOutput {
|
pub fn blend_offset_and_factor(&self, block_x: i32, block_z: i32) -> BlendingOutput {
|
||||||
let cell_x = biome_coords::from_block(block_x);
|
let cell_x = biome_coords::from_block(block_x);
|
||||||
let cell_z = biome_coords::from_block(block_z);
|
let cell_z = biome_coords::from_block(block_z);
|
||||||
|
|
||||||
let fixed_height = self
|
let fixed_height = self.get_blending_data_value(
|
||||||
.get_blending_data_value(cell_x, 0, cell_z, |data, x, y, z| data.get_height(x, y, z));
|
cell_x,
|
||||||
|
0,
|
||||||
|
cell_z,
|
||||||
|
blending_data::BlendingData::get_height,
|
||||||
|
);
|
||||||
|
|
||||||
if fixed_height != f64::MAX {
|
if fixed_height != f64::MAX {
|
||||||
return BlendingOutput {
|
return BlendingOutput {
|
||||||
@ -111,7 +118,7 @@ impl Blender {
|
|||||||
|test_cell_x, test_cell_z, height| {
|
|test_cell_x, test_cell_z, height| {
|
||||||
let dx = (cell_x - test_cell_x) as f64;
|
let dx = (cell_x - test_cell_x) as f64;
|
||||||
let dz = (cell_z - test_cell_z) as f64;
|
let dz = (cell_z - test_cell_z) as f64;
|
||||||
let distance = (dx * dx + dz * dz).sqrt();
|
let distance = dx.hypot(dz);
|
||||||
|
|
||||||
if distance <= HEIGHT_BLENDING_RANGE_CELLS as f64 {
|
if distance <= HEIGHT_BLENDING_RANGE_CELLS as f64 {
|
||||||
if distance < closest_distance {
|
if distance < closest_distance {
|
||||||
@ -151,6 +158,7 @@ impl Blender {
|
|||||||
/ (128.0 * (32.0 - 3.0 * target_y_mod))
|
/ (128.0 * (32.0 - 3.0 * target_y_mod))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn blend_density(&self, block_x: i32, block_y: i32, block_z: i32, noise_value: f64) -> f64 {
|
pub fn blend_density(&self, block_x: i32, block_y: i32, block_z: i32, noise_value: f64) -> f64 {
|
||||||
let cell_x = biome_coords::from_block(block_x);
|
let cell_x = biome_coords::from_block(block_x);
|
||||||
let cell_y = block_y / 8;
|
let cell_y = block_y / 8;
|
||||||
@ -263,6 +271,7 @@ impl Blender {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn blend_biome(
|
pub fn blend_biome(
|
||||||
&self,
|
&self,
|
||||||
quart_x: i32,
|
quart_x: i32,
|
||||||
@ -284,7 +293,7 @@ impl Blender {
|
|||||||
|test_cell_x, test_cell_z, biome| {
|
|test_cell_x, test_cell_z, biome| {
|
||||||
let dx = (quart_x - test_cell_x) as f64;
|
let dx = (quart_x - test_cell_x) as f64;
|
||||||
let dz = (quart_z - test_cell_z) as f64;
|
let dz = (quart_z - test_cell_z) as f64;
|
||||||
let distance = (dx * dx + dz * dz).sqrt();
|
let distance = dx.hypot(dz);
|
||||||
|
|
||||||
if distance <= HEIGHT_BLENDING_RANGE_CELLS as f64 && distance < closest_distance
|
if distance <= HEIGHT_BLENDING_RANGE_CELLS as f64 && distance < closest_distance
|
||||||
{
|
{
|
||||||
|
|||||||
@ -428,7 +428,7 @@ pub fn get_height(p: &HeightProvider, random: &mut RandomGenerator, min_y: i8, h
|
|||||||
HeightProvider::VeryBiasedToBottom(p) => {
|
HeightProvider::VeryBiasedToBottom(p) => {
|
||||||
let min = p.min_inclusive.get_y(min_y as i16, height);
|
let min = p.min_inclusive.get_y(min_y as i16, height);
|
||||||
let max = p.max_inclusive.get_y(min_y as i16, height);
|
let max = p.max_inclusive.get_y(min_y as i16, height);
|
||||||
let inner = p.inner.map_or(1, |n| n.get()) as i32;
|
let inner = p.inner.map_or(1, std::num::NonZero::get) as i32;
|
||||||
let min_rnd = random.next_inbetween_i32(min + inner, max);
|
let min_rnd = random.next_inbetween_i32(min + inner, max);
|
||||||
let max_rnd = random.next_inbetween_i32(min, min_rnd - 1);
|
let max_rnd = random.next_inbetween_i32(min, min_rnd - 1);
|
||||||
random.next_inbetween_i32(min, max_rnd - 1 + inner)
|
random.next_inbetween_i32(min, max_rnd - 1 + inner)
|
||||||
|
|||||||
@ -16,7 +16,7 @@ impl CarvingMask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_bit_index(&self, x: i32, y: i32, z: i32) -> usize {
|
const fn get_bit_index(&self, x: i32, y: i32, z: i32) -> usize {
|
||||||
// x: 0..16, z: 0..16, y: min_y..min_y+height
|
// x: 0..16, z: 0..16, y: min_y..min_y+height
|
||||||
let rel_x = x & 15;
|
let rel_x = x & 15;
|
||||||
let rel_z = z & 15;
|
let rel_z = z & 15;
|
||||||
|
|||||||
@ -413,7 +413,7 @@ impl IcebergFeature {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_ellipse_c(&self, y_off: i32, height: i32, shape_ellipse_c: i32) -> i32 {
|
const fn get_ellipse_c(&self, y_off: i32, height: i32, shape_ellipse_c: i32) -> i32 {
|
||||||
let mut c = shape_ellipse_c;
|
let mut c = shape_ellipse_c;
|
||||||
if y_off > 0 && height - y_off <= 3 {
|
if y_off > 0 && height - y_off <= 3 {
|
||||||
c = shape_ellipse_c - (4 - (height - y_off));
|
c = shape_ellipse_c - (4 - (height - y_off));
|
||||||
|
|||||||
@ -130,7 +130,7 @@ struct SculkSpreader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SculkSpreader {
|
impl SculkSpreader {
|
||||||
fn new() -> Self {
|
const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
cursors: Vec::new(),
|
cursors: Vec::new(),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -143,8 +143,8 @@ impl CherryTrunkPlacer {
|
|||||||
tree_height as i32 - 1 + self.branch_end_offset_from_top.get(random);
|
tree_height as i32 - 1 + self.branch_end_offset_from_top.get(random);
|
||||||
let extend_branch_away_from_trunk =
|
let extend_branch_away_from_trunk =
|
||||||
middle_continues_upwards || branch_end_pos_offset < offset_from_origin;
|
middle_continues_upwards || branch_end_pos_offset < offset_from_origin;
|
||||||
let distance_to_trunk = self.branch_horizontal_length.get(random)
|
let distance_to_trunk =
|
||||||
+ if extend_branch_away_from_trunk { 1 } else { 0 };
|
self.branch_horizontal_length.get(random) + i32::from(extend_branch_away_from_trunk);
|
||||||
|
|
||||||
let branch_end_pos = origin
|
let branch_end_pos = origin
|
||||||
.add(
|
.add(
|
||||||
|
|||||||
@ -85,9 +85,8 @@ impl TwistingVinesFeature {
|
|||||||
// TODO: Set age property
|
// TODO: Set age property
|
||||||
chunk.set_block_state(¤t_pos.0, Block::TWISTING_VINES.default_state);
|
chunk.set_block_state(¤t_pos.0, Block::TWISTING_VINES.default_state);
|
||||||
break;
|
break;
|
||||||
} else {
|
|
||||||
chunk.set_block_state(¤t_pos.0, Block::TWISTING_VINES_PLANT.default_state);
|
|
||||||
}
|
}
|
||||||
|
chunk.set_block_state(¤t_pos.0, Block::TWISTING_VINES_PLANT.default_state);
|
||||||
current_pos = current_pos.up();
|
current_pos = current_pos.up();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,6 +33,7 @@ impl DoublePerlinNoiseSampler {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn get_amplitude(amplitudes: &[f64]) -> f64 {
|
pub fn get_amplitude(amplitudes: &[f64]) -> f64 {
|
||||||
let mut j = i32::MAX;
|
let mut j = i32::MAX;
|
||||||
let mut k = i32::MIN;
|
let mut k = i32::MIN;
|
||||||
|
|||||||
@ -657,11 +657,11 @@ impl ProtoChunk {
|
|||||||
generator: &super::generator::VanillaGenerator,
|
generator: &super::generator::VanillaGenerator,
|
||||||
multi_noise_sampler: &mut MultiNoiseSampler,
|
multi_noise_sampler: &mut MultiNoiseSampler,
|
||||||
) {
|
) {
|
||||||
let dimension = generator.dimension;
|
let dimension = &generator.dimension;
|
||||||
// Instantiate ONLY the supplier we actually need
|
// Instantiate ONLY the supplier we actually need
|
||||||
let active_supplier = if dimension == Dimension::THE_END {
|
let active_supplier = if dimension == &Dimension::THE_END {
|
||||||
ActiveSupplier::End(TheEndBiomeSupplier)
|
ActiveSupplier::End(TheEndBiomeSupplier)
|
||||||
} else if dimension == Dimension::THE_NETHER {
|
} else if dimension == &Dimension::THE_NETHER {
|
||||||
ActiveSupplier::Nether(MultiNoiseBiomeSupplier::NETHER)
|
ActiveSupplier::Nether(MultiNoiseBiomeSupplier::NETHER)
|
||||||
} else {
|
} else {
|
||||||
ActiveSupplier::Overworld(MultiNoiseBiomeSupplier::OVERWORLD)
|
ActiveSupplier::Overworld(MultiNoiseBiomeSupplier::OVERWORLD)
|
||||||
@ -1114,6 +1114,7 @@ impl ProtoChunk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn get_allowed_biomes(set: &StructureSet) -> Vec<u16> {
|
pub fn get_allowed_biomes(set: &StructureSet) -> Vec<u16> {
|
||||||
let mut allowed_biomes = Vec::new();
|
let mut allowed_biomes = Vec::new();
|
||||||
for entry in set.structures {
|
for entry in set.structures {
|
||||||
|
|||||||
@ -392,8 +392,8 @@ impl StructurePieceBase for JungleTemplePiece {
|
|||||||
};
|
};
|
||||||
self.piece.add_block(chunk, r_state, 9, -3, 5, bb);
|
self.piece.add_block(chunk, r_state, 9, -3, 5, bb);
|
||||||
self.piece
|
self.piece
|
||||||
.add_block(chunk, Block::MOSSY_COBBLESTONE.default_state, 9, -3, 4, bb);
|
.add_block(chunk, Block::MOSSY_COBBLESTONE.default_state, 9, -3, 4, bb)
|
||||||
}
|
};
|
||||||
self.piece.add_block(chunk, redstone_wire_ns, 9, -2, 4, bb);
|
self.piece.add_block(chunk, redstone_wire_ns, 9, -2, 4, bb);
|
||||||
/*if !self.placed_trap_2 {
|
/*if !self.placed_trap_2 {
|
||||||
self.placed_trap_2 = this.createDispenser(
|
self.placed_trap_2 = this.createDispenser(
|
||||||
@ -527,7 +527,7 @@ impl StructurePieceBase for JungleTemplePiece {
|
|||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn set_side_dir(props: &mut RedstoneWireLikeProperties, facing: HorizontalFacing) {
|
const fn set_side_dir(props: &mut RedstoneWireLikeProperties, facing: HorizontalFacing) {
|
||||||
match facing {
|
match facing {
|
||||||
HorizontalFacing::North => props.north = NorthRedstone::Side,
|
HorizontalFacing::North => props.north = NorthRedstone::Side,
|
||||||
HorizontalFacing::South => props.south = SouthRedstone::Side,
|
HorizontalFacing::South => props.south = SouthRedstone::Side,
|
||||||
|
|||||||
@ -9,6 +9,7 @@ pub struct SimpleInventory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SimpleInventory {
|
impl SimpleInventory {
|
||||||
|
#[must_use]
|
||||||
pub fn new(size: usize) -> Self {
|
pub fn new(size: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
stacks: (0..size)
|
stacks: (0..size)
|
||||||
|
|||||||
@ -138,6 +138,7 @@ pub async fn dump() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Level {
|
impl Level {
|
||||||
|
#[must_use]
|
||||||
pub fn from_root_folder(
|
pub fn from_root_folder(
|
||||||
level_config: &LevelConfig,
|
level_config: &LevelConfig,
|
||||||
root_folder: PathBuf,
|
root_folder: PathBuf,
|
||||||
@ -204,7 +205,7 @@ impl Level {
|
|||||||
should_save: AtomicBool::new(false),
|
should_save: AtomicBool::new(false),
|
||||||
should_unload: AtomicBool::new(false),
|
should_unload: AtomicBool::new(false),
|
||||||
autosave_ticks: level_config.autosave_ticks,
|
autosave_ticks: level_config.autosave_ticks,
|
||||||
pending_entity_generations: pending_entity_generations.clone(),
|
pending_entity_generations,
|
||||||
level_channel: level_channel.clone(),
|
level_channel: level_channel.clone(),
|
||||||
thread_tracker,
|
thread_tracker,
|
||||||
chunk_listener: listener.clone(),
|
chunk_listener: listener.clone(),
|
||||||
@ -213,8 +214,7 @@ impl Level {
|
|||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
let total_cores = thread::available_parallelism()
|
let total_cores = thread::available_parallelism()
|
||||||
.map(|n| n.get())
|
.map_or(1, std::num::NonZero::get)
|
||||||
.unwrap_or(1)
|
|
||||||
.saturating_sub(2)
|
.saturating_sub(2)
|
||||||
.max(1);
|
.max(1);
|
||||||
let threads_per_dimension = (total_cores / 2).max(1);
|
let threads_per_dimension = (total_cores / 2).max(1);
|
||||||
@ -253,9 +253,9 @@ impl Level {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Fallback to spawning a new thread if no pool is available (should not happen in production)
|
// Fallback to spawning a new thread if no pool is available (should not happen in production)
|
||||||
let level_clone = level.clone();
|
let level_clone = level;
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name(format!("Entity Gen {:?}", pos))
|
.name(format!("Entity Gen {pos:?}"))
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
let arc_chunk = Arc::new(ChunkEntityData {
|
let arc_chunk = Arc::new(ChunkEntityData {
|
||||||
x: pos.x,
|
x: pos.x,
|
||||||
|
|||||||
@ -369,8 +369,10 @@ impl SkyLightPropagator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Chunk::Level(c) => {
|
Chunk::Level(c) => {
|
||||||
let mut light_engine =
|
let mut light_engine = c
|
||||||
c.light_engine.lock().unwrap_or_else(|e| e.into_inner());
|
.light_engine
|
||||||
|
.lock()
|
||||||
|
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||||
if section_idx < light_engine.sky_light.len() {
|
if section_idx < light_engine.sky_light.len() {
|
||||||
light_engine.sky_light[section_idx]
|
light_engine.sky_light[section_idx]
|
||||||
.set(local_x, local_y, local_z, 15);
|
.set(local_x, local_y, local_z, 15);
|
||||||
@ -407,8 +409,10 @@ impl SkyLightPropagator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Chunk::Level(c) => {
|
Chunk::Level(c) => {
|
||||||
let mut light_engine =
|
let mut light_engine = c
|
||||||
c.light_engine.lock().unwrap_or_else(|e| e.into_inner());
|
.light_engine
|
||||||
|
.lock()
|
||||||
|
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||||
if section_idx < light_engine.sky_light.len() {
|
if section_idx < light_engine.sky_light.len() {
|
||||||
light_engine.sky_light[section_idx]
|
light_engine.sky_light[section_idx]
|
||||||
.set(local_x, local_y, local_z, light_val);
|
.set(local_x, local_y, local_z, light_val);
|
||||||
|
|||||||
@ -482,7 +482,10 @@ impl DynamicLightEngine {
|
|||||||
let section_index =
|
let section_index =
|
||||||
(relative.y - chunk.section.min_y) as usize / BlockPalette::SIZE;
|
(relative.y - chunk.section.min_y) as usize / BlockPalette::SIZE;
|
||||||
// Bounds check for section index
|
// Bounds check for section index
|
||||||
let mut light_engine = chunk.light_engine.lock().unwrap_or_else(|e| e.into_inner());
|
let mut light_engine = chunk
|
||||||
|
.light_engine
|
||||||
|
.lock()
|
||||||
|
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||||
if section_index >= light_engine.block_light.len() {
|
if section_index >= light_engine.block_light.len() {
|
||||||
return Err("Invalid section index".to_string());
|
return Err("Invalid section index".to_string());
|
||||||
}
|
}
|
||||||
@ -509,7 +512,10 @@ impl DynamicLightEngine {
|
|||||||
let section_index =
|
let section_index =
|
||||||
(relative.y - chunk.section.min_y) as usize / BlockPalette::SIZE;
|
(relative.y - chunk.section.min_y) as usize / BlockPalette::SIZE;
|
||||||
|
|
||||||
let light_engine = chunk.light_engine.lock().unwrap_or_else(|e| e.into_inner());
|
let light_engine = chunk
|
||||||
|
.light_engine
|
||||||
|
.lock()
|
||||||
|
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||||
// Bounds check for section index (lock the light engine)
|
// Bounds check for section index (lock the light engine)
|
||||||
if section_index >= light_engine.sky_light.len() {
|
if section_index >= light_engine.sky_light.len() {
|
||||||
return 15;
|
return 15;
|
||||||
@ -536,7 +542,10 @@ impl DynamicLightEngine {
|
|||||||
let section_index =
|
let section_index =
|
||||||
(relative.y - chunk.section.min_y) as usize / BlockPalette::SIZE;
|
(relative.y - chunk.section.min_y) as usize / BlockPalette::SIZE;
|
||||||
// Bounds check for section index
|
// Bounds check for section index
|
||||||
let mut light_engine = chunk.light_engine.lock().unwrap_or_else(|e| e.into_inner());
|
let mut light_engine = chunk
|
||||||
|
.light_engine
|
||||||
|
.lock()
|
||||||
|
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||||
if section_index >= light_engine.sky_light.len() {
|
if section_index >= light_engine.sky_light.len() {
|
||||||
return Err("Invalid section index".to_string());
|
return Err("Invalid section index".to_string());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,6 @@ const fn get_chunk_index(cache: &Cache, chunk_x: i32, chunk_z: i32) -> Option<us
|
|||||||
Some((rel_x * cache.size + rel_z) as usize)
|
Some((rel_x * cache.size + rel_z) as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn get_section_y(cache: &Cache, pos_y: i32) -> Option<usize> {
|
fn get_section_y(cache: &Cache, pos_y: i32) -> Option<usize> {
|
||||||
let bottom = cache.bottom_y() as i32;
|
let bottom = cache.bottom_y() as i32;
|
||||||
if pos_y < bottom {
|
if pos_y < bottom {
|
||||||
@ -58,7 +57,6 @@ pub fn get_block_light(cache: &Cache, pos: BlockPos) -> u8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn set_block_light(cache: &mut Cache, pos: BlockPos, level: u8) {
|
pub fn set_block_light(cache: &mut Cache, pos: BlockPos, level: u8) {
|
||||||
let chunk_x = pos.0.x >> 4;
|
let chunk_x = pos.0.x >> 4;
|
||||||
let chunk_z = pos.0.z >> 4;
|
let chunk_z = pos.0.z >> 4;
|
||||||
@ -76,7 +74,10 @@ pub fn set_block_light(cache: &mut Cache, pos: BlockPos, level: u8) {
|
|||||||
|
|
||||||
match &mut cache.chunks[idx] {
|
match &mut cache.chunks[idx] {
|
||||||
Chunk::Level(c) => {
|
Chunk::Level(c) => {
|
||||||
let mut light_engine = c.light_engine.lock().unwrap_or_else(|e| e.into_inner());
|
let mut light_engine = c
|
||||||
|
.light_engine
|
||||||
|
.lock()
|
||||||
|
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||||
if section_y < light_engine.block_light.len() {
|
if section_y < light_engine.block_light.len() {
|
||||||
light_engine.block_light[section_y].set(x, y, z, level);
|
light_engine.block_light[section_y].set(x, y, z, level);
|
||||||
c.dirty.store(true, std::sync::atomic::Ordering::Relaxed);
|
c.dirty.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
@ -90,7 +91,6 @@ pub fn set_block_light(cache: &mut Cache, pos: BlockPos, level: u8) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_sky_light(cache: &Cache, pos: BlockPos) -> u8 {
|
pub fn get_sky_light(cache: &Cache, pos: BlockPos) -> u8 {
|
||||||
let chunk_x = pos.0.x >> 4;
|
let chunk_x = pos.0.x >> 4;
|
||||||
@ -125,7 +125,6 @@ pub fn get_sky_light(cache: &Cache, pos: BlockPos) -> u8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn set_sky_light(cache: &mut Cache, pos: BlockPos, level: u8) {
|
pub fn set_sky_light(cache: &mut Cache, pos: BlockPos, level: u8) {
|
||||||
let chunk_x = pos.0.x >> 4;
|
let chunk_x = pos.0.x >> 4;
|
||||||
let chunk_z = pos.0.z >> 4;
|
let chunk_z = pos.0.z >> 4;
|
||||||
@ -143,7 +142,10 @@ pub fn set_sky_light(cache: &mut Cache, pos: BlockPos, level: u8) {
|
|||||||
|
|
||||||
match &mut cache.chunks[idx] {
|
match &mut cache.chunks[idx] {
|
||||||
Chunk::Level(c) => {
|
Chunk::Level(c) => {
|
||||||
let mut light_engine = c.light_engine.lock().unwrap_or_else(|e| e.into_inner());
|
let mut light_engine = c
|
||||||
|
.light_engine
|
||||||
|
.lock()
|
||||||
|
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||||
if section_y < light_engine.sky_light.len() {
|
if section_y < light_engine.sky_light.len() {
|
||||||
light_engine.sky_light[section_y].set(x, y, z, level);
|
light_engine.sky_light[section_y].set(x, y, z, level);
|
||||||
c.dirty.store(true, std::sync::atomic::Ordering::Relaxed);
|
c.dirty.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
|||||||
@ -25,7 +25,10 @@ impl<'a, T: std::hash::Hash + Eq> ChunkTickScheduler<&'a T> {
|
|||||||
let next_offset = (current_offset + 1) % MAX_TICK_DELAY;
|
let next_offset = (current_offset + 1) % MAX_TICK_DELAY;
|
||||||
self.offset.store(next_offset, Ordering::SeqCst);
|
self.offset.store(next_offset, Ordering::SeqCst);
|
||||||
|
|
||||||
let mut inner_guard = self.inner.lock().unwrap_or_else(|e| e.into_inner());
|
let mut inner_guard = self
|
||||||
|
.inner
|
||||||
|
.lock()
|
||||||
|
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||||
let Some(inner) = inner_guard.as_mut() else {
|
let Some(inner) = inner_guard.as_mut() else {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
};
|
};
|
||||||
@ -43,7 +46,10 @@ impl<'a, T: std::hash::Hash + Eq> ChunkTickScheduler<&'a T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn schedule_tick(&self, tick: &ScheduledTick<&'a T>, sub_tick_order: u64) {
|
pub fn schedule_tick(&self, tick: &ScheduledTick<&'a T>, sub_tick_order: u64) {
|
||||||
let mut inner_guard = self.inner.lock().unwrap_or_else(|e| e.into_inner());
|
let mut inner_guard = self
|
||||||
|
.inner
|
||||||
|
.lock()
|
||||||
|
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||||
let inner = inner_guard.get_or_insert_with(|| {
|
let inner = inner_guard.get_or_insert_with(|| {
|
||||||
Box::new(ChunkTickSchedulerInner {
|
Box::new(ChunkTickSchedulerInner {
|
||||||
tick_queue: std::array::from_fn(|_| Vec::new()),
|
tick_queue: std::array::from_fn(|_| Vec::new()),
|
||||||
@ -67,7 +73,7 @@ impl<'a, T: std::hash::Hash + Eq> ChunkTickScheduler<&'a T> {
|
|||||||
pub fn is_scheduled(&self, pos: BlockPos, value: &T) -> bool {
|
pub fn is_scheduled(&self, pos: BlockPos, value: &T) -> bool {
|
||||||
self.inner
|
self.inner
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap_or_else(|e| e.into_inner())
|
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.is_some_and(|inner| inner.queued_ticks.contains(&(pos, value)))
|
.is_some_and(|inner| inner.queued_ticks.contains(&(pos, value)))
|
||||||
}
|
}
|
||||||
@ -75,7 +81,7 @@ impl<'a, T: std::hash::Hash + Eq> ChunkTickScheduler<&'a T> {
|
|||||||
pub fn has_ticks(&self) -> bool {
|
pub fn has_ticks(&self) -> bool {
|
||||||
self.inner
|
self.inner
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap_or_else(|e| e.into_inner())
|
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.is_some_and(|inner| !inner.queued_ticks.is_empty())
|
.is_some_and(|inner| !inner.queued_ticks.is_empty())
|
||||||
}
|
}
|
||||||
@ -83,7 +89,10 @@ impl<'a, T: std::hash::Hash + Eq> ChunkTickScheduler<&'a T> {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn to_vec(&self) -> Vec<ScheduledTick<&'a T>> {
|
pub fn to_vec(&self) -> Vec<ScheduledTick<&'a T>> {
|
||||||
let offset = self.offset.load(Ordering::SeqCst);
|
let offset = self.offset.load(Ordering::SeqCst);
|
||||||
let inner_guard = self.inner.lock().unwrap_or_else(|e| e.into_inner());
|
let inner_guard = self
|
||||||
|
.inner
|
||||||
|
.lock()
|
||||||
|
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||||
let Some(inner) = inner_guard.as_ref() else {
|
let Some(inner) = inner_guard.as_ref() else {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
};
|
};
|
||||||
@ -112,7 +121,10 @@ impl<'a, T: std::hash::Hash + Eq + 'static> FromIterator<ScheduledTick<&'a T>>
|
|||||||
|
|
||||||
let (lower, _) = iter.size_hint();
|
let (lower, _) = iter.size_hint();
|
||||||
if lower > 0 {
|
if lower > 0 {
|
||||||
let mut inner_guard = scheduler.inner.lock().unwrap_or_else(|e| e.into_inner());
|
let mut inner_guard = scheduler
|
||||||
|
.inner
|
||||||
|
.lock()
|
||||||
|
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||||
let inner = inner_guard.get_or_insert_with(|| {
|
let inner = inner_guard.get_or_insert_with(|| {
|
||||||
Box::new(ChunkTickSchedulerInner {
|
Box::new(ChunkTickSchedulerInner {
|
||||||
tick_queue: std::array::from_fn(|_| Vec::new()),
|
tick_queue: std::array::from_fn(|_| Vec::new()),
|
||||||
|
|||||||
@ -314,10 +314,11 @@ impl BlockBehaviour for BedBlock {
|
|||||||
if args
|
if args
|
||||||
.player
|
.player
|
||||||
.set_respawn_point(
|
.set_respawn_point(
|
||||||
args.world.dimension,
|
args.world.dimension.clone(),
|
||||||
bed_head_pos,
|
bed_head_pos,
|
||||||
args.player.get_entity().yaw.load(),
|
args.player.get_entity().yaw.load(),
|
||||||
args.player.get_entity().pitch.load(),
|
args.player.get_entity().pitch.load(),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
|||||||
@ -195,9 +195,9 @@ impl BlockBehaviour for FireBlock {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let dimension = args.world.dimension;
|
let dimension = &args.world.dimension;
|
||||||
// First lets check if we are in OverWorld or Nether, its not possible to place an Nether portal in other dimensions in Vanilla
|
// First lets check if we are in OverWorld or Nether, its not possible to place an Nether portal in other dimensions in Vanilla
|
||||||
if (dimension == Dimension::OVERWORLD || dimension == Dimension::THE_NETHER)
|
if (dimension == &Dimension::OVERWORLD || dimension == &Dimension::THE_NETHER)
|
||||||
&& let Some(portal) =
|
&& let Some(portal) =
|
||||||
NetherPortal::get_new_portal(args.world, args.position, HorizontalAxis::X).await
|
NetherPortal::get_new_portal(args.world, args.position, HorizontalAxis::X).await
|
||||||
{
|
{
|
||||||
|
|||||||
@ -90,8 +90,8 @@ impl FireBlockBase {
|
|||||||
block_pos: &BlockPos,
|
block_pos: &BlockPos,
|
||||||
direction: BlockDirection,
|
direction: BlockDirection,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let dimension = world.dimension;
|
let dimension = &world.dimension;
|
||||||
if dimension != Dimension::OVERWORLD && dimension != Dimension::THE_NETHER {
|
if dimension != &Dimension::OVERWORLD && dimension != &Dimension::THE_NETHER {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
|
|||||||
@ -124,10 +124,10 @@ impl CommandExecutor for TargetsPosAngleExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn set_spawnpoint(sender: &CommandSender, target: &Arc<Player>, pos: BlockPos, yaw: f32) {
|
async fn set_spawnpoint(sender: &CommandSender, target: &Arc<Player>, pos: BlockPos, yaw: f32) {
|
||||||
let dimension = target.world().dimension;
|
let dimension = &target.world().dimension;
|
||||||
|
|
||||||
target
|
target
|
||||||
.set_respawn_point_forced(dimension, pos, yaw, 0.0)
|
.set_respawn_point(dimension.clone(), pos, yaw, 0.0, true)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
sender
|
sender
|
||||||
|
|||||||
@ -3,13 +3,16 @@ use std::sync::atomic::{AtomicBool, AtomicI32, Ordering::Relaxed};
|
|||||||
|
|
||||||
use pumpkin_data::damage::DamageType;
|
use pumpkin_data::damage::DamageType;
|
||||||
use pumpkin_data::sound::Sound;
|
use pumpkin_data::sound::Sound;
|
||||||
|
use pumpkin_data::tag::{self, Taggable};
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
|
use pumpkin_world::chunk::ChunkHeightmapType;
|
||||||
use rand::RngExt;
|
use rand::RngExt;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use crate::entity::mob::{Mob, MobEntity};
|
use crate::entity::mob::{Mob, MobEntity};
|
||||||
use crate::entity::{Entity, EntityBase, EntityBaseFuture, NBTStorage, NbtFuture};
|
use crate::entity::{Entity, EntityBase, EntityBaseFuture, NBTStorage, NbtFuture};
|
||||||
|
use crate::world::World;
|
||||||
|
|
||||||
const ROOSTING_FLAG: u8 = 1;
|
const ROOSTING_FLAG: u8 = 1;
|
||||||
const CLOSE_PLAYER_DISTANCE: f64 = 4.0;
|
const CLOSE_PLAYER_DISTANCE: f64 = 4.0;
|
||||||
@ -39,6 +42,31 @@ impl BatEntity {
|
|||||||
mob_arc
|
mob_arc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn check_bat_spawn_rules(world: &World, pos: &BlockPos) -> bool {
|
||||||
|
if pos.0.y
|
||||||
|
>= world
|
||||||
|
.get_heightmap_height(ChunkHeightmapType::WorldSurface, pos.0.x, pos.0.z)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if rand::random_bool(1.0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if world.get_max_local_raw_brightness(pos).await > rand::random_range(0..4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if world
|
||||||
|
.get_block(pos)
|
||||||
|
.await
|
||||||
|
.has_tag(&tag::Block::MINECRAFT_BATS_SPAWNABLE_ON)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//TODO:check_mob_spawn_rules(entity_type, world, spawn_reason, pos).await
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn is_roosting(&self) -> bool {
|
fn is_roosting(&self) -> bool {
|
||||||
self.roosting.load(Relaxed)
|
self.roosting.load(Relaxed)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,10 +13,13 @@ use pumpkin_data::meta_data_type::MetaDataType;
|
|||||||
use pumpkin_data::tag::{self, Taggable};
|
use pumpkin_data::tag::{self, Taggable};
|
||||||
use pumpkin_data::tracked_data::TrackedData;
|
use pumpkin_data::tracked_data::TrackedData;
|
||||||
use pumpkin_protocol::java::client::play::{CHeadRot, CUpdateEntityRot, Metadata};
|
use pumpkin_protocol::java::client::play::{CHeadRot, CUpdateEntityRot, Metadata};
|
||||||
|
use pumpkin_util::Difficulty;
|
||||||
use pumpkin_util::math::boundingbox::BoundingBox;
|
use pumpkin_util::math::boundingbox::BoundingBox;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_util::math::vector2::Vector2;
|
use pumpkin_util::math::vector2::Vector2;
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
|
use pumpkin_util::random::xoroshiro128::Xoroshiro;
|
||||||
|
use pumpkin_util::random::{RandomGenerator, get_seed};
|
||||||
use rand::RngExt;
|
use rand::RngExt;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -184,6 +187,44 @@ impl MobEntity {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn is_dark_enough_to_spawn(world: &World, pos: &BlockPos) -> bool {
|
||||||
|
let sky_light = world.get_sky_light_level(pos).await;
|
||||||
|
if sky_light > rand::random_range(0..32) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dimension = &world.dimension;
|
||||||
|
let block_light_limit = dimension.monster_spawn_block_light_limit;
|
||||||
|
|
||||||
|
let block_light = world.get_block_light_level(pos).await.unwrap();
|
||||||
|
if block_light_limit < 15 && block_light > block_light_limit {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let current_brightness = if world.is_thundering().await {
|
||||||
|
(sky_light - 10).max(block_light)
|
||||||
|
} else {
|
||||||
|
sky_light.max(block_light)
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(get_seed()));
|
||||||
|
current_brightness <= dimension.monster_spawn_light_level.get(&mut random) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn check_monster_spawn_rules(world: &World, pos: &BlockPos) -> bool {
|
||||||
|
if world.level_info.load().difficulty == Difficulty::Peaceful {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Self::is_dark_enough_to_spawn(world, pos).await {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO:check_mob_spawn_rules(entity_type, world, spawn_reason, pos).await
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn try_attack(&self, caller: &dyn EntityBase, target: &dyn EntityBase) {
|
pub async fn try_attack(&self, caller: &dyn EntityBase, target: &dyn EntityBase) {
|
||||||
if self.living_entity.dead.load(Relaxed) {
|
if self.living_entity.dead.load(Relaxed) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -410,7 +410,7 @@ pub struct Player {
|
|||||||
/// The player's previous gamemode
|
/// The player's previous gamemode
|
||||||
pub previous_gamemode: AtomicCell<Option<GameMode>>,
|
pub previous_gamemode: AtomicCell<Option<GameMode>>,
|
||||||
/// The player's spawnpoint
|
/// The player's spawnpoint
|
||||||
pub respawn_point: AtomicCell<Option<RespawnPoint>>,
|
pub respawn_point: Mutex<Option<RespawnPoint>>,
|
||||||
/// The player's sleep status
|
/// The player's sleep status
|
||||||
pub sleeping_since: AtomicCell<Option<u8>>,
|
pub sleeping_since: AtomicCell<Option<u8>>,
|
||||||
/// Manages the player's breath level
|
/// Manages the player's breath level
|
||||||
@ -566,7 +566,7 @@ impl Player {
|
|||||||
gamemode: AtomicCell::new(gamemode),
|
gamemode: AtomicCell::new(gamemode),
|
||||||
previous_gamemode: AtomicCell::new(None),
|
previous_gamemode: AtomicCell::new(None),
|
||||||
// TODO: Send the CPlayerSpawnPosition packet when the client connects with proper values
|
// TODO: Send the CPlayerSpawnPosition packet when the client connects with proper values
|
||||||
respawn_point: AtomicCell::new(None),
|
respawn_point: Mutex::new(None),
|
||||||
sleeping_since: AtomicCell::new(None),
|
sleeping_since: AtomicCell::new(None),
|
||||||
// We want this to be an impossible watched section so that `chunker::update_position`
|
// We want this to be an impossible watched section so that `chunker::update_position`
|
||||||
// will mark chunks as watched for a new join rather than a respawn.
|
// will mark chunks as watched for a new join rather than a respawn.
|
||||||
@ -1072,21 +1072,16 @@ impl Player {
|
|||||||
block_pos: BlockPos,
|
block_pos: BlockPos,
|
||||||
yaw: f32,
|
yaw: f32,
|
||||||
pitch: f32,
|
pitch: f32,
|
||||||
|
forced: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if let Some(respawn_point) = self.respawn_point.load()
|
if !forced
|
||||||
|
&& let Some(respawn_point) = self.respawn_point.lock().await.as_ref()
|
||||||
&& dimension == respawn_point.dimension
|
&& dimension == respawn_point.dimension
|
||||||
&& block_pos == respawn_point.position
|
&& block_pos == respawn_point.position
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.respawn_point.store(Some(RespawnPoint {
|
|
||||||
dimension,
|
|
||||||
position: block_pos,
|
|
||||||
yaw,
|
|
||||||
force: false,
|
|
||||||
}));
|
|
||||||
|
|
||||||
self.client
|
self.client
|
||||||
.send_packet_now(&CPlayerSpawnPosition::new(
|
.send_packet_now(&CPlayerSpawnPosition::new(
|
||||||
block_pos,
|
block_pos,
|
||||||
@ -1095,35 +1090,16 @@ impl Player {
|
|||||||
dimension.minecraft_name.to_owned(),
|
dimension.minecraft_name.to_owned(),
|
||||||
))
|
))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
*self.respawn_point.lock().await = Some(RespawnPoint {
|
||||||
|
dimension,
|
||||||
|
position: block_pos,
|
||||||
|
yaw,
|
||||||
|
force: forced,
|
||||||
|
});
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the respawn point with force=true, bypassing bed/anchor checks.
|
|
||||||
/// Used by /spawnpoint command.
|
|
||||||
pub async fn set_respawn_point_forced(
|
|
||||||
&self,
|
|
||||||
dimension: Dimension,
|
|
||||||
block_pos: BlockPos,
|
|
||||||
yaw: f32,
|
|
||||||
pitch: f32,
|
|
||||||
) {
|
|
||||||
self.respawn_point.store(Some(RespawnPoint {
|
|
||||||
dimension,
|
|
||||||
position: block_pos,
|
|
||||||
yaw,
|
|
||||||
force: true,
|
|
||||||
}));
|
|
||||||
|
|
||||||
self.client
|
|
||||||
.send_packet_now(&CPlayerSpawnPosition::new(
|
|
||||||
block_pos,
|
|
||||||
yaw,
|
|
||||||
pitch,
|
|
||||||
dimension.minecraft_name.to_owned(),
|
|
||||||
))
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calculates the player's respawn point based on stored spawn data.
|
/// Calculates the player's respawn point based on stored spawn data.
|
||||||
///
|
///
|
||||||
/// Returns `Some(CalculatedRespawnPoint)` if a valid respawn point exists, `None` otherwise.
|
/// Returns `Some(CalculatedRespawnPoint)` if a valid respawn point exists, `None` otherwise.
|
||||||
@ -1143,7 +1119,8 @@ impl Player {
|
|||||||
type BedProperties = pumpkin_data::block_properties::WhiteBedLikeProperties;
|
type BedProperties = pumpkin_data::block_properties::WhiteBedLikeProperties;
|
||||||
type AnchorProperties = pumpkin_data::block_properties::RespawnAnchorLikeProperties;
|
type AnchorProperties = pumpkin_data::block_properties::RespawnAnchorLikeProperties;
|
||||||
|
|
||||||
let respawn_point = self.respawn_point.load()?;
|
let respawn_guard = self.respawn_point.lock().await;
|
||||||
|
let respawn_point = respawn_guard.as_ref()?;
|
||||||
let world = self.world();
|
let world = self.world();
|
||||||
let pos = &respawn_point.position;
|
let pos = &respawn_point.position;
|
||||||
let (block, state_id) = world.get_block_and_state_id(pos).await;
|
let (block, state_id) = world.get_block_and_state_id(pos).await;
|
||||||
@ -1172,7 +1149,7 @@ impl Player {
|
|||||||
position,
|
position,
|
||||||
yaw: respawn_point.yaw,
|
yaw: respawn_point.yaw,
|
||||||
pitch: 0.0,
|
pitch: 0.0,
|
||||||
dimension: respawn_point.dimension,
|
dimension: respawn_point.dimension.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
@ -1192,7 +1169,7 @@ impl Player {
|
|||||||
position: spawn_pos,
|
position: spawn_pos,
|
||||||
yaw: respawn_point.yaw,
|
yaw: respawn_point.yaw,
|
||||||
pitch: 0.0,
|
pitch: 0.0,
|
||||||
dimension: respawn_point.dimension,
|
dimension: respawn_point.dimension.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
@ -1226,7 +1203,7 @@ impl Player {
|
|||||||
position: spawn_pos,
|
position: spawn_pos,
|
||||||
yaw: respawn_point.yaw,
|
yaw: respawn_point.yaw,
|
||||||
pitch: 0.0,
|
pitch: 0.0,
|
||||||
dimension: respawn_point.dimension,
|
dimension: respawn_point.dimension.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
@ -1512,10 +1489,11 @@ impl Player {
|
|||||||
|
|
||||||
pub async fn wake_up(&self) {
|
pub async fn wake_up(&self) {
|
||||||
let world = self.world();
|
let world = self.world();
|
||||||
let respawn_point = self
|
let respawn_point = self.respawn_point.lock().await;
|
||||||
.respawn_point
|
let Some(respawn_point) = respawn_point.as_ref() else {
|
||||||
.load()
|
warn!("Player waking up should have it's respawn point set on the bed");
|
||||||
.expect("Player waking up should have it's respawn point set on the bed.");
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let (bed, bed_state) = world.get_block_and_state_id(&respawn_point.position).await;
|
let (bed, bed_state) = world.get_block_and_state_id(&respawn_point.position).await;
|
||||||
BedBlock::set_occupied(false, &world, bed, &respawn_point.position, bed_state).await;
|
BedBlock::set_occupied(false, &world, bed, &respawn_point.position, bed_state).await;
|
||||||
@ -3744,7 +3722,7 @@ impl NBTStorage for Player {
|
|||||||
nbt.put_bool(self.has_played_before.load(Ordering::Relaxed));
|
nbt.put_bool(self.has_played_before.load(Ordering::Relaxed));
|
||||||
self.hunger_manager.write_nbt(nbt).await;
|
self.hunger_manager.write_nbt(nbt).await;
|
||||||
|
|
||||||
if let Some(respawn) = self.respawn_point.load().as_ref() {
|
if let Some(respawn) = self.respawn_point.lock().await.as_ref() {
|
||||||
nbt.put_bool(true);
|
nbt.put_bool(true);
|
||||||
nbt.put_int(respawn.position.0.x);
|
nbt.put_int(respawn.position.0.x);
|
||||||
nbt.put_int(respawn.position.0.y);
|
nbt.put_int(respawn.position.0.y);
|
||||||
@ -3796,15 +3774,15 @@ impl NBTStorage for Player {
|
|||||||
let dim = nbt
|
let dim = nbt
|
||||||
.get_string()
|
.get_string()
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|s| Dimension::from_name(s.as_str()).copied())
|
.and_then(|s| Dimension::from_name(s.as_str()).cloned())
|
||||||
.unwrap_or(self.world().dimension);
|
.unwrap_or(self.world().dimension.clone());
|
||||||
let force = nbt.get_bool().unwrap_or(false);
|
let force = nbt.get_bool().unwrap_or(false);
|
||||||
self.respawn_point.store(Some(RespawnPoint {
|
*self.respawn_point.lock().await = Some(RespawnPoint {
|
||||||
dimension: dim,
|
dimension: dim,
|
||||||
position: BlockPos(Vector3::new(x, y, z)),
|
position: BlockPos(Vector3::new(x, y, z)),
|
||||||
yaw: 0.0,
|
yaw: 0.0,
|
||||||
force,
|
force,
|
||||||
}));
|
});
|
||||||
}
|
}
|
||||||
self.enchantment_seed
|
self.enchantment_seed
|
||||||
.store(nbt.get_int().unwrap_or(rand::random()), Ordering::Relaxed);
|
.store(nbt.get_int().unwrap_or(rand::random()), Ordering::Relaxed);
|
||||||
@ -4168,7 +4146,7 @@ impl Abilities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Represents the player's stored respawn point (bed/anchor/forced).
|
/// Represents the player's stored respawn point (bed/anchor/forced).
|
||||||
#[derive(Copy, Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct RespawnPoint {
|
pub struct RespawnPoint {
|
||||||
pub dimension: Dimension,
|
pub dimension: Dimension,
|
||||||
pub position: BlockPos,
|
pub position: BlockPos,
|
||||||
@ -4176,9 +4154,6 @@ pub struct RespawnPoint {
|
|||||||
pub force: bool,
|
pub force: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculated respawn position ready for use.
|
|
||||||
/// Returned by `calculate_respawn_point()`.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct CalculatedRespawnPoint {
|
pub struct CalculatedRespawnPoint {
|
||||||
/// The exact position to spawn at (centered in block).
|
/// The exact position to spawn at (centered in block).
|
||||||
pub position: Vector3<f64>,
|
pub position: Vector3<f64>,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use pumpkin_data::entity::EntityType;
|
use pumpkin_data::entity::EntityType;
|
||||||
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ use crate::entity::experience_orb::ExperienceOrbEntity;
|
|||||||
use crate::entity::falling::FallingEntity;
|
use crate::entity::falling::FallingEntity;
|
||||||
use crate::entity::item::ItemEntity;
|
use crate::entity::item::ItemEntity;
|
||||||
use crate::entity::living::LivingEntity;
|
use crate::entity::living::LivingEntity;
|
||||||
use crate::entity::mob::bat::BatEntity;
|
use crate::entity::mob::bat::{self, BatEntity};
|
||||||
use crate::entity::mob::blaze::BlazeEntity;
|
use crate::entity::mob::blaze::BlazeEntity;
|
||||||
use crate::entity::mob::breeze::BreezeEntity;
|
use crate::entity::mob::breeze::BreezeEntity;
|
||||||
use crate::entity::mob::cave_spider::CaveSpiderEntity;
|
use crate::entity::mob::cave_spider::CaveSpiderEntity;
|
||||||
@ -107,7 +108,7 @@ use crate::entity::projectile::splash_potion::SplashPotionEntity;
|
|||||||
use crate::entity::projectile::wind_charge::WindChargeEntity;
|
use crate::entity::projectile::wind_charge::WindChargeEntity;
|
||||||
use crate::entity::tnt::TNTEntity;
|
use crate::entity::tnt::TNTEntity;
|
||||||
use crate::entity::vehicle::boat::BoatEntity;
|
use crate::entity::vehicle::boat::BoatEntity;
|
||||||
use crate::entity::{Entity, EntityBase};
|
use crate::entity::{Entity, EntityBase, mob};
|
||||||
use crate::world::World;
|
use crate::world::World;
|
||||||
use pumpkin_data::Block;
|
use pumpkin_data::Block;
|
||||||
use pumpkin_data::item::Item;
|
use pumpkin_data::item::Item;
|
||||||
@ -304,3 +305,41 @@ pub async fn from_type(
|
|||||||
|
|
||||||
mob
|
mob
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn check_spawn_rules(
|
||||||
|
entity_type: &'static EntityType,
|
||||||
|
world: &World,
|
||||||
|
pos: &BlockPos,
|
||||||
|
) -> bool {
|
||||||
|
let id = entity_type.id;
|
||||||
|
|
||||||
|
if id == EntityType::BOGGED.id
|
||||||
|
|| id == EntityType::CAVE_SPIDER.id
|
||||||
|
|| id == EntityType::CREEPER.id
|
||||||
|
|| id == EntityType::ENDERMAN.id
|
||||||
|
|| id == EntityType::GIANT.id
|
||||||
|
|| id == EntityType::RAVAGER.id
|
||||||
|
|| id == EntityType::SKELETON.id
|
||||||
|
|| id == EntityType::SPIDER.id
|
||||||
|
|| id == EntityType::WITCH.id
|
||||||
|
|| id == EntityType::WITHER.id
|
||||||
|
|| id == EntityType::WITHER_SKELETON.id
|
||||||
|
|| id == EntityType::ZOMBIE.id
|
||||||
|
|| id == EntityType::ZOMBIE_HORSE.id
|
||||||
|
|| id == EntityType::ZOMBIE_VILLAGER.id
|
||||||
|
|| id == EntityType::CREAKING.id
|
||||||
|
|| id == EntityType::EVOKER.id
|
||||||
|
|| id == EntityType::ILLUSIONER.id
|
||||||
|
|| id == EntityType::VEX.id
|
||||||
|
|| id == EntityType::VINDICATOR.id
|
||||||
|
|| id == EntityType::WARDEN.id
|
||||||
|
{
|
||||||
|
return mob::MobEntity::check_monster_spawn_rules(world, pos).await;
|
||||||
|
}
|
||||||
|
if id == EntityType::BAT.id {
|
||||||
|
return bat::BatEntity::check_bat_spawn_rules(world, pos).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|||||||
@ -45,7 +45,7 @@ impl ItemBehaviour for MapItem {
|
|||||||
let map_id = server.next_map_id();
|
let map_id = server.next_map_id();
|
||||||
let _ = server.map_manager.create_map(
|
let _ = server.map_manager.create_map(
|
||||||
map_id,
|
map_id,
|
||||||
player.world().dimension,
|
player.world().dimension.clone(),
|
||||||
player.position().x as i32,
|
player.position().x as i32,
|
||||||
player.position().z as i32,
|
player.position().z as i32,
|
||||||
0, // Default scale
|
0, // Default scale
|
||||||
|
|||||||
@ -286,7 +286,7 @@ impl Server {
|
|||||||
.color_named(NamedColor::DarkGreen)
|
.color_named(NamedColor::DarkGreen)
|
||||||
.to_pretty_console()
|
.to_pretty_console()
|
||||||
);
|
);
|
||||||
let level = into_level(dim, &config, path, seed, Some(pool));
|
let level = into_level(dim.clone(), &config, path, seed, Some(pool));
|
||||||
let world = Arc::new(World::load(level.clone(), l_info, dim, registry, weak));
|
let world = Arc::new(World::load(level.clone(), l_info, dim, registry, weak));
|
||||||
let portal: Arc<dyn WorldPortalExt> = Arc::new(WorldPortal(
|
let portal: Arc<dyn WorldPortalExt> = Arc::new(WorldPortal(
|
||||||
world.clone(),
|
world.clone(),
|
||||||
@ -383,8 +383,13 @@ impl Server {
|
|||||||
let seed = server.level_info.load().world_gen_settings.seed;
|
let seed = server.level_info.load().world_gen_settings.seed;
|
||||||
|
|
||||||
// TODO: gen_pool should be reused
|
// TODO: gen_pool should be reused
|
||||||
let level =
|
let level = pumpkin_world::dimension::into_level(
|
||||||
pumpkin_world::dimension::into_level(dimension, &config, world_path, seed, None);
|
dimension.clone(),
|
||||||
|
&config,
|
||||||
|
world_path,
|
||||||
|
seed,
|
||||||
|
None,
|
||||||
|
);
|
||||||
let world: World = World::load(level.clone(), l_info, dimension, registry, weak);
|
let world: World = World::load(level.clone(), l_info, dimension, registry, weak);
|
||||||
let world = Arc::new(world);
|
let world = Arc::new(world);
|
||||||
let portal: Arc<dyn WorldPortalExt> = Arc::new(WorldPortal(
|
let portal: Arc<dyn WorldPortalExt> = Arc::new(WorldPortal(
|
||||||
|
|||||||
@ -150,7 +150,7 @@ pub mod weather;
|
|||||||
use crate::world::natural_spawner::{SpawnState, spawn_for_chunk};
|
use crate::world::natural_spawner::{SpawnState, spawn_for_chunk};
|
||||||
use pumpkin_config::lighting::LightingEngineConfig;
|
use pumpkin_config::lighting::LightingEngineConfig;
|
||||||
use pumpkin_data::effect::StatusEffect;
|
use pumpkin_data::effect::StatusEffect;
|
||||||
use pumpkin_world::chunk::ChunkHeightmapType::MotionBlocking;
|
use pumpkin_world::chunk::ChunkHeightmapType::{self, MotionBlocking};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use weather::Weather;
|
use weather::Weather;
|
||||||
|
|
||||||
@ -241,7 +241,8 @@ impl World {
|
|||||||
|
|
||||||
// Load portal POI from disk (PoiStorage::new automatically loads from disk if files exist)
|
// Load portal POI from disk (PoiStorage::new automatically loads from disk if files exist)
|
||||||
let portal_poi = portal::PortalPoiStorage::new(&level.level_folder.root_folder);
|
let portal_poi = portal::PortalPoiStorage::new(&level.level_folder.root_folder);
|
||||||
|
let dragon_fight =
|
||||||
|
(dimension == Dimension::THE_END).then(|| Mutex::new(dragon_fight::DragonFight::new()));
|
||||||
Self {
|
Self {
|
||||||
uuid: Uuid::new_v4(),
|
uuid: Uuid::new_v4(),
|
||||||
level,
|
level,
|
||||||
@ -259,8 +260,7 @@ impl World {
|
|||||||
synced_block_event_queue: Mutex::new(Vec::new()),
|
synced_block_event_queue: Mutex::new(Vec::new()),
|
||||||
unsent_block_changes: Mutex::new(HashMap::new()),
|
unsent_block_changes: Mutex::new(HashMap::new()),
|
||||||
portal_poi: Mutex::new(portal_poi),
|
portal_poi: Mutex::new(portal_poi),
|
||||||
dragon_fight: (dimension == Dimension::THE_END)
|
dragon_fight,
|
||||||
.then(|| Mutex::new(dragon_fight::DragonFight::new())),
|
|
||||||
spawn_state: ArcSwap::new(Arc::new(SpawnState::empty())),
|
spawn_state: ArcSwap::new(Arc::new(SpawnState::empty())),
|
||||||
active_chunks: ArcSwap::new(Arc::new(FxHashSet::default())),
|
active_chunks: ArcSwap::new(Arc::new(FxHashSet::default())),
|
||||||
server,
|
server,
|
||||||
@ -1574,6 +1574,24 @@ impl World {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_heightmap_height(
|
||||||
|
&self,
|
||||||
|
height_map: ChunkHeightmapType,
|
||||||
|
x: i32,
|
||||||
|
z: i32,
|
||||||
|
) -> i32 {
|
||||||
|
let chunk_pos = Vector2::new(x >> 4, z >> 4);
|
||||||
|
self.level
|
||||||
|
.get_or_fetch_chunk(chunk_pos, |chunk| {
|
||||||
|
chunk
|
||||||
|
.heightmap
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.get(height_map, x, z, self.min_y)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
pub async fn spawn_bedrock_player(
|
pub async fn spawn_bedrock_player(
|
||||||
&self,
|
&self,
|
||||||
@ -1884,7 +1902,7 @@ impl World {
|
|||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
self.dimension,
|
self.dimension.clone(),
|
||||||
biome::hash_seed(self.level.seed.0), // seed
|
biome::hash_seed(self.level.seed.0), // seed
|
||||||
gamemode as u8,
|
gamemode as u8,
|
||||||
player
|
player
|
||||||
@ -2427,7 +2445,7 @@ impl World {
|
|||||||
),
|
),
|
||||||
spawn_yaw,
|
spawn_yaw,
|
||||||
spawn_pitch,
|
spawn_pitch,
|
||||||
self.dimension,
|
self.dimension.clone(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3424,6 +3442,26 @@ impl World {
|
|||||||
replaced_block_state_id
|
replaced_block_state_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_max_local_raw_brightness(&self, pos: &BlockPos) -> u8 {
|
||||||
|
let sky_light = self.get_sky_light_level(pos).await;
|
||||||
|
let block_light = self.get_block_light_level(pos).await.unwrap();
|
||||||
|
sky_light.max(block_light) // TODO: getSkyDarken
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_block_light_level(&self, position: &BlockPos) -> Option<u8> {
|
||||||
|
self.level
|
||||||
|
.light_engine
|
||||||
|
.get_block_light_level(&self.level, position)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_sky_light_level(&self, position: &BlockPos) -> u8 {
|
||||||
|
self.level
|
||||||
|
.light_engine
|
||||||
|
.get_sky_light_level(&self.level, position)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn schedule_block_tick(
|
pub async fn schedule_block_tick(
|
||||||
&self,
|
&self,
|
||||||
block: &Block,
|
block: &Block,
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use crate::entity::EntityBase;
|
use crate::entity::EntityBase;
|
||||||
use crate::entity::r#type::from_type;
|
use crate::entity::r#type::{check_spawn_rules, from_type};
|
||||||
use crate::world::World;
|
use crate::world::World;
|
||||||
use arc_swap::ArcSwap;
|
use arc_swap::ArcSwap;
|
||||||
use pumpkin_data::biome::Spawner;
|
use pumpkin_data::biome::Spawner;
|
||||||
@ -840,6 +840,9 @@ pub async fn is_valid_spawn_position_for_type(
|
|||||||
if !is_spawn_position_ok(world, block_pos, entity_type).await {
|
if !is_spawn_position_ok(world, block_pos, entity_type).await {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if !check_spawn_rules(entity_type, world, block_pos).await {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// TODO: we should use getSpawnBox, but this is only modified for slimes and magma slimes
|
// TODO: we should use getSpawnBox, but this is only modified for slimes and magma slimes
|
||||||
if !world
|
if !world
|
||||||
.is_space_empty(BoundingBox::new_from_pos(
|
.is_space_empty(BoundingBox::new_from_pos(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user