Load tags

main
kalle 2025-03-08 15:16:41 +01:00
parent f005964c36
commit bfe34183a5
12 changed files with 309 additions and 50 deletions

View File

@ -12,13 +12,13 @@ N/A = Not applicable
| Registry | Loads | Loads tags | Synced to client | Tags synced to client | | Registry | Loads | Loads tags | Synced to client | Tags synced to client |
|--------------------------------------------------|-------|------------|------------------|-----------------------| |--------------------------------------------------|-------|------------|------------------|-----------------------|
| advancement | ❌ | ❌ | ❌ | ❌ | | advancement | ❌ | ❌ | ❌ | ❌ |
| banner_pattern | ✅ | | ❌ | ❌ | | banner_pattern | ✅ | | ❌ | ❌ |
<!-- |cat_variant | ❌ | ❌ | ❌ | ❌ | --> <!-- |cat_variant | ❌ | ❌ | ❌ | ❌ | -->
| chat_type | ✅ | ❌ | ❌ | ❌ | | chat_type | ✅ | ❌ | ❌ | ❌ |
<!-- |cow_variant | ❌ | ❌ | ❌ | ❌ | --> <!-- |cow_variant | ❌ | ❌ | ❌ | ❌ | -->
| damage_type | ✅ | | ❌ | ❌ | | damage_type | ✅ | | ❌ | ❌ |
| dimension | ❌ | ❌ | ❌ | ❌ | | dimension | ❌ | ❌ | ❌ | ❌ |
| dimension_type | ✅ | | ❌ | ❌ | | dimension_type | ✅ | | ❌ | ❌ |
| enchantment | ❌ | ❌ | ❌ | ❌ | | enchantment | ❌ | ❌ | ❌ | ❌ |
| enchantment_provider | ❌ | ❌ | ❌ | ❌ | | enchantment_provider | ❌ | ❌ | ❌ | ❌ |
<!-- |frog_variant | ❌ | ❌ | ❌ | ❌ | --> <!-- |frog_variant | ❌ | ❌ | ❌ | ❌ | -->
@ -26,17 +26,17 @@ N/A = Not applicable
| item_modifier | ❌ | ❌ | ❌ | ❌ | | item_modifier | ❌ | ❌ | ❌ | ❌ |
| jukebox_song | ❌ | ❌ | ❌ | ❌ | | jukebox_song | ❌ | ❌ | ❌ | ❌ |
| loot_table | ❌ | ❌ | ❌ | ❌ | | loot_table | ❌ | ❌ | ❌ | ❌ |
| painting_variant | ✅ | | ❌ | ❌ | | painting_variant | ✅ | | ❌ | ❌ |
<!-- |pig_variant | ❌ | ❌ | ❌ | ❌ | --> <!-- |pig_variant | ❌ | ❌ | ❌ | ❌ | -->
| predicate | ❌ | ❌ | ❌ | ❌ | | predicate | ❌ | ❌ | ❌ | ❌ |
| recipe | ❌ | ❌ | ❌ | ❌ | | recipe | ❌ | ❌ | ❌ | ❌ |
<!-- |test_environment | ❌ | ❌ | ❌ | ❌ | --> <!-- |test_environment | ❌ | ❌ | ❌ | ❌ | -->
<!-- |test_instance | ❌ | ❌ | ❌ | ❌ | --> <!-- |test_instance | ❌ | ❌ | ❌ | ❌ | -->
| trial_spawner | ❌ | ❌ | ❌ | ❌ | | trial_spawner | ❌ | ❌ | ❌ | ❌ |
| trim_material | ✅ | | ❌ | ❌ | | trim_material | ✅ | | ❌ | ❌ |
| trim_pattern | ✅ | | ❌ | ❌ | | trim_pattern | ✅ | | ❌ | ❌ |
| wolf_variant | ✅ | | ❌ | ❌ | | wolf_variant | ✅ | | ❌ | ❌ |
| worldgen/biome | ✅ | | ❌ | ❌ | | worldgen/biome | ✅ | | ❌ | ❌ |
| worldgen/configured_carver | ❌ | ❌ | ❌ | ❌ | | worldgen/configured_carver | ❌ | ❌ | ❌ | ❌ |
| worldgen/configured_feature | ❌ | ❌ | ❌ | ❌ | | worldgen/configured_feature | ❌ | ❌ | ❌ | ❌ |
| worldgen/density_function | ❌ | ❌ | ❌ | ❌ | | worldgen/density_function | ❌ | ❌ | ❌ | ❌ |

View File

@ -1,10 +1,13 @@
use std::{ffi::OsStr, fs::read_to_string, path::PathBuf}; use std::{ffi::OsStr, fs::read_to_string, path::PathBuf};
use serde::{Deserialize, Serialize};
use thiserror::Error; use thiserror::Error;
use crate::{ use crate::{
TagOrIdentifier,
identifier::Identifier, identifier::Identifier,
registry::{Registries, Registry}, registry::{Registries, Registry, RegistryError},
tag::Tag,
}; };
pub struct Datapack { pub struct Datapack {
@ -35,7 +38,7 @@ impl Datapack {
Ok(()) Ok(())
} }
fn load_registry<T: serde::de::DeserializeOwned>( fn load_registry<T: serde::de::DeserializeOwned + Clone>(
&self, &self,
registry: &mut Registry<T>, registry: &mut Registry<T>,
path: &str, path: &str,
@ -55,26 +58,85 @@ impl Datapack {
.and_then(OsStr::to_str) .and_then(OsStr::to_str)
.ok_or(DatapackError::Utf8)?; .ok_or(DatapackError::Utf8)?;
let path = namespace.join(path); let path = namespace.join(path);
if let Ok(files) = std::fs::read_dir(path) { let files = std::fs::read_dir(path)?;
for file in files { for file in files {
let file = file?.path(); println!("Loading file: {:?}", file);
let name = file let file = file?.path();
.file_stem() let name = file
.and_then(OsStr::to_str) .file_stem()
.ok_or(DatapackError::Utf8)?; .and_then(OsStr::to_str)
let identfiier = Identifier::new_str(namespace_str, name); .ok_or(DatapackError::Utf8)?;
let identfiier = Identifier::new_str(namespace_str, name);
let data: T = serde_json::from_str(&read_to_string(file)?)?; let data: T = serde_json::from_str(&read_to_string(file)?)?;
registry.insert(identfiier, data); registry.register(identfiier, data)?;
}
} }
} }
self.load_registry_tags(registry, path)?;
Ok(())
}
fn load_registry_tags<T: Clone>(
&self,
registry: &mut Registry<T>,
path: &str,
) -> Result<(), DatapackError> {
let namespaces: Vec<_> = self
.path
.join("data")
.read_dir()
.unwrap()
.map(|f| f.unwrap().path())
.filter(|p| p.is_dir())
.collect();
for namespace in namespaces {
let namespace_str = namespace
.file_name()
.and_then(OsStr::to_str)
.ok_or(DatapackError::Utf8)?;
let path = namespace.join("tags").join(path);
load_registry_tags_in_directory(registry, namespace_str, path)?;
}
Ok(()) Ok(())
} }
} }
fn load_registry_tags_in_directory<T: Clone>(
registry: &mut Registry<T>,
namespace: &str,
dir: PathBuf,
) -> Result<(), DatapackError> {
if let Ok(files) = std::fs::read_dir(dir) {
for file in files {
let file = file?.path();
if file.is_dir() {
load_registry_tags_in_directory(registry, namespace, file)?;
} else {
let name = file
.file_stem()
.and_then(OsStr::to_str)
.ok_or(DatapackError::Utf8)?;
let tag = Tag::new_str(namespace, name);
let data: RegistryTagData = serde_json::from_str(&read_to_string(file)?)?;
if data.replace {
registry.replace_tag(tag, data.values);
} else {
registry.register_tag(tag, data.values);
}
}
}
}
Ok(())
}
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum DatapackError { pub enum DatapackError {
#[error("IO error while reading datapack: {0}")] #[error("IO error while reading datapack: {0}")]
@ -83,4 +145,13 @@ pub enum DatapackError {
Json(#[from] serde_json::Error), Json(#[from] serde_json::Error),
#[error("Invalid UTF-8 in datapack")] #[error("Invalid UTF-8 in datapack")]
Utf8, Utf8,
#[error("Error while registering item in datapack: {0}")]
Registry(#[from] RegistryError),
}
#[derive(Debug, Serialize, Deserialize)]
struct RegistryTagData {
#[serde(default)]
replace: bool,
values: Vec<TagOrIdentifier>,
} }

View File

@ -1,3 +1,5 @@
use std::fmt;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use thiserror::Error; use thiserror::Error;
@ -42,6 +44,12 @@ impl Identifier {
} }
} }
impl fmt::Display for Identifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.namespace, self.path)
}
}
impl From<Identifier> for String { impl From<Identifier> for String {
fn from(value: Identifier) -> Self { fn from(value: Identifier) -> Self {
(&value).into() (&value).into()

View File

@ -1,4 +1,6 @@
use identifier::Identifier;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tag::Tag;
pub mod block_state; pub mod block_state;
pub mod color; pub mod color;
@ -23,3 +25,10 @@ pub enum OneOrMany<T> {
One(T), One(T),
Many(Vec<T>), Many(Vec<T>),
} }
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
#[serde(untagged)]
pub enum TagOrIdentifier {
Tag(Tag),
Identifier(Identifier),
}

View File

@ -5,12 +5,13 @@ use chat_type::ChatType;
use damage_type::DamageType; use damage_type::DamageType;
use dimension_type::DimensionType; use dimension_type::DimensionType;
use painting_variant::PaintingVariant; use painting_variant::PaintingVariant;
use thiserror::Error;
use trim_material::TrimMaterial; use trim_material::TrimMaterial;
use trim_pattern::TrimPattern; use trim_pattern::TrimPattern;
use wolf_variant::WolfVariant; use wolf_variant::WolfVariant;
use worldgen::biome::Biome; use worldgen::biome::Biome;
use crate::identifier::Identifier; use crate::{TagOrIdentifier, identifier::Identifier, tag::Tag};
pub mod banner_pattern; pub mod banner_pattern;
pub mod chat_type; pub mod chat_type;
@ -22,8 +23,6 @@ pub mod trim_pattern;
pub mod wolf_variant; pub mod wolf_variant;
pub mod worldgen; pub mod worldgen;
pub type Registry<T> = HashMap<Identifier, T>;
#[derive(Default)] #[derive(Default)]
pub struct Registries { pub struct Registries {
// advancement // advancement
@ -71,3 +70,89 @@ pub struct WorldgenRegistries {
// flat_level_generator_preset // flat_level_generator_preset
// multi_noise_biome_source_parameter_list // multi_noise_biome_source_parameter_list
} }
#[derive(Debug, Error)]
pub enum RegistryError {
#[error("Duplicate key: {0}")]
DuplicateKey(Identifier),
}
pub struct Registry<T>
where
T: Clone,
{
// Int id -> T
by_id: Vec<T>,
// Int id -> Identifier
id_to_key: Vec<Identifier>,
// Identifier -> Int Id
to_id: HashMap<Identifier, usize>,
// Identifier -> T
by_key: HashMap<Identifier, T>,
tags: HashMap<Tag, Vec<TagOrIdentifier>>,
}
impl<T> Default for Registry<T>
where
T: Clone,
{
fn default() -> Self {
Self {
by_id: Default::default(),
id_to_key: Default::default(),
to_id: Default::default(),
by_key: Default::default(),
tags: Default::default(),
}
}
}
impl<T> Registry<T>
where
T: Clone,
{
pub fn register(&mut self, key: Identifier, value: T) -> Result<(), RegistryError> {
if self.to_id.contains_key(&key) {
return Err(RegistryError::DuplicateKey(key));
}
let id = self.by_id.len();
self.by_id.push(value.clone());
self.id_to_key.push(key.clone());
self.to_id.insert(key.clone(), id);
self.by_key.insert(key, value);
Ok(())
}
pub fn register_tag(&mut self, tag: Tag, entries: Vec<TagOrIdentifier>) {
match self.tags.get_mut(&tag) {
Some(values) => {
values.extend(entries);
}
None => {
self.tags.insert(tag, entries);
}
}
}
pub fn replace_tag(&mut self, tag: Tag, entries: Vec<TagOrIdentifier>) {
self.tags.insert(tag, entries);
}
pub fn len(&self) -> usize {
self.by_id.len()
}
pub fn tags_len(&self) -> usize {
self.tags.len()
}
pub fn is_empty(&self) -> bool {
self.by_id.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (&Identifier, &T)> {
self.id_to_key.iter().zip(self.by_id.iter())
}
}

View File

@ -7,8 +7,9 @@ use crate::{identifier::Identifier, text_component::TextComponent};
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct TrimMaterial { pub struct TrimMaterial {
pub asset_name: String, pub asset_name: String,
pub description: TextComponent,
pub ingredient: Identifier, pub ingredient: Identifier,
#[serde(default)]
pub item_model_index: f32,
pub override_armor_assets: Option<HashMap<Identifier, String>>, pub override_armor_assets: Option<HashMap<Identifier, String>>,
// NOTE: Wiki says item_model_index exists as option, but can't find it in the vanilla pack pub description: TextComponent,
} }

View File

@ -5,8 +5,8 @@ use crate::{identifier::Identifier, text_component::TextComponent};
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct TrimPattern { pub struct TrimPattern {
pub asset_id: Identifier, pub asset_id: Identifier,
pub description: TextComponent,
pub template_item: Identifier, pub template_item: Identifier,
pub description: TextComponent,
#[serde(default)] #[serde(default)]
pub decal: bool, pub decal: bool,
} }

View File

@ -20,7 +20,7 @@ pub struct Biome {
pub spawn_costs: HashMap<Identifier, BiomeSpawnCost>, pub spawn_costs: HashMap<Identifier, BiomeSpawnCost>,
} }
#[derive(Debug, Serialize, Deserialize, Clone, Default)] #[derive(Debug, Serialize, Deserialize, Clone, Copy, Default)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum BiomeTemperatureModifier { pub enum BiomeTemperatureModifier {
#[default] #[default]
@ -51,7 +51,7 @@ pub struct SoundEvent {
pub range: Option<f32>, pub range: Option<f32>,
} }
#[derive(Debug, Serialize, Deserialize, Clone, Default)] #[derive(Debug, Serialize, Deserialize, Clone, Copy, Default)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum BiomeGrassColorModifier { pub enum BiomeGrassColorModifier {
#[default] #[default]

View File

@ -4,10 +4,28 @@ use thiserror::Error;
use crate::identifier::{Identifier, IdentifierError}; use crate::identifier::{Identifier, IdentifierError};
// TODO: Needs to serialize/deserialize as the identifier with a hash in front. // TODO: Needs to serialize/deserialize as the identifier with a hash in front.
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
#[serde(try_from = "&str", into = "String")] #[serde(try_from = "&str", into = "String")]
pub struct Tag(Identifier); pub struct Tag(Identifier);
impl Tag {
pub fn new(namespace: String, path: String) -> Self {
Self(Identifier::new(namespace, path))
}
pub fn minecraft(path: String) -> Self {
Self::new("minecraft".to_string(), path)
}
pub fn new_str(namespace: &str, path: &str) -> Self {
Self::new(namespace.to_string(), path.to_string())
}
pub fn minecraft_str(path: &str) -> Self {
Self::minecraft(path.to_string())
}
}
impl TryFrom<&str> for Tag { impl TryFrom<&str> for Tag {
type Error = TagError; type Error = TagError;
fn try_from(value: &str) -> Result<Self, Self::Error> { fn try_from(value: &str) -> Result<Self, Self::Error> {

View File

@ -1,10 +1,20 @@
use potato_data::{ use potato_data::{
Either,
identifier::Identifier, identifier::Identifier,
registry::{ registry::{
banner_pattern::BannerPattern, chat_type::ChatType, damage_type::DamageType, self,
dimension_type::DimensionType, painting_variant::PaintingVariant, banner_pattern::BannerPattern,
trim_material::TrimMaterial, trim_pattern::TrimPattern, wolf_variant::WolfVariant, chat_type::ChatType,
worldgen::biome::Biome, damage_type::DamageType,
dimension_type::DimensionType,
painting_variant::PaintingVariant,
trim_material::TrimMaterial,
trim_pattern::TrimPattern,
wolf_variant::WolfVariant,
worldgen::biome::{
BiomeAdditionsSound, BiomeGrassColorModifier, BiomeMoodSound, BiomeMusicEntry,
BiomeParticle, BiomeTemperatureModifier, SoundEvent,
},
}, },
}; };
use potato_protocol_derive::{Packet, PacketEncodable}; use potato_protocol_derive::{Packet, PacketEncodable};
@ -55,10 +65,65 @@ impl_from_registry!(
TrimMaterial, TrimMaterial,
TrimPattern, TrimPattern,
BannerPattern, BannerPattern,
Biome,
ChatType, ChatType,
DamageType, DamageType,
DimensionType, DimensionType,
WolfVariant, WolfVariant,
PaintingVariant PaintingVariant
); );
#[derive(Debug, Serialize, Deserialize)]
pub struct Biome {
has_precipitation: bool,
temperature: f32,
temperature_modifier: BiomeTemperatureModifier,
downfall: f32,
effects: BiomeEffects,
}
impl From<&registry::worldgen::biome::Biome> for RegistryData {
fn from(value: &registry::worldgen::biome::Biome) -> Self {
RegistryData::Biome(Box::new(Biome {
has_precipitation: value.has_precipitation,
temperature: value.temperature,
temperature_modifier: value.temperature_modifier,
downfall: value.downfall,
effects: (&value.effects).into(),
}))
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct BiomeEffects {
fog_color: i32,
water_color: i32,
water_fog_color: i32,
sky_color: i32,
foliage_color: Option<i32>,
grass_color: Option<i32>,
grass_color_modifier: BiomeGrassColorModifier,
particle: Option<BiomeParticle>,
ambient_sound: Option<Either<Identifier, SoundEvent>>,
mood_sound: Option<BiomeMoodSound>,
additions_sound: Option<BiomeAdditionsSound>,
music: Option<Vec<BiomeMusicEntry>>,
}
impl From<&registry::worldgen::biome::BiomeEffects> for BiomeEffects {
fn from(value: &registry::worldgen::biome::BiomeEffects) -> Self {
BiomeEffects {
fog_color: value.fog_color,
water_color: value.water_color,
water_fog_color: value.water_fog_color,
sky_color: value.sky_color,
foliage_color: value.foliage_color,
grass_color: value.grass_color,
grass_color_modifier: value.grass_color_modifier,
particle: value.particle.clone(),
ambient_sound: value.ambient_sound.clone(),
mood_sound: value.mood_sound.clone(),
additions_sound: value.additions_sound.clone(),
music: value.music.clone(),
}
}
}

View File

@ -437,7 +437,7 @@ impl Client {
Ok(()) Ok(())
} }
async fn send_registry<T>( async fn send_registry<T: Clone>(
&self, &self,
identifier: Identifier, identifier: Identifier,
registry: &Registry<T>, registry: &Registry<T>,

View File

@ -6,6 +6,13 @@ pub struct Server {
pub registries: Arc<Registries>, pub registries: Arc<Registries>,
} }
macro_rules! print_registry_loaded {
($registry:expr, $name:literal) => {
println!("Loaded {} {}s", $registry.len(), $name);
println!("Loaded {} {} tags", $registry.tags_len(), $name);
};
}
impl Server { impl Server {
#[allow(clippy::new_without_default)] #[allow(clippy::new_without_default)]
pub fn new() -> Server { pub fn new() -> Server {
@ -24,20 +31,15 @@ impl Server {
.for_each(|pack| pack.load(&mut registries).expect("Failed to load datapack")); .for_each(|pack| pack.load(&mut registries).expect("Failed to load datapack"));
println!("Finished loading datapacks"); println!("Finished loading datapacks");
println!("Loaded {} trim materials", registries.trim_materials.len()); print_registry_loaded!(registries.trim_materials, "trim material");
println!("Loaded {} trim patterns", registries.trim_patterns.len()); print_registry_loaded!(registries.trim_patterns, "trim pattern");
println!("Loaded {} chat types", registries.chat_types.len()); print_registry_loaded!(registries.banner_patterns, "banner pattern");
println!( print_registry_loaded!(registries.chat_types, "chat type");
"Loaded {} dimension types", print_registry_loaded!(registries.dimension_types, "dimension type");
registries.dimension_types.len() print_registry_loaded!(registries.painting_variants, "painting variant");
); print_registry_loaded!(registries.wolf_variants, "wolf variant");
println!( print_registry_loaded!(registries.damage_types, "damage_type");
"Loaded {} painting variants", print_registry_loaded!(registries.worldgen.biomes, "biome");
registries.painting_variants.len()
);
println!("Loaded {} wolf variants", registries.wolf_variants.len());
println!("Loaded {} damage types", registries.damage_types.len());
println!("Loaded {} biomes", registries.worldgen.biomes.len());
Server { Server {
registries: Arc::new(registries), registries: Arc::new(registries),