Load a bunch of registries from datapacks, logging in is broken by this for now
This commit is contained in:
parent
d133126f7f
commit
6ffad8ff20
35 changed files with 1230 additions and 446 deletions
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -106,7 +106,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastnbt"
|
name = "fastnbt"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
source = "git+https://github.com/owengage/fastnbt.git#e2a5d8a7001d4f074ae99fd21bb485667934baeb"
|
source = "git+https://github.com/CheAle14/fastnbt.git#91ce891ca8aa3048b9e1d08ae0fcc51a7b714451"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"cesu8",
|
"cesu8",
|
||||||
|
@ -223,6 +223,7 @@ name = "potato"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"potato-data",
|
||||||
"potato-protocol",
|
"potato-protocol",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -231,6 +232,15 @@ dependencies = [
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "potato-data"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "potato-protocol"
|
name = "potato-protocol"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -238,6 +248,7 @@ dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes",
|
"bytes",
|
||||||
"fastnbt",
|
"fastnbt",
|
||||||
|
"potato-data",
|
||||||
"potato-protocol-derive",
|
"potato-protocol-derive",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = ["potato", "potato-protocol", "potato-protocol-derive"]
|
members = ["potato", "potato-data", "potato-protocol", "potato-protocol-derive"]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
9
potato-data/Cargo.toml
Normal file
9
potato-data/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "potato-data"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
thiserror.workspace = true
|
11
potato-data/src/block_state.rs
Normal file
11
potato-data/src/block_state.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::identifier::Identifier;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct BlockState {
|
||||||
|
pub name: Identifier,
|
||||||
|
pub properties: HashMap<String, String>,
|
||||||
|
}
|
35
potato-data/src/color.rs
Normal file
35
potato-data/src/color.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
// TODO: Serialize as array of [red, green, blue] in NBT
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
|
pub struct RGBf32 {
|
||||||
|
red: f32,
|
||||||
|
green: f32,
|
||||||
|
blue: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Serialize as array of [red, green, blue, alpha] in NBT
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
|
pub struct RGBAf32 {
|
||||||
|
red: f32,
|
||||||
|
green: f32,
|
||||||
|
blue: f32,
|
||||||
|
alpha: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Serialize as u32 of red<<16+green<<8+blue in NBT
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
|
pub struct RGBu8 {
|
||||||
|
red: u8,
|
||||||
|
green: u8,
|
||||||
|
blue: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Serialize as u32 of alpha<<24+red<<16+green<<8+blue in NBT
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
|
pub struct ARGBu8 {
|
||||||
|
alpha: u8,
|
||||||
|
red: u8,
|
||||||
|
green: u8,
|
||||||
|
blue: u8,
|
||||||
|
}
|
86
potato-data/src/datapack.rs
Normal file
86
potato-data/src/datapack.rs
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
use std::{ffi::OsStr, fs::read_to_string, path::PathBuf};
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
identifier::Identifier,
|
||||||
|
registry::{Registries, Registry},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Datapack {
|
||||||
|
pub name: String,
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Datapack {
|
||||||
|
pub fn new(path: PathBuf) -> Self {
|
||||||
|
// TODO: Load name (and other info) from pack.mcmeta
|
||||||
|
let name = path.file_name().unwrap().to_str().unwrap().to_string();
|
||||||
|
|
||||||
|
Self { name, path }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(&self, registries: &mut Registries) -> Result<(), DatapackError> {
|
||||||
|
println!("Loading datapack: {}", self.name);
|
||||||
|
self.load_registry(&mut registries.trim_materials, "trim_material")?;
|
||||||
|
self.load_registry(&mut registries.trim_patterns, "trim_pattern")?;
|
||||||
|
self.load_registry(&mut registries.banner_patterns, "banner_pattern")?;
|
||||||
|
self.load_registry(&mut registries.worldgen.biomes, "worldgen/biome")?;
|
||||||
|
self.load_registry(&mut registries.damage_types, "damage_type")?;
|
||||||
|
self.load_registry(&mut registries.dimension_types, "dimension_type")?;
|
||||||
|
self.load_registry(&mut registries.wolf_variants, "wolf_variant")?;
|
||||||
|
self.load_registry(&mut registries.painting_variants, "painting_variant")?;
|
||||||
|
self.load_registry(&mut registries.chat_types, "chat_type")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_registry<T: serde::de::DeserializeOwned>(
|
||||||
|
&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(path);
|
||||||
|
if let Ok(files) = std::fs::read_dir(path) {
|
||||||
|
for file in files {
|
||||||
|
let file = file.unwrap().path();
|
||||||
|
let name = file
|
||||||
|
.file_stem()
|
||||||
|
.and_then(OsStr::to_str)
|
||||||
|
.ok_or(DatapackError::Utf8)?;
|
||||||
|
let identfiier = Identifier::new_str(namespace_str, name);
|
||||||
|
|
||||||
|
let data: T = serde_json::from_str(&read_to_string(file)?)?;
|
||||||
|
|
||||||
|
registry.insert(identfiier, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum DatapackError {
|
||||||
|
#[error("IO error while reading datapack: {0}")]
|
||||||
|
Io(#[from] std::io::Error),
|
||||||
|
#[error("Error while parsing JSON in datapack: {0}")]
|
||||||
|
Json(#[from] serde_json::Error),
|
||||||
|
#[error("Invalid UTF-8 in datapack")]
|
||||||
|
Utf8,
|
||||||
|
}
|
71
potato-data/src/identifier.rs
Normal file
71
potato-data/src/identifier.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
// TODO: Custom serializer/deserializer for format namespace:path as string
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
#[serde(try_from = "&str", into = "String")]
|
||||||
|
pub struct Identifier {
|
||||||
|
pub namespace: String,
|
||||||
|
pub path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Identifier {
|
||||||
|
pub fn new(namespace: String, path: String) -> Self {
|
||||||
|
// TODO: Validate namespace and path
|
||||||
|
Self { 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())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_raw_str(raw: &str) -> Result<Self, IdentifierError> {
|
||||||
|
let mut parts = raw.split(":");
|
||||||
|
Ok(Self::new(
|
||||||
|
parts
|
||||||
|
.next()
|
||||||
|
.ok_or(IdentifierError::InvalidNamespace)?
|
||||||
|
.to_string(),
|
||||||
|
parts
|
||||||
|
.next()
|
||||||
|
.ok_or(IdentifierError::InvalidPath)?
|
||||||
|
.to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Identifier> for String {
|
||||||
|
fn from(value: Identifier) -> Self {
|
||||||
|
(&value).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Identifier> for String {
|
||||||
|
fn from(value: &Identifier) -> Self {
|
||||||
|
format!("{}:{}", value.namespace, value.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for Identifier {
|
||||||
|
type Error = IdentifierError;
|
||||||
|
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
Identifier::from_raw_str(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum IdentifierError {
|
||||||
|
#[error("Invalid namespace")]
|
||||||
|
InvalidNamespace,
|
||||||
|
#[error("Invalid path")]
|
||||||
|
InvalidPath,
|
||||||
|
}
|
12
potato-data/src/item_stack.rs
Normal file
12
potato-data/src/item_stack.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::identifier::Identifier;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct ItemStack {
|
||||||
|
id: Identifier,
|
||||||
|
components: Vec<ItemComponent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||||
|
pub enum ItemComponent {}
|
25
potato-data/src/lib.rs
Normal file
25
potato-data/src/lib.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub mod block_state;
|
||||||
|
pub mod color;
|
||||||
|
pub mod datapack;
|
||||||
|
pub mod identifier;
|
||||||
|
pub mod item_stack;
|
||||||
|
pub mod particle;
|
||||||
|
pub mod registry;
|
||||||
|
pub mod tag;
|
||||||
|
pub mod text_component;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum Either<A, B> {
|
||||||
|
Left(A),
|
||||||
|
Right(B),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum OneOrMany<T> {
|
||||||
|
One(T),
|
||||||
|
Many(Vec<T>),
|
||||||
|
}
|
268
potato-data/src/particle.rs
Normal file
268
potato-data/src/particle.rs
Normal file
|
@ -0,0 +1,268 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
Either,
|
||||||
|
block_state::BlockState,
|
||||||
|
color::{ARGBu8, RGBAf32, RGBf32, RGBu8},
|
||||||
|
identifier::Identifier,
|
||||||
|
item_stack::ItemStack,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum Particle {
|
||||||
|
#[serde(rename = "minecraft:angry_villager")]
|
||||||
|
AngryVillager,
|
||||||
|
#[serde(rename = "minecraft:ash")]
|
||||||
|
Ash,
|
||||||
|
Block {
|
||||||
|
block_state: Either<Identifier, BlockState>,
|
||||||
|
},
|
||||||
|
#[serde(rename = "minecraft:block_crumble")]
|
||||||
|
BlockCrumble {
|
||||||
|
block_state: Either<Identifier, BlockState>,
|
||||||
|
},
|
||||||
|
BlockMarker {
|
||||||
|
block_state: Either<Identifier, BlockState>,
|
||||||
|
},
|
||||||
|
#[serde(rename = "minecraft:bubble")]
|
||||||
|
Bubble,
|
||||||
|
#[serde(rename = "minecraft:bubble_column_up")]
|
||||||
|
BubbleColumnUp,
|
||||||
|
#[serde(rename = "minecraft:bubble_pop")]
|
||||||
|
BuublePop,
|
||||||
|
#[serde(rename = "minecraft:campfire_cosy_smoke")]
|
||||||
|
CampfireCosySmoke,
|
||||||
|
#[serde(rename = "minecraft:campfire_signal_smoke")]
|
||||||
|
CampfireSignalSmoke,
|
||||||
|
#[serde(rename = "minecraft:cherry_leaves")]
|
||||||
|
CherryLeaves,
|
||||||
|
#[serde(rename = "minecraft:cloud")]
|
||||||
|
Cloud,
|
||||||
|
#[serde(rename = "minecraft:composter")]
|
||||||
|
Composter,
|
||||||
|
#[serde(rename = "minecraft:crimson_spore")]
|
||||||
|
CrimsonSpore,
|
||||||
|
#[serde(rename = "minecraft:crit")]
|
||||||
|
Crit,
|
||||||
|
#[serde(rename = "minecraft:current_down")]
|
||||||
|
CurrentDown,
|
||||||
|
#[serde(rename = "minecraft:damage_indicator")]
|
||||||
|
DamageIndicator,
|
||||||
|
#[serde(rename = "minecraft:dolphin")]
|
||||||
|
Dolphin,
|
||||||
|
#[serde(rename = "minecraft:dragon_breath")]
|
||||||
|
DragonBreath,
|
||||||
|
#[serde(rename = "minecraft:dripping_dripstone_lava")]
|
||||||
|
DrippingDripstoneLava,
|
||||||
|
#[serde(rename = "minecraft:dripping_dripstone_water")]
|
||||||
|
DrippingDripstoneWater,
|
||||||
|
#[serde(rename = "minecraft:dripping_honey")]
|
||||||
|
DrippingHoney,
|
||||||
|
#[serde(rename = "minecraft:dripping_lava")]
|
||||||
|
DrippingLava,
|
||||||
|
#[serde(rename = "minecraft:dripping_obsidian_tear")]
|
||||||
|
DrippingObsidianTear,
|
||||||
|
#[serde(rename = "minecraft:dripping_water")]
|
||||||
|
DrippingWater,
|
||||||
|
Dust {
|
||||||
|
color: RGBf32,
|
||||||
|
scale: f32,
|
||||||
|
},
|
||||||
|
DustColorTransition {
|
||||||
|
from_color: RGBf32,
|
||||||
|
to_color: RGBf32,
|
||||||
|
scale: f32,
|
||||||
|
},
|
||||||
|
DustPillar {
|
||||||
|
block_state: Either<Identifier, BlockState>,
|
||||||
|
},
|
||||||
|
#[serde(rename = "minecraft:dust_plume")]
|
||||||
|
DustPlume,
|
||||||
|
#[serde(rename = "minecraft:effect")]
|
||||||
|
Effect,
|
||||||
|
#[serde(rename = "minecraft:egg_crack")]
|
||||||
|
EggCrack,
|
||||||
|
#[serde(rename = "minecraft:elder_guardian")]
|
||||||
|
ElderGuardian,
|
||||||
|
#[serde(rename = "minecraft:electric_spark")]
|
||||||
|
ElectricSpark,
|
||||||
|
#[serde(rename = "minecraft:enchant")]
|
||||||
|
Enchant,
|
||||||
|
#[serde(rename = "minecraft:enchanted_hit")]
|
||||||
|
EnchantedHit,
|
||||||
|
#[serde(rename = "minecraft:end_rod")]
|
||||||
|
EndRod,
|
||||||
|
EntityEffect {
|
||||||
|
color: Either<RGBAf32, ARGBu8>,
|
||||||
|
},
|
||||||
|
#[serde(rename = "minecraft:explosion")]
|
||||||
|
Explosion,
|
||||||
|
#[serde(rename = "minecraft:explosion_emitter")]
|
||||||
|
ExplosionEmitter,
|
||||||
|
#[serde(rename = "minecraft:falling_dripstone_lava")]
|
||||||
|
FallingDripstoneLava,
|
||||||
|
#[serde(rename = "minecraft:falling_dripstone_water")]
|
||||||
|
FallingDrpistoneWater,
|
||||||
|
FallingDust {
|
||||||
|
block_state: Either<Identifier, BlockState>,
|
||||||
|
},
|
||||||
|
#[serde(rename = "minecraft:falling_honey")]
|
||||||
|
FallingHoney,
|
||||||
|
#[serde(rename = "minecraft:falling_lava")]
|
||||||
|
FallingLava,
|
||||||
|
#[serde(rename = "minecraft:falling_nectar")]
|
||||||
|
FallingNectar,
|
||||||
|
#[serde(rename = "minecraft:falling_obsidian_tear")]
|
||||||
|
FallingObsidianTear,
|
||||||
|
#[serde(rename = "minecraft:falling_spore_blossom")]
|
||||||
|
FallingSporeBlossom,
|
||||||
|
#[serde(rename = "minecraft:falling_water")]
|
||||||
|
FallingWater,
|
||||||
|
// Firefly, (Upcoming: 1.21.5)
|
||||||
|
#[serde(rename = "minecraft:firework")]
|
||||||
|
Firework,
|
||||||
|
#[serde(rename = "minecraft:fishing")]
|
||||||
|
Fishing,
|
||||||
|
#[serde(rename = "minecraft:flame")]
|
||||||
|
Flame,
|
||||||
|
#[serde(rename = "minecraft:flash")]
|
||||||
|
Flash,
|
||||||
|
#[serde(rename = "minecraft:glow")]
|
||||||
|
Glow,
|
||||||
|
#[serde(rename = "minecraft:glow_squid_ink")]
|
||||||
|
GlowSquidInk,
|
||||||
|
#[serde(rename = "minecraft:gust")]
|
||||||
|
Gust,
|
||||||
|
#[serde(rename = "minecraft:gust_emitter")]
|
||||||
|
GustEmitter,
|
||||||
|
#[serde(rename = "minecraft:happy_villager")]
|
||||||
|
HappyVillager,
|
||||||
|
#[serde(rename = "minecraft:heart")]
|
||||||
|
Heart,
|
||||||
|
#[serde(rename = "minecraft:infested")]
|
||||||
|
Infested,
|
||||||
|
#[serde(rename = "minecraft:instant_effect")]
|
||||||
|
InstantEffect,
|
||||||
|
Item {
|
||||||
|
item: Either<Identifier, ItemStack>,
|
||||||
|
},
|
||||||
|
#[serde(rename = "minecraft:item_cobweb")]
|
||||||
|
ItemCobweb,
|
||||||
|
#[serde(rename = "minecraft:item_slime")]
|
||||||
|
ItemSlime,
|
||||||
|
#[serde(rename = "minecraft:item_snowball")]
|
||||||
|
ItemSnowball,
|
||||||
|
#[serde(rename = "minecraft:landing_honey")]
|
||||||
|
LandingHoney,
|
||||||
|
#[serde(rename = "minecraft:landing_lava")]
|
||||||
|
LandingLava,
|
||||||
|
#[serde(rename = "minecraft:landing_obsidian_tear")]
|
||||||
|
LandingObsidianTear,
|
||||||
|
#[serde(rename = "minecraft:large_smoke")]
|
||||||
|
LargeSmoke,
|
||||||
|
#[serde(rename = "minecraft:lava")]
|
||||||
|
Lava,
|
||||||
|
#[serde(rename = "minecraft:mycelium")]
|
||||||
|
Mycelium,
|
||||||
|
#[serde(rename = "minecraft:nautilus")]
|
||||||
|
Nautlius,
|
||||||
|
#[serde(rename = "minecraft:note")]
|
||||||
|
Note,
|
||||||
|
#[serde(rename = "minecraft:ominous_spawning")]
|
||||||
|
OminousSpawning,
|
||||||
|
#[serde(rename = "minecraft:pale_oak_leaves")]
|
||||||
|
PaleOakLeaves,
|
||||||
|
#[serde(rename = "minecraft:poof")]
|
||||||
|
Poof,
|
||||||
|
#[serde(rename = "minecraft:portal")]
|
||||||
|
Portal,
|
||||||
|
#[serde(rename = "minecraft:raid_omen")]
|
||||||
|
RaidOmen,
|
||||||
|
#[serde(rename = "minecraft:rain")]
|
||||||
|
Rain,
|
||||||
|
#[serde(rename = "minecraft:reverse_portal")]
|
||||||
|
ReversePortal,
|
||||||
|
#[serde(rename = "minecraft:scrape")]
|
||||||
|
Scrape,
|
||||||
|
SculkCharge {
|
||||||
|
roll: f32,
|
||||||
|
},
|
||||||
|
#[serde(rename = "minecraft:sculk_charge_pop")]
|
||||||
|
SculkChargePop,
|
||||||
|
#[serde(rename = "minecraft:sculk_soul")]
|
||||||
|
SculkSoul,
|
||||||
|
Shriek {
|
||||||
|
delay: i32,
|
||||||
|
},
|
||||||
|
#[serde(rename = "minecraft:small_flame")]
|
||||||
|
SmallFlame,
|
||||||
|
#[serde(rename = "minecraft:small_gust")]
|
||||||
|
SmallGust,
|
||||||
|
#[serde(rename = "minecraft:smoke")]
|
||||||
|
Smoke,
|
||||||
|
#[serde(rename = "minecraft:sneeze")]
|
||||||
|
Sneeze,
|
||||||
|
#[serde(rename = "minecraft:snowflake")]
|
||||||
|
Snowflake,
|
||||||
|
#[serde(rename = "minecraft:sonic_boom")]
|
||||||
|
SonicBoom,
|
||||||
|
#[serde(rename = "minecraft:soul")]
|
||||||
|
Soul,
|
||||||
|
#[serde(rename = "minecraft:soul_fire_flame")]
|
||||||
|
SoulFireFlame,
|
||||||
|
#[serde(rename = "minecraft:spit")]
|
||||||
|
Spit,
|
||||||
|
#[serde(rename = "minecraft:splash")]
|
||||||
|
Splash,
|
||||||
|
#[serde(rename = "minecraft:spore_blossom_air")]
|
||||||
|
SporeBlossomAir,
|
||||||
|
#[serde(rename = "minecraft:squid_ink")]
|
||||||
|
SquidInk,
|
||||||
|
#[serde(rename = "minecraft:sweep_attack")]
|
||||||
|
SweepAttack,
|
||||||
|
#[serde(rename = "minecraft:totem_of_undying")]
|
||||||
|
TotemOfUndying,
|
||||||
|
Trail {
|
||||||
|
target: (f32, f32, f32),
|
||||||
|
colr: RGBu8,
|
||||||
|
},
|
||||||
|
#[serde(rename = "minecraft:trail_omen")]
|
||||||
|
TrailOmen,
|
||||||
|
#[serde(rename = "minecraft:trail_spawner_detection")]
|
||||||
|
TrailSpawnerDetection,
|
||||||
|
#[serde(rename = "minecraft:underwater")]
|
||||||
|
Underwater,
|
||||||
|
#[serde(rename = "minecraft:vault_connection")]
|
||||||
|
VaultConnection,
|
||||||
|
Vibration {
|
||||||
|
destination: PositionSource,
|
||||||
|
arrival_in_ticks: i32,
|
||||||
|
},
|
||||||
|
#[serde(rename = "minecraft:warped_spore")]
|
||||||
|
WarpedSpore,
|
||||||
|
#[serde(rename = "minecraft:wax_off")]
|
||||||
|
WaxOff,
|
||||||
|
#[serde(rename = "minecraft:wax_on")]
|
||||||
|
WaxOn,
|
||||||
|
#[serde(rename = "minecraft:white_ash")]
|
||||||
|
WhiteAsh,
|
||||||
|
#[serde(rename = "minecraft:white_smoke")]
|
||||||
|
WhiteSmoke,
|
||||||
|
#[serde(rename = "minecraft:witch")]
|
||||||
|
Witch,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(tag = "type", rename_all = "snake_case")]
|
||||||
|
pub enum PositionSource {
|
||||||
|
Block {
|
||||||
|
pos: (i32, i32, i32),
|
||||||
|
},
|
||||||
|
Entity {
|
||||||
|
// source_entity stores a UUID as 4xi32
|
||||||
|
source_entity: (i32, i32, i32, i32),
|
||||||
|
// y_offset is optional and defaults to 0.0
|
||||||
|
y_offset: f32,
|
||||||
|
},
|
||||||
|
}
|
9
potato-data/src/registry/banner_pattern.rs
Normal file
9
potato-data/src/registry/banner_pattern.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::identifier::Identifier;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct BannerPattern {
|
||||||
|
pub asset_id: Identifier,
|
||||||
|
pub translation_key: String,
|
||||||
|
}
|
24
potato-data/src/registry/chat_type.rs
Normal file
24
potato-data/src/registry/chat_type.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::text_component::TextComponentStyle;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
|
pub struct ChatType {
|
||||||
|
pub chat: ChatTypeInner,
|
||||||
|
pub narration: ChatTypeInner,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum ChatTypeParameter {
|
||||||
|
Sender,
|
||||||
|
Target,
|
||||||
|
Content,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
|
pub struct ChatTypeInner {
|
||||||
|
pub translation_key: String,
|
||||||
|
pub parameters: Vec<ChatTypeParameter>,
|
||||||
|
pub style: Option<TextComponentStyle>,
|
||||||
|
}
|
37
potato-data/src/registry/damage_type.rs
Normal file
37
potato-data/src/registry/damage_type.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct DamageType {
|
||||||
|
message_id: String,
|
||||||
|
scaling: DamageTypeScaling,
|
||||||
|
exhaustion: f32,
|
||||||
|
effects: Option<DamageTypeEffect>,
|
||||||
|
death_message_type: Option<DamageTypeDeathMessageType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum DamageTypeScaling {
|
||||||
|
Never,
|
||||||
|
WhenCausedByLivingNonPlayer,
|
||||||
|
Always,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum DamageTypeEffect {
|
||||||
|
Hurt,
|
||||||
|
Thorns,
|
||||||
|
Drowning,
|
||||||
|
Burning,
|
||||||
|
Poking,
|
||||||
|
Freezing,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum DamageTypeDeathMessageType {
|
||||||
|
Default,
|
||||||
|
FallVariants,
|
||||||
|
IntentionalGameDesign,
|
||||||
|
}
|
74
potato-data/src/registry/dimension_type.rs
Normal file
74
potato-data/src/registry/dimension_type.rs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
use crate::Either;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct DimensionType {
|
||||||
|
pub fixed_time: Option<i64>,
|
||||||
|
pub has_skylight: bool,
|
||||||
|
pub has_ceiling: bool,
|
||||||
|
pub ultrawarm: bool,
|
||||||
|
pub natural: bool,
|
||||||
|
pub coordinate_scale: f64,
|
||||||
|
pub bed_works: bool,
|
||||||
|
pub respawn_anchor_works: bool,
|
||||||
|
pub min_y: i32,
|
||||||
|
pub height: i32,
|
||||||
|
pub logical_height: i32,
|
||||||
|
pub infiniburn: String, // TODO: This is a tag, create a type for these
|
||||||
|
pub effects: DimensionEffect,
|
||||||
|
pub ambient_light: f32,
|
||||||
|
pub piglin_safe: bool,
|
||||||
|
pub has_raids: bool,
|
||||||
|
pub monster_spawn_light_level: Either<i32, IntProvider>,
|
||||||
|
pub monster_spawn_block_light_limit: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub enum DimensionEffect {
|
||||||
|
#[serde(rename = "minecraft:overworld")]
|
||||||
|
Overworld,
|
||||||
|
#[serde(rename = "minecraft:the_nether")]
|
||||||
|
TheNether,
|
||||||
|
#[serde(rename = "minecraft:the_end")]
|
||||||
|
TheEnd,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum IntProvider {
|
||||||
|
#[serde(rename = "minecraft:constant")]
|
||||||
|
Constant { value: i32 },
|
||||||
|
#[serde(rename = "minecraft:uniform")]
|
||||||
|
Uniform {
|
||||||
|
min_inclusive: i32,
|
||||||
|
max_inclusive: i32,
|
||||||
|
},
|
||||||
|
#[serde(rename = "minecraft:biased_to_bottom")]
|
||||||
|
BiasedToBottom {
|
||||||
|
min_inclusive: i32,
|
||||||
|
max_inclusive: i32,
|
||||||
|
},
|
||||||
|
#[serde(rename = "minecraft:clamped")]
|
||||||
|
Clamped {
|
||||||
|
min_inclusive: i32,
|
||||||
|
max_inclusive: i32,
|
||||||
|
source: Box<IntProvider>,
|
||||||
|
},
|
||||||
|
#[serde(rename = "minecraft:clamped_normal")]
|
||||||
|
ClampedNormal {
|
||||||
|
mean: f32,
|
||||||
|
deviation: f32,
|
||||||
|
min_include: i32,
|
||||||
|
max_include: i32,
|
||||||
|
},
|
||||||
|
#[serde(rename = "minecraft:weighted_list")]
|
||||||
|
WeightedList {
|
||||||
|
distribution: Vec<WeightedListEntry>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct WeightedListEntry {
|
||||||
|
pub data: Box<IntProvider>,
|
||||||
|
pub weight: i32,
|
||||||
|
}
|
49
potato-data/src/registry/mod.rs
Normal file
49
potato-data/src/registry/mod.rs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use banner_pattern::BannerPattern;
|
||||||
|
use chat_type::ChatType;
|
||||||
|
use damage_type::DamageType;
|
||||||
|
use dimension_type::DimensionType;
|
||||||
|
use painting_variant::PaintingVariant;
|
||||||
|
use trim_material::TrimMaterial;
|
||||||
|
use trim_pattern::TrimPattern;
|
||||||
|
use wolf_variant::WolfVariant;
|
||||||
|
use worldgen::biome::Biome;
|
||||||
|
|
||||||
|
use crate::identifier::Identifier;
|
||||||
|
|
||||||
|
pub mod banner_pattern;
|
||||||
|
pub mod chat_type;
|
||||||
|
pub mod damage_type;
|
||||||
|
pub mod dimension_type;
|
||||||
|
pub mod painting_variant;
|
||||||
|
pub mod trim_material;
|
||||||
|
pub mod trim_pattern;
|
||||||
|
pub mod wolf_variant;
|
||||||
|
pub mod worldgen;
|
||||||
|
|
||||||
|
pub type Registry<T> = HashMap<Identifier, T>;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Registries {
|
||||||
|
pub trim_materials: Registry<TrimMaterial>,
|
||||||
|
pub trim_patterns: Registry<TrimPattern>,
|
||||||
|
pub banner_patterns: Registry<BannerPattern>,
|
||||||
|
pub worldgen: WorldgenRegistries,
|
||||||
|
pub dimension_types: Registry<DimensionType>,
|
||||||
|
pub damage_types: Registry<DamageType>,
|
||||||
|
pub painting_variants: Registry<PaintingVariant>,
|
||||||
|
pub wolf_variants: Registry<WolfVariant>,
|
||||||
|
pub chat_types: Registry<ChatType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Registries {
|
||||||
|
pub fn empty() -> Registries {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct WorldgenRegistries {
|
||||||
|
pub biomes: Registry<Biome>,
|
||||||
|
}
|
9
potato-data/src/registry/painting_variant.rs
Normal file
9
potato-data/src/registry/painting_variant.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct PaintingVariant {
|
||||||
|
pub asset_id: String,
|
||||||
|
pub height: i32,
|
||||||
|
pub width: i32,
|
||||||
|
// TODO: Missing title and author fields
|
||||||
|
}
|
14
potato-data/src/registry/trim_material.rs
Normal file
14
potato-data/src/registry/trim_material.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{identifier::Identifier, text_component::TextComponent};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct TrimMaterial {
|
||||||
|
pub asset_name: String,
|
||||||
|
pub description: TextComponent,
|
||||||
|
pub ingredient: Identifier,
|
||||||
|
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
|
||||||
|
}
|
12
potato-data/src/registry/trim_pattern.rs
Normal file
12
potato-data/src/registry/trim_pattern.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{identifier::Identifier, text_component::TextComponent};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct TrimPattern {
|
||||||
|
pub asset_id: Identifier,
|
||||||
|
pub description: TextComponent,
|
||||||
|
pub template_item: Identifier,
|
||||||
|
#[serde(default)]
|
||||||
|
pub decal: bool,
|
||||||
|
}
|
11
potato-data/src/registry/wolf_variant.rs
Normal file
11
potato-data/src/registry/wolf_variant.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{Either, OneOrMany, identifier::Identifier, tag::Tag};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct WolfVariant {
|
||||||
|
pub wild_texture: String,
|
||||||
|
pub tame_texture: String,
|
||||||
|
pub angry_texture: String,
|
||||||
|
pub biomes: OneOrMany<Either<Identifier, Tag>>,
|
||||||
|
}
|
124
potato-data/src/registry/worldgen/biome.rs
Normal file
124
potato-data/src/registry/worldgen/biome.rs
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{Either, OneOrMany, identifier::Identifier, particle::Particle, tag::Tag};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct Biome {
|
||||||
|
pub has_precipitation: bool,
|
||||||
|
pub temperature: f32,
|
||||||
|
#[serde(default)]
|
||||||
|
pub temperature_modifier: BiomeTemperatureModifier,
|
||||||
|
pub downfall: f32,
|
||||||
|
pub effects: BiomeEffects,
|
||||||
|
pub carvers: OneOrMany<Either<Identifier, Tag>>,
|
||||||
|
pub features: Vec<Vec<Either<Identifier, Tag>>>,
|
||||||
|
// TODO: Limit between 0.0 and 0.9999999
|
||||||
|
pub creature_spawn_probability: Option<f32>,
|
||||||
|
pub spawners: BiomeSpawners,
|
||||||
|
pub spawn_costs: HashMap<Identifier, BiomeSpawnCost>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum BiomeTemperatureModifier {
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
Frozen,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct BiomeEffects {
|
||||||
|
pub fog_color: i32,
|
||||||
|
pub sky_color: i32,
|
||||||
|
pub water_color: i32,
|
||||||
|
pub water_fog_color: i32,
|
||||||
|
pub foliage_color: Option<i32>,
|
||||||
|
pub grass_color: Option<i32>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub grass_color_modifier: BiomeGrassColorModifier,
|
||||||
|
pub particle: Option<BiomeParticle>,
|
||||||
|
pub ambient_sound: Option<Either<Identifier, SoundEvent>>,
|
||||||
|
pub mood_sound: Option<BiomeMoodSound>,
|
||||||
|
pub additions_sound: Option<BiomeAdditionsSound>,
|
||||||
|
pub music: Option<Vec<BiomeMusicEntry>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct SoundEvent {
|
||||||
|
pub sound_id: Identifier,
|
||||||
|
pub range: Option<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum BiomeGrassColorModifier {
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
DarkForest,
|
||||||
|
Swamp,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct BiomeParticle {
|
||||||
|
pub probability: f64,
|
||||||
|
pub options: Particle,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct BiomeMoodSound {
|
||||||
|
pub sound: Either<Identifier, SoundEvent>,
|
||||||
|
pub tick_delay: i32,
|
||||||
|
pub block_search_extent: i32,
|
||||||
|
pub offset: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct BiomeAdditionsSound {
|
||||||
|
pub sound: Either<Identifier, SoundEvent>,
|
||||||
|
pub tick_chance: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct BiomeMusicEntry {
|
||||||
|
pub data: BiomeMusicData,
|
||||||
|
pub weight: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct BiomeMusicData {
|
||||||
|
pub sound: Either<Identifier, SoundEvent>,
|
||||||
|
pub min_delay: i32,
|
||||||
|
pub max_delay: i32,
|
||||||
|
pub replace_current_music: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct BiomeSpawners {
|
||||||
|
pub monster: Option<Vec<BiomeSpawner>>,
|
||||||
|
pub creature: Option<Vec<BiomeSpawner>>,
|
||||||
|
pub ambient: Option<Vec<BiomeSpawner>>,
|
||||||
|
pub water_creature: Option<Vec<BiomeSpawner>>,
|
||||||
|
pub underground_water_creature: Option<Vec<BiomeSpawner>>,
|
||||||
|
pub water_ambient: Option<Vec<BiomeSpawner>>,
|
||||||
|
pub misc: Option<Vec<BiomeSpawner>>,
|
||||||
|
pub axolotls: Option<Vec<BiomeSpawner>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct BiomeSpawner {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub ty: Identifier,
|
||||||
|
pub weight: i32,
|
||||||
|
#[serde(rename = "minCount")]
|
||||||
|
pub min_count: i32,
|
||||||
|
#[serde(rename = "maxCount")]
|
||||||
|
pub max_count: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct BiomeSpawnCost {
|
||||||
|
pub energy_budget: f64,
|
||||||
|
pub charge: f64,
|
||||||
|
}
|
1
potato-data/src/registry/worldgen/mod.rs
Normal file
1
potato-data/src/registry/worldgen/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod biome;
|
34
potato-data/src/tag.rs
Normal file
34
potato-data/src/tag.rs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::identifier::{Identifier, IdentifierError};
|
||||||
|
|
||||||
|
// TODO: Needs to serialize/deserialize as the identifier with a hash in front.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(try_from = "&str", into = "String")]
|
||||||
|
pub struct Tag(Identifier);
|
||||||
|
|
||||||
|
impl TryFrom<&str> for Tag {
|
||||||
|
type Error = TagError;
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
match value.strip_prefix("#") {
|
||||||
|
Some(v) => Ok(Self(Identifier::try_from(v)?)),
|
||||||
|
None => Err(TagError::MissingHash),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Tag> for String {
|
||||||
|
fn from(value: Tag) -> Self {
|
||||||
|
let v: String = value.0.into();
|
||||||
|
format!("#{v}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum TagError {
|
||||||
|
#[error("Missing hash")]
|
||||||
|
MissingHash,
|
||||||
|
#[error("Invalid identifier: {0}")]
|
||||||
|
InvalidIdentifier(#[from] IdentifierError),
|
||||||
|
}
|
166
potato-data/src/text_component.rs
Normal file
166
potato-data/src/text_component.rs
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{Either, identifier::Identifier, item_stack::ItemComponent};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||||
|
pub struct TextComponent {
|
||||||
|
// Content
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub content: TextComponentContent,
|
||||||
|
// Children
|
||||||
|
#[serde(default)]
|
||||||
|
pub extra: Vec<TextComponent>,
|
||||||
|
// Formatting
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub style: TextComponentStyle,
|
||||||
|
// Interactivity
|
||||||
|
pub insertion: Option<String>,
|
||||||
|
#[serde(rename = "clickEvent")]
|
||||||
|
pub click_event: Option<ClickEvent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||||
|
pub struct TextComponentStyle {
|
||||||
|
pub color: Option<String>,
|
||||||
|
pub font: Option<Identifier>,
|
||||||
|
pub bold: Option<bool>,
|
||||||
|
pub italic: Option<bool>,
|
||||||
|
pub underlined: Option<bool>,
|
||||||
|
pub strikethrough: Option<bool>,
|
||||||
|
pub obfuscated: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||||
|
#[serde(tag = "action", content = "value", rename_all = "snake_case")]
|
||||||
|
pub enum ClickEvent {
|
||||||
|
OpenUrl(String),
|
||||||
|
OpenFile(String),
|
||||||
|
RunCommand(String),
|
||||||
|
SuggestCommand(String),
|
||||||
|
ChangePage(String),
|
||||||
|
CopyToClipboard(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||||
|
#[serde(tag = "action", content = "contents", rename_all = "snake_case")]
|
||||||
|
pub enum HoverEvent {
|
||||||
|
ShowText(Box<TextComponent>),
|
||||||
|
ShowItem {
|
||||||
|
id: Identifier,
|
||||||
|
count: Option<i32>,
|
||||||
|
#[serde(default)]
|
||||||
|
components: Vec<ItemComponent>,
|
||||||
|
},
|
||||||
|
ShowEntity {
|
||||||
|
// NOTE: Not sure if this is a TextComponent or just a string.
|
||||||
|
name: String,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
ty: Identifier,
|
||||||
|
id: Either<String, (u32, u32, u32, u32)>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum TextComponentContent {
|
||||||
|
Text(TextComponentText),
|
||||||
|
Translatable(TextComponentTranslatable),
|
||||||
|
Score(TextComponentScore),
|
||||||
|
Selector(TextComponentSelector),
|
||||||
|
Keybind(TextComponentKeybind),
|
||||||
|
Nbt(TextComponentNbt),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||||
|
#[serde(tag = "type", rename = "text")]
|
||||||
|
pub struct TextComponentText {
|
||||||
|
pub text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||||
|
#[serde(tag = "type", rename = "translatable")]
|
||||||
|
pub struct TextComponentTranslatable {
|
||||||
|
pub translate: String,
|
||||||
|
pub fallback: Option<String>,
|
||||||
|
pub with: Option<Vec<TextComponent>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||||
|
#[serde(tag = "type", rename = "score")]
|
||||||
|
pub struct TextComponentScore {
|
||||||
|
pub score: TextComponentScoreInner,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||||
|
pub struct TextComponentScoreInner {
|
||||||
|
pub name: String,
|
||||||
|
pub objective: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||||
|
#[serde(tag = "type", rename = "selector")]
|
||||||
|
pub struct TextComponentSelector {
|
||||||
|
pub selector: String,
|
||||||
|
// TODO: Instead of an option, make this default to the default value.
|
||||||
|
pub separator: Option<Box<TextComponent>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||||
|
#[serde(tag = "type", rename = "keybind")]
|
||||||
|
pub struct TextComponentKeybind {
|
||||||
|
pub keybind: String, // TODO: Maybe restrict this to the allowed keybinds, or maybe leave that
|
||||||
|
// for the client.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||||
|
#[serde(tag = "type", rename = "nbt")]
|
||||||
|
pub struct TextComponentNbt {
|
||||||
|
pub source: String,
|
||||||
|
pub nbt: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub interpret: bool,
|
||||||
|
// TODO: Instead of an option, make this default to the default value.
|
||||||
|
pub separator: Option<Box<TextComponent>>,
|
||||||
|
pub block: Option<String>,
|
||||||
|
pub entity: Option<String>,
|
||||||
|
pub storage: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::text_component::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_text_component() {
|
||||||
|
let text = r#"{"text":"Hello, world!"}"#;
|
||||||
|
let text_component: TextComponent = serde_json::from_str(text).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
text_component,
|
||||||
|
TextComponent {
|
||||||
|
content: TextComponentContent::Text(TextComponentText {
|
||||||
|
text: "Hello, world!".to_string(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_translatable_text_component() {
|
||||||
|
let text = r#"{"translate":"chat.type.text","with":[{"text":"Hello, world!"}]}"#;
|
||||||
|
let text_component: TextComponent = serde_json::from_str(text).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
text_component,
|
||||||
|
TextComponent {
|
||||||
|
content: TextComponentContent::Translatable(TextComponentTranslatable {
|
||||||
|
translate: "chat.type.text".to_string(),
|
||||||
|
fallback: None,
|
||||||
|
with: Some(vec![TextComponent {
|
||||||
|
content: TextComponentContent::Text(TextComponentText {
|
||||||
|
text: "Hello, world!".to_string(),
|
||||||
|
}),
|
||||||
|
}]),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
potato-data = { path = "../potato-data" }
|
||||||
potato-protocol-derive = { path = "../potato-protocol-derive" }
|
potato-protocol-derive = { path = "../potato-protocol-derive" }
|
||||||
|
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
@ -16,7 +17,9 @@ tokio.workspace = true
|
||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
|
|
||||||
# Build from git, since there has not been a release in over a year
|
# Build from git, since there has not been a release in over a year
|
||||||
fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
|
# Original repo: https://github.com/owengage/fastnbt.git Using a fork
|
||||||
|
# to fix an issue with boolean serialization.
|
||||||
|
fastnbt = { git = "https://github.com/CheAle14/fastnbt.git" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
|
|
@ -1,79 +0,0 @@
|
||||||
use bytes::{Buf, BufMut};
|
|
||||||
|
|
||||||
use crate::packet_encodable::{PacketDecodeError, PacketEncodable, PacketEncodeError};
|
|
||||||
|
|
||||||
// The _phantom is used to stop construction of the struct outside of the new() function. Not for
|
|
||||||
// future api compatibility, but to avoid accidental instantiation of invalid identifiers.
|
|
||||||
#[allow(clippy::manual_non_exhaustive)]
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub struct Identifier {
|
|
||||||
pub namespace: String,
|
|
||||||
pub path: String,
|
|
||||||
_phantom: (),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Identifier {
|
|
||||||
pub fn new(namespace: String, path: String) -> Self {
|
|
||||||
// TODO: Validate namespace and path
|
|
||||||
Self {
|
|
||||||
namespace,
|
|
||||||
path,
|
|
||||||
_phantom: (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_raw_str(raw: &str) -> Self {
|
|
||||||
let mut parts = raw.split(":");
|
|
||||||
Self::new(
|
|
||||||
parts.next().expect("Invalid identifier").to_string(),
|
|
||||||
parts.next().expect("Invalid identifier").to_string(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PacketEncodable for Identifier {
|
|
||||||
fn encode_packet(&self, buf: &mut impl BufMut) -> Result<(), PacketEncodeError> {
|
|
||||||
let val: String = self.into();
|
|
||||||
val.encode_packet(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn decode_packet(buf: &mut impl Buf) -> Result<Self, PacketDecodeError> {
|
|
||||||
let str = String::decode_packet(buf)?;
|
|
||||||
let mut parts = str.split(":");
|
|
||||||
|
|
||||||
Ok(Self::new(
|
|
||||||
parts
|
|
||||||
.next()
|
|
||||||
.ok_or_else(|| PacketDecodeError::InvalidIdentifier(str.clone()))?
|
|
||||||
.to_string(),
|
|
||||||
parts
|
|
||||||
.next()
|
|
||||||
.ok_or_else(|| PacketDecodeError::InvalidIdentifier(str.clone()))?
|
|
||||||
.to_string(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Identifier> for String {
|
|
||||||
fn from(value: Identifier) -> Self {
|
|
||||||
(&value).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&Identifier> for String {
|
|
||||||
fn from(value: &Identifier) -> Self {
|
|
||||||
format!("{}:{}", value.namespace, value.path)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,4 @@
|
||||||
pub mod byte_array;
|
pub mod byte_array;
|
||||||
pub mod identifier;
|
|
||||||
pub mod pack;
|
pub mod pack;
|
||||||
pub mod position;
|
pub mod position;
|
||||||
pub mod var_int;
|
pub mod var_int;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
use potato_data::identifier::Identifier;
|
||||||
use potato_protocol_derive::{Packet, PacketEncodable};
|
use potato_protocol_derive::{Packet, PacketEncodable};
|
||||||
|
|
||||||
use crate::datatypes::{identifier::Identifier, position::Position, var_int::VarInt};
|
use crate::datatypes::{position::Position, var_int::VarInt};
|
||||||
|
|
||||||
#[derive(Debug, Packet)]
|
#[derive(Debug, Packet)]
|
||||||
#[packet(play_id = crate::ids::play::clientbound::LOGIN)]
|
#[packet(play_id = crate::ids::play::clientbound::LOGIN)]
|
||||||
|
|
|
@ -1,7 +1,16 @@
|
||||||
|
use potato_data::{
|
||||||
|
identifier::Identifier,
|
||||||
|
registry::{
|
||||||
|
banner_pattern::BannerPattern, chat_type::ChatType, damage_type::DamageType,
|
||||||
|
dimension_type::DimensionType, painting_variant::PaintingVariant,
|
||||||
|
trim_material::TrimMaterial, trim_pattern::TrimPattern, wolf_variant::WolfVariant,
|
||||||
|
worldgen::biome::Biome,
|
||||||
|
},
|
||||||
|
};
|
||||||
use potato_protocol_derive::{Packet, PacketEncodable};
|
use potato_protocol_derive::{Packet, PacketEncodable};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{datatypes::identifier::Identifier, packet_encodable::Nbt};
|
use crate::packet_encodable::Nbt;
|
||||||
|
|
||||||
#[derive(Debug, Packet)]
|
#[derive(Debug, Packet)]
|
||||||
#[packet(configuration_id = crate::ids::configuration::clientbound::REGISTRY_DATA)]
|
#[packet(configuration_id = crate::ids::configuration::clientbound::REGISTRY_DATA)]
|
||||||
|
@ -19,111 +28,37 @@ pub struct RegistryDataEntry {
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum RegistryData {
|
pub enum RegistryData {
|
||||||
DimensionType(DimensionType),
|
TrimMaterial(Box<TrimMaterial>),
|
||||||
PaintingVariant(PaintingVariant),
|
TrimPattern(Box<TrimPattern>),
|
||||||
WolfVariant(WolfVariant),
|
BannerPattern(Box<BannerPattern>),
|
||||||
DamegeType(DamageType),
|
Biome(Box<Biome>),
|
||||||
Biome(Biome),
|
ChatType(Box<ChatType>),
|
||||||
|
DamageType(Box<DamageType>),
|
||||||
|
DimensionType(Box<DimensionType>),
|
||||||
|
WolfVariant(Box<WolfVariant>),
|
||||||
|
PaintingVariant(Box<PaintingVariant>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
macro_rules! impl_from_registry {
|
||||||
pub struct DimensionType {
|
($($variant:ident),*) => {
|
||||||
pub fixed_time: Option<i64>,
|
$(
|
||||||
pub has_skylight: i8,
|
impl From<&$variant> for RegistryData {
|
||||||
pub has_ceiling: i8,
|
fn from(value: &$variant) -> Self {
|
||||||
pub ultrawarm: i8,
|
RegistryData::$variant(Box::new(value.clone()))
|
||||||
pub natural: i8,
|
}
|
||||||
pub coordinate_scale: f64,
|
}
|
||||||
pub bed_works: i8,
|
)*
|
||||||
pub respawn_anchor_works: i8,
|
};
|
||||||
pub min_y: i32,
|
|
||||||
pub height: i32,
|
|
||||||
pub logical_height: i32,
|
|
||||||
pub infiniburn: String, // TODO: This is a tag, create a type for these
|
|
||||||
pub effects: String, // TODO: This is an enum of 3 string variants:
|
|
||||||
// - mincecraft:overworld
|
|
||||||
// - minecraft:the_nether
|
|
||||||
// - minecraft:the_end
|
|
||||||
pub ambient_light: f32,
|
|
||||||
pub piglin_safe: i8,
|
|
||||||
pub has_raids: i8,
|
|
||||||
pub monster_spawn_light_level: i32, // TODO: This can also be a tag compound of some sort
|
|
||||||
pub monster_spawn_block_light_limit: i32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
impl_from_registry!(
|
||||||
pub struct PaintingVariant {
|
TrimMaterial,
|
||||||
pub asset_id: String,
|
TrimPattern,
|
||||||
pub height: i32,
|
BannerPattern,
|
||||||
pub width: i32,
|
Biome,
|
||||||
// TODO: Missing title and author fields
|
ChatType,
|
||||||
}
|
DamageType,
|
||||||
|
DimensionType,
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
WolfVariant,
|
||||||
pub struct WolfVariant {
|
PaintingVariant
|
||||||
pub wild_texture: String,
|
);
|
||||||
pub tame_texture: String,
|
|
||||||
pub angry_texture: String,
|
|
||||||
pub biomes: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
||||||
pub struct DamageType {
|
|
||||||
pub message_id: String,
|
|
||||||
pub scaling: String,
|
|
||||||
pub exhaustion: f32,
|
|
||||||
pub effects: Option<String>,
|
|
||||||
pub death_message_type: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
||||||
pub struct Biome {
|
|
||||||
pub has_precipitation: i8,
|
|
||||||
pub temperature: f32,
|
|
||||||
pub temperature_modifier: Option<String>,
|
|
||||||
pub downfall: f32,
|
|
||||||
pub effects: BiomeEffects,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
||||||
pub struct BiomeEffects {
|
|
||||||
pub fog_color: i32,
|
|
||||||
pub water_color: i32,
|
|
||||||
pub water_fog_color: i32,
|
|
||||||
pub sky_color: i32,
|
|
||||||
pub foliage_color: Option<i32>,
|
|
||||||
pub grass_color: Option<i32>,
|
|
||||||
pub grass_color_modifier: Option<String>,
|
|
||||||
pub particle: Option<BiomeParticle>,
|
|
||||||
pub ambient_sound: Option<String>, // TODO: Can also be a compound tag
|
|
||||||
pub mood_sound: Option<BiomeMoodSound>,
|
|
||||||
pub additions_sound: Option<BiomeAdditionsSound>,
|
|
||||||
pub music: Option<BiomeMusic>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
||||||
pub struct BiomeParticle {
|
|
||||||
// TODO: Add fields
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
||||||
pub struct BiomeMoodSound {
|
|
||||||
pub sound: String,
|
|
||||||
pub tick_delay: i32,
|
|
||||||
pub block_search_extent: i32,
|
|
||||||
pub offset: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
||||||
pub struct BiomeAdditionsSound {
|
|
||||||
sound: String,
|
|
||||||
tick_chance: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
||||||
pub struct BiomeMusic {
|
|
||||||
sound: String,
|
|
||||||
min_delay: i32,
|
|
||||||
max_delay: i32,
|
|
||||||
replace_current_music: i8,
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use bytes::{Buf, BufMut};
|
use bytes::{Buf, BufMut};
|
||||||
|
use potato_data::identifier::Identifier;
|
||||||
use serde::{Serialize, de::DeserializeOwned};
|
use serde::{Serialize, de::DeserializeOwned};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
@ -180,6 +181,29 @@ impl PacketEncodable for bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PacketEncodable for Identifier {
|
||||||
|
fn encode_packet(&self, buf: &mut impl BufMut) -> Result<(), PacketEncodeError> {
|
||||||
|
let val: String = self.into();
|
||||||
|
val.encode_packet(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_packet(buf: &mut impl Buf) -> Result<Self, PacketDecodeError> {
|
||||||
|
let str = String::decode_packet(buf)?;
|
||||||
|
let mut parts = str.split(":");
|
||||||
|
|
||||||
|
Ok(Self::new(
|
||||||
|
parts
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| PacketDecodeError::InvalidIdentifier(str.clone()))?
|
||||||
|
.to_string(),
|
||||||
|
parts
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| PacketDecodeError::InvalidIdentifier(str.clone()))?
|
||||||
|
.to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! number_impl {
|
macro_rules! number_impl {
|
||||||
($($type:ty, $read_fn:tt, $write_fn:tt),* $(,)?) => {
|
($($type:ty, $read_fn:tt, $write_fn:tt),* $(,)?) => {
|
||||||
$(
|
$(
|
||||||
|
|
|
@ -5,6 +5,7 @@ edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
potato-protocol = { path = "../potato-protocol" }
|
potato-protocol = { path = "../potato-protocol" }
|
||||||
|
potato-data = { path = "../potato-data" }
|
||||||
|
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
use std::{collections::VecDeque, net::SocketAddr, sync::Arc};
|
use std::{collections::VecDeque, net::SocketAddr, sync::Arc};
|
||||||
|
|
||||||
use bytes::{Buf, BytesMut};
|
use bytes::{Buf, BytesMut};
|
||||||
|
use potato_data::{
|
||||||
|
identifier::Identifier,
|
||||||
|
registry::{Registries, Registry},
|
||||||
|
};
|
||||||
use potato_protocol::{
|
use potato_protocol::{
|
||||||
datatypes::{identifier::Identifier, pack::Pack, var_int::VarInt},
|
datatypes::{pack::Pack, var_int::VarInt},
|
||||||
packet::{
|
packet::{
|
||||||
Packet, RawPacket,
|
Packet, RawPacket,
|
||||||
clientbound::{
|
clientbound::{
|
||||||
self, GameEventPacket, SetChunkCacheCenterPacket,
|
self, GameEventPacket, SetChunkCacheCenterPacket,
|
||||||
registry_data::{
|
registry_data::{RegistryData, RegistryDataEntry},
|
||||||
Biome, BiomeEffects, DimensionType, PaintingVariant, RegistryData,
|
|
||||||
RegistryDataEntry, WolfVariant,
|
|
||||||
},
|
|
||||||
status_response,
|
status_response,
|
||||||
},
|
},
|
||||||
serverbound,
|
serverbound,
|
||||||
|
@ -24,8 +25,6 @@ use tokio::{
|
||||||
sync::{Mutex, RwLock, mpsc::Sender},
|
sync::{Mutex, RwLock, mpsc::Sender},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::server::Registries;
|
|
||||||
|
|
||||||
// Max packet size is 2MB in vanilla
|
// Max packet size is 2MB in vanilla
|
||||||
const RECV_BUFFER_SIZE: usize = 1024 * 1024 * 2;
|
const RECV_BUFFER_SIZE: usize = 1024 * 1024 * 2;
|
||||||
|
|
||||||
|
@ -405,107 +404,60 @@ impl Client {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
async fn sync_registries(&self, registries: &Arc<Registries>) -> Result<(), ConnectionError> {
|
async fn sync_registries(&self, registries: &Arc<Registries>) -> Result<(), ConnectionError> {
|
||||||
self.send_packet(&clientbound::RegistryDataPacket {
|
self.send_registry(
|
||||||
registry_id: Identifier::minecraft_str("dimension_type"),
|
Identifier::minecraft_str("dimension_type"),
|
||||||
entries: vec![RegistryDataEntry {
|
®istries.dimension_types,
|
||||||
id: Identifier::minecraft_str("overworld"),
|
)
|
||||||
data: Some(Nbt(RegistryData::DimensionType(DimensionType {
|
|
||||||
fixed_time: None,
|
|
||||||
has_skylight: 1,
|
|
||||||
has_ceiling: 0,
|
|
||||||
ultrawarm: 0,
|
|
||||||
natural: 1,
|
|
||||||
coordinate_scale: 1.,
|
|
||||||
bed_works: 1,
|
|
||||||
respawn_anchor_works: 0,
|
|
||||||
min_y: 0,
|
|
||||||
height: 256,
|
|
||||||
logical_height: 256,
|
|
||||||
infiniburn: "#minecraft:infiniburn_overworld".to_owned(),
|
|
||||||
effects: "minecraft:overworld".to_owned(),
|
|
||||||
ambient_light: 0.,
|
|
||||||
piglin_safe: 1,
|
|
||||||
has_raids: 1,
|
|
||||||
monster_spawn_light_level: 0,
|
|
||||||
monster_spawn_block_light_limit: 0,
|
|
||||||
}))),
|
|
||||||
}],
|
|
||||||
})
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
self.send_packet(&clientbound::RegistryDataPacket {
|
self.send_registry(
|
||||||
registry_id: Identifier::minecraft_str("painting_variant"),
|
Identifier::minecraft_str("painting_variant"),
|
||||||
entries: vec![RegistryDataEntry {
|
®istries.painting_variants,
|
||||||
id: Identifier::minecraft_str("backyard"),
|
)
|
||||||
data: Some(Nbt(RegistryData::PaintingVariant(PaintingVariant {
|
|
||||||
asset_id: "minecraft:backyard".to_owned(),
|
|
||||||
height: 2,
|
|
||||||
width: 2,
|
|
||||||
}))),
|
|
||||||
}],
|
|
||||||
})
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
self.send_packet(&clientbound::RegistryDataPacket {
|
self.send_registry(
|
||||||
registry_id: Identifier::minecraft_str("wolf_variant"),
|
Identifier::minecraft_str("wolf_variant"),
|
||||||
entries: vec![RegistryDataEntry {
|
®istries.wolf_variants,
|
||||||
id: Identifier::minecraft_str("ashen"),
|
)
|
||||||
data: Some(Nbt(RegistryData::WolfVariant(WolfVariant {
|
|
||||||
wild_texture: "minecraft:entity/wolf/wolf_ashen".to_owned(),
|
|
||||||
tame_texture: "minecraft:entity/wolf/wolf_ashen_tame".to_owned(),
|
|
||||||
angry_texture: "minecraft:entity/wolf/wolf_ashen_angry".to_owned(),
|
|
||||||
biomes: vec![],
|
|
||||||
}))),
|
|
||||||
}],
|
|
||||||
})
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let damage_type_registry_entries = registries
|
self.send_registry(
|
||||||
.damage_types
|
Identifier::minecraft_str("damage_type"),
|
||||||
.iter()
|
®istries.damage_types,
|
||||||
.map(|(key, value)| RegistryDataEntry {
|
)
|
||||||
id: key.clone(),
|
|
||||||
data: Some(Nbt(RegistryData::DamegeType(value.clone().into()))),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
self.send_packet(&clientbound::RegistryDataPacket {
|
|
||||||
registry_id: Identifier::minecraft_str("damage_type"),
|
|
||||||
entries: damage_type_registry_entries,
|
|
||||||
})
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
self.send_packet(&clientbound::RegistryDataPacket {
|
self.send_registry(
|
||||||
registry_id: Identifier::minecraft_str("worldgen/biome"),
|
Identifier::minecraft_str("worldgen/biome"),
|
||||||
entries: vec![RegistryDataEntry {
|
®istries.worldgen.biomes,
|
||||||
id: Identifier::minecraft_str("plains"),
|
)
|
||||||
data: Some(Nbt(RegistryData::Biome(Biome {
|
|
||||||
has_precipitation: 0,
|
|
||||||
temperature: 0.,
|
|
||||||
temperature_modifier: None,
|
|
||||||
downfall: 0.,
|
|
||||||
effects: BiomeEffects {
|
|
||||||
fog_color: 0,
|
|
||||||
water_color: 0,
|
|
||||||
water_fog_color: 0,
|
|
||||||
sky_color: 0,
|
|
||||||
foliage_color: None,
|
|
||||||
grass_color: None,
|
|
||||||
grass_color_modifier: None,
|
|
||||||
particle: None,
|
|
||||||
ambient_sound: None,
|
|
||||||
mood_sound: None,
|
|
||||||
additions_sound: None,
|
|
||||||
music: None,
|
|
||||||
},
|
|
||||||
}))),
|
|
||||||
}],
|
|
||||||
})
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn send_registry<T>(
|
||||||
|
&self,
|
||||||
|
identifier: Identifier,
|
||||||
|
registry: &Registry<T>,
|
||||||
|
) -> Result<(), ConnectionError>
|
||||||
|
where
|
||||||
|
for<'a> &'a T: Into<RegistryData>,
|
||||||
|
{
|
||||||
|
self.send_packet(&clientbound::RegistryDataPacket {
|
||||||
|
registry_id: identifier,
|
||||||
|
entries: registry
|
||||||
|
.iter()
|
||||||
|
.map(|(key, value)| RegistryDataEntry {
|
||||||
|
id: key.clone(),
|
||||||
|
data: Some(Nbt(value.into())),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
async fn change_state_num(&self, state: i32) -> Result<(), ConnectionError> {
|
async fn change_state_num(&self, state: i32) -> Result<(), ConnectionError> {
|
||||||
let new_state = match state {
|
let new_state = match state {
|
||||||
1 => ConnectionState::Status,
|
1 => ConnectionState::Status,
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
use potato_protocol::packet::clientbound::registry_data;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
||||||
pub struct DamageType {
|
|
||||||
message_id: String,
|
|
||||||
scaling: DamageTypeScaling,
|
|
||||||
exhaustion: f32,
|
|
||||||
effects: Option<DamageTypeEffect>,
|
|
||||||
death_message_type: Option<DamageTypeDeathMessageType>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: It is stupid to have two identical types
|
|
||||||
impl From<DamageType> for registry_data::DamageType {
|
|
||||||
fn from(value: DamageType) -> Self {
|
|
||||||
registry_data::DamageType {
|
|
||||||
message_id: value.message_id,
|
|
||||||
scaling: value.scaling.into(),
|
|
||||||
exhaustion: value.exhaustion,
|
|
||||||
effects: value.effects.map(Into::<String>::into),
|
|
||||||
death_message_type: value.death_message_type.map(Into::<String>::into),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum DamageTypeScaling {
|
|
||||||
Never,
|
|
||||||
WhenCausedByLivingNonPlayer,
|
|
||||||
Always,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<DamageTypeScaling> for String {
|
|
||||||
fn from(value: DamageTypeScaling) -> Self {
|
|
||||||
match value {
|
|
||||||
DamageTypeScaling::Never => "never".into(),
|
|
||||||
DamageTypeScaling::WhenCausedByLivingNonPlayer => {
|
|
||||||
"when_caused_by_living_non_player".into()
|
|
||||||
}
|
|
||||||
DamageTypeScaling::Always => "always".into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum DamageTypeEffect {
|
|
||||||
Hurt,
|
|
||||||
Thorns,
|
|
||||||
Drowning,
|
|
||||||
Burning,
|
|
||||||
Poking,
|
|
||||||
Freezing,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<DamageTypeEffect> for String {
|
|
||||||
fn from(value: DamageTypeEffect) -> Self {
|
|
||||||
match value {
|
|
||||||
DamageTypeEffect::Hurt => "hurt".into(),
|
|
||||||
DamageTypeEffect::Thorns => "thorns".into(),
|
|
||||||
DamageTypeEffect::Drowning => "drowning".into(),
|
|
||||||
DamageTypeEffect::Burning => "burning".into(),
|
|
||||||
DamageTypeEffect::Poking => "poking".into(),
|
|
||||||
DamageTypeEffect::Freezing => "freezing".into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum DamageTypeDeathMessageType {
|
|
||||||
Default,
|
|
||||||
FallVariants,
|
|
||||||
IntentionalGameDesign,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<DamageTypeDeathMessageType> for String {
|
|
||||||
fn from(value: DamageTypeDeathMessageType) -> Self {
|
|
||||||
match value {
|
|
||||||
DamageTypeDeathMessageType::Default => "default".into(),
|
|
||||||
DamageTypeDeathMessageType::FallVariants => "fall_variants".into(),
|
|
||||||
DamageTypeDeathMessageType::IntentionalGameDesign => "intentional_game_design".into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
pub mod damage_type;
|
|
||||||
|
|
||||||
use std::{fs::read_to_string, path::PathBuf};
|
|
||||||
|
|
||||||
use damage_type::DamageType;
|
|
||||||
use potato_protocol::datatypes::identifier::Identifier;
|
|
||||||
|
|
||||||
use crate::server::{Registries, Registry};
|
|
||||||
|
|
||||||
pub struct Datapack {
|
|
||||||
pub name: String,
|
|
||||||
path: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Datapack {
|
|
||||||
pub fn new(path: PathBuf) -> Self {
|
|
||||||
// TODO: Load name (and other info) from pack.mcmeta
|
|
||||||
let name = path.file_name().unwrap().to_str().unwrap().to_string();
|
|
||||||
|
|
||||||
Self { name, path }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load(&self, registries: &mut Registries) {
|
|
||||||
println!("Loading datapack: {}", self.name);
|
|
||||||
self.load_damage_types(&mut registries.damage_types);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Remove the unwraps and deal with errors gracefully
|
|
||||||
fn load_damage_types(&self, registry: &mut Registry<DamageType>) {
|
|
||||||
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().unwrap().to_str().unwrap();
|
|
||||||
let path = namespace.join("damage_type");
|
|
||||||
if let Ok(files) = std::fs::read_dir(path) {
|
|
||||||
for file in files {
|
|
||||||
let file = file.unwrap().path();
|
|
||||||
let name = file.file_stem().unwrap().to_str().unwrap();
|
|
||||||
let identfiier = Identifier::new_str(namespace_str, name);
|
|
||||||
|
|
||||||
let data: DamageType = serde_json::from_str(&read_to_string(file).unwrap())
|
|
||||||
.expect("Failed to parse damage type");
|
|
||||||
|
|
||||||
registry.insert(identfiier, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,4 @@
|
||||||
pub mod connection;
|
pub mod connection;
|
||||||
pub mod datapack;
|
|
||||||
pub mod player;
|
pub mod player;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::sync::Arc;
|
||||||
|
|
||||||
use potato_protocol::datatypes::identifier::Identifier;
|
use potato_data::{datapack::Datapack, registry::Registries};
|
||||||
|
|
||||||
use crate::datapack::{Datapack, damage_type::DamageType};
|
|
||||||
|
|
||||||
pub struct Server {
|
pub struct Server {
|
||||||
pub registries: Arc<Registries>,
|
pub registries: Arc<Registries>,
|
||||||
|
@ -21,27 +19,28 @@ impl Server {
|
||||||
println!("Found {} datapacks", datapacks.len());
|
println!("Found {} datapacks", datapacks.len());
|
||||||
println!("Loading datapacks...");
|
println!("Loading datapacks...");
|
||||||
let mut registries = Registries::empty();
|
let mut registries = Registries::empty();
|
||||||
datapacks.iter().for_each(|pack| pack.load(&mut registries));
|
datapacks
|
||||||
|
.iter()
|
||||||
|
.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());
|
||||||
|
println!("Loaded {} trim patterns", registries.trim_patterns.len());
|
||||||
|
println!("Loaded {} chat types", registries.chat_types.len());
|
||||||
|
println!(
|
||||||
|
"Loaded {} dimension types",
|
||||||
|
registries.dimension_types.len()
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"Loaded {} painting variants",
|
||||||
|
registries.painting_variants.len()
|
||||||
|
);
|
||||||
|
println!("Loaded {} wolf variants", registries.wolf_variants.len());
|
||||||
println!("Loaded {} damage types", registries.damage_types.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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Registry<T> = HashMap<Identifier, T>;
|
|
||||||
|
|
||||||
pub struct Registries {
|
|
||||||
pub damage_types: Registry<DamageType>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Registries {
|
|
||||||
fn empty() -> Registries {
|
|
||||||
Registries {
|
|
||||||
damage_types: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue