From b83b9f76edb2f4326cfd11a2cff5ce99ab3a7e2e Mon Sep 17 00:00:00 2001 From: Terminator Date: Mon, 3 Oct 2022 22:19:42 +0200 Subject: [PATCH] Expose enchantments to Rust (#99) * Expose extracted enchantments to Rust. * Give enchantments a `level: i16` field. * Update enchantment derive impls * Restructure how enchants are exposed to Rust through C-like enums * Update build/enchant.rs Co-authored-by: Ryan Johnson * Update build/enchant.rs Co-authored-by: Ryan Johnson * Move enchantment sources to their own struct. * make id() function return it's discriminant instead of matching Co-authored-by: Ryan Johnson --- build/enchant.rs | 223 +++++++++++++++++++++++++++++++++++++++++++++++ build/main.rs | 2 + src/enchant.rs | 5 ++ src/lib.rs | 1 + 4 files changed, 231 insertions(+) create mode 100644 build/enchant.rs create mode 100644 src/enchant.rs diff --git a/build/enchant.rs b/build/enchant.rs new file mode 100644 index 0000000..d75dac1 --- /dev/null +++ b/build/enchant.rs @@ -0,0 +1,223 @@ +use heck::ToPascalCase; +use proc_macro2::TokenStream; +use quote::quote; +use serde::Deserialize; + +use crate::ident; + +#[derive(Deserialize, Debug)] +struct TopLevel { + enchants: Vec, +} + +#[derive(Deserialize, Debug)] +pub struct ParsedEnchantment { + #[allow(unused)] + id: u16, + name: String, + translation_key: String, + min_level: i16, + max_level: i16, + #[serde(alias = "cursed")] + is_curse: bool, + rarity_weight: i32, + #[serde(alias = "sources")] + source: ParsedEnchantmentSource, +} + +#[derive(Deserialize, Debug)] +pub struct ParsedEnchantmentSource { + treasure: bool, + enchantment_table: bool, + random_selection: bool, +} + +pub fn build() -> anyhow::Result { + let TopLevel { enchants } = serde_json::from_str(include_str!("../extracted/enchants.json"))?; + + let enchantmentkind_definitions = enchants + .iter() + .map(|enchant| { + let rustified_name = ident(enchant.name.to_pascal_case()); + let id = enchant.id as isize; + quote! { + #rustified_name = #id, + } + }) + .collect::(); + + let enchantmentkind_id_to_variant_lookup = enchants + .iter() + .map(|enchant| { + let rustified_name = ident(enchant.name.to_pascal_case()); + let id = &enchant.id; + quote! { + #id => Some(Self::#rustified_name), + } + }) + .collect::(); + + let enchantmentkind_names = enchants + .iter() + .map(|enchant| { + let rustified_name = ident(enchant.name.to_pascal_case()); + let name = &enchant.name; + quote! { + Self::#rustified_name => #name, + } + }) + .collect::(); + + let enchantmentkind_translations = enchants + .iter() + .map(|enchant| { + let rustified_name = ident(enchant.name.to_pascal_case()); + let translation_key = &enchant.translation_key; + quote! { + Self::#rustified_name => #translation_key, + } + }) + .collect::(); + + let enchantmentkind_min_level = enchants + .iter() + .map(|enchant| { + let rustified_name = ident(enchant.name.to_pascal_case()); + let min_level = &enchant.min_level; + quote! { + Self::#rustified_name => #min_level, + } + }) + .collect::(); + + let enchantmentkind_max_level = enchants + .iter() + .map(|enchant| { + let rustified_name = ident(enchant.name.to_pascal_case()); + let max_level = &enchant.max_level; + quote! { + Self::#rustified_name => #max_level, + } + }) + .collect::(); + + let enchantmentkind_is_curse = enchants + .iter() + .map(|enchant| { + let rustified_name = ident(enchant.name.to_pascal_case()); + let is_curse = &enchant.is_curse; + quote! { + Self::#rustified_name => #is_curse, + } + }) + .collect::(); + + let enchantmentkind_rarity_weight = enchants + .iter() + .map(|enchant| { + let rustified_name = ident(enchant.name.to_pascal_case()); + let rarity_weight = &enchant.rarity_weight; + quote! { + Self::#rustified_name => #rarity_weight, + } + }) + .collect::(); + + let enchantmentkind_sources = enchants + .iter() + .map(|enchant| { + let rustified_name = ident(enchant.name.to_pascal_case()); + let treasure = &enchant.source.treasure; + let enchantment_table = &enchant.source.enchantment_table; + let random_selection = &enchant.source.random_selection; + quote! { + Self::#rustified_name => EnchantmentSources { + treasure: #treasure, + enchantment_table: #enchantment_table, + random_selection: #random_selection, + }, + } + }) + .collect::(); + + Ok(quote! { + + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct EnchantmentSources { + pub treasure: bool, + pub enchantment_table: bool, + pub random_selection: bool, + } + + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub enum EnchantmentKind { + #enchantmentkind_definitions + } + + impl EnchantmentKind { + /// Constructs an `EnchantmentKind` from a raw enchantment ID. + /// + /// If the given ID is invalid, `None` is returned. + pub const fn from_raw(id: u16) -> Option { + match id{ + #enchantmentkind_id_to_variant_lookup + _ => None + } + } + + /// Returns the enchantment ID. + pub const fn id(self) -> u16 { + self as u16 + } + + /// Returns the translation key. + pub const fn translation_key(self) -> &'static str { + match self{ + #enchantmentkind_translations + } + } + + /// Returns the enchantment name the game uses. + pub const fn name(self) -> &'static str { + match self{ + #enchantmentkind_names + } + } + + /// Returns the minimum enchantment level officially supported by Minecraft. + pub const fn min_level(self) -> i16 { + match self{ + #enchantmentkind_min_level + } + } + + /// Returns the maximum enchantment level officially supported by Minecraft. + pub const fn max_level(self) -> i16 { + match self{ + #enchantmentkind_max_level + } + } + + /// Returns true if the enchantment is of the curse type. + pub const fn is_curse(self) -> bool { + match self{ + #enchantmentkind_is_curse + } + } + + /// Returns the rarity of the enchant. Lower means more rare. + pub const fn rarity_weight(self) -> i32 { + match self{ + #enchantmentkind_rarity_weight + } + } + + /// Returns the different sources this enchantment has. + pub const fn sources(self) -> EnchantmentSources { + match self{ + #enchantmentkind_sources + } + } + } + }) +} diff --git a/build/main.rs b/build/main.rs index 0d66643..790080d 100644 --- a/build/main.rs +++ b/build/main.rs @@ -6,6 +6,7 @@ use anyhow::Context; use proc_macro2::{Ident, Span}; mod block; +mod enchant; mod entity; mod entity_event; @@ -16,6 +17,7 @@ pub fn main() -> anyhow::Result<()> { (entity::build as fn() -> _, "entity.rs"), (entity_event::build, "entity_event.rs"), (block::build, "block.rs"), + (enchant::build, "enchant.rs"), ]; let out_dir = env::var_os("OUT_DIR").context("can't get OUT_DIR env var")?; diff --git a/src/enchant.rs b/src/enchant.rs new file mode 100644 index 0000000..77f993a --- /dev/null +++ b/src/enchant.rs @@ -0,0 +1,5 @@ +// enchant.rs exposes constant values provided by the build script. +// All enchantment variants are located in `EnchantmentKind`. You can use the +// associated const fn functions of `EnchantmentKind` to access details about an +// enchantment type. enchantment specific functions +include!(concat!(env!("OUT_DIR"), "/enchant.rs")); diff --git a/src/lib.rs b/src/lib.rs index 34df81e..023eded 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -107,6 +107,7 @@ mod chunk_pos; pub mod client; pub mod config; pub mod dimension; +pub mod enchant; pub mod entity; pub mod ident; pub mod itemstack;