diff --git a/crates/valence_entity/extracted/entities.json b/crates/valence_entity/extracted/entities.json index 2102f18a9..dba249584 100644 --- a/crates/valence_entity/extracted/entities.json +++ b/crates/valence_entity/extracted/entities.json @@ -1528,7 +1528,7 @@ "name": "health", "index": 9, "type": "float", - "default_value": 6.0 + "default_value": 20.0 }, { "name": "potion_swirls_color", @@ -2863,7 +2863,7 @@ "type": "villager_data", "default_value": { "type": "plains", - "profession": "weaponsmith", + "profession": "none", "level": 1 } } diff --git a/crates/valence_generated/build/main.rs b/crates/valence_generated/build/main.rs index adfa4fec2..2e05bb122 100644 --- a/crates/valence_generated/build/main.rs +++ b/crates/valence_generated/build/main.rs @@ -5,6 +5,7 @@ mod chunk_view; mod item; mod packet_id; mod sound; +mod status_effects; pub fn main() -> anyhow::Result<()> { write_generated_file(block::build()?, "block.rs")?; @@ -12,6 +13,7 @@ pub fn main() -> anyhow::Result<()> { write_generated_file(sound::build()?, "sound.rs")?; write_generated_file(packet_id::build()?, "packet_id.rs")?; write_generated_file(chunk_view::build(), "chunk_view.rs")?; + write_generated_file(status_effects::build()?, "status_effects.rs")?; Ok(()) } diff --git a/crates/valence_generated/build/status_effects.rs b/crates/valence_generated/build/status_effects.rs new file mode 100644 index 000000000..b9d0384c4 --- /dev/null +++ b/crates/valence_generated/build/status_effects.rs @@ -0,0 +1,226 @@ +use heck::ToPascalCase; +use proc_macro2::TokenStream; +use quote::quote; +use serde::Deserialize; +use valence_build_utils::{ident, rerun_if_changed}; + +#[derive(Deserialize, Debug)] +pub enum StatusEffectCategory { + Beneficial, + Harmful, + Neutral, +} + +#[derive(Deserialize, Debug)] +pub struct StatusEffect { + id: u16, + name: String, + translation_key: String, + category: StatusEffectCategory, + color: u32, + instant: bool, +} + +pub fn build() -> anyhow::Result { + rerun_if_changed(["extracted/effects.json"]); + + let effects = + serde_json::from_str::>(include_str!("../extracted/effects.json"))?; + + let effect_count = effects.len(); + + let effect_from_raw_id_arms = effects + .iter() + .map(|effect| { + let id = &effect.id; + let name = ident(effect.name.to_pascal_case()); + + quote! { + #id => Some(Self::#name), + } + }) + .collect::(); + + let effect_to_raw_id_arms = effects + .iter() + .map(|effect| { + let id = &effect.id; + let name = ident(effect.name.to_pascal_case()); + + quote! { + Self::#name => #id, + } + }) + .collect::(); + + let effect_from_ident_arms = effects + .iter() + .map(|effect| { + let path_name = &effect.name; + let ident_name = format!("minecraft:{}", &effect.name); + + let name = ident(path_name.to_pascal_case()); + quote! { + #ident_name => Some(Self::#name), + } + }) + .collect::(); + + let effect_to_ident_arms = effects + .iter() + .map(|effect| { + let str_name = &effect.name; + let name = ident(str_name.to_pascal_case()); + quote! { + Self::#name => ident!(#str_name), + } + }) + .collect::(); + + let effect_to_translation_key_arms = effects + .iter() + .map(|effect| { + let str_name = &effect.translation_key; + let name = ident(effect.name.to_pascal_case()); + quote! { + Self::#name => #str_name, + } + }) + .collect::(); + + let effect_to_category_arms = effects + .iter() + .map(|effect| { + let category = match &effect.category { + StatusEffectCategory::Beneficial => quote! { StatusEffectCategory::Beneficial }, + StatusEffectCategory::Harmful => quote! { StatusEffectCategory::Harmful }, + StatusEffectCategory::Neutral => quote! { StatusEffectCategory::Neutral }, + }; + + let name = ident(effect.name.to_pascal_case()); + quote! { + Self::#name => #category, + } + }) + .collect::(); + + let effect_to_color_arms = effects + .iter() + .map(|effect| { + let color = &effect.color; + let name = ident(effect.name.to_pascal_case()); + quote! { + Self::#name => #color, + } + }) + .collect::(); + + let effect_to_instant_arms = effects + .iter() + .map(|effect| { + let instant = &effect.instant; + let name = ident(effect.name.to_pascal_case()); + quote! { + Self::#name => #instant, + } + }) + .collect::(); + + let effect_variants = effects + .iter() + .map(|effect| ident(effect.name.to_pascal_case())) + .collect::>(); + + Ok(quote! { + use valence_ident::{Ident, ident}; + + /// Represents a status effect category + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] + pub enum StatusEffectCategory { + Beneficial, + Harmful, + Neutral, + } + + /// Represents a status effect from the game + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] + pub enum StatusEffect { + #(#effect_variants,)* + } + + impl StatusEffect { + /// Constructs a effect from a raw item ID. + /// + /// If the given ID is invalid, `None` is returned. + pub const fn from_raw(id: u16) -> Option { + match id { + #effect_from_raw_id_arms + _ => None + } + } + + /// Gets the raw effect ID from the effect + pub const fn to_raw(self) -> u16 { + match self { + #effect_to_raw_id_arms + } + } + + /// Construct a effect from its snake_case name. + /// + /// Returns `None` if the name is invalid. + pub fn from_ident(id: Ident<&str>) -> Option { + match id.as_str() { + #effect_from_ident_arms + _ => None + } + } + + /// Gets the identifier of this effect. + pub const fn to_ident(self) -> Ident<&'static str> { + match self { + #effect_to_ident_arms + } + } + + /// Gets the name of this effect. + /// Same as [`StatusEffect::to_ident`], but doesn't take ownership. + pub const fn name(&self) -> Ident<&'static str> { + match self { + #effect_to_ident_arms + } + } + + /// Gets the translation key of this effect. + pub const fn translation_key(&self) -> &'static str { + match self { + #effect_to_translation_key_arms + } + } + + /// Gets the category of this effect. + pub const fn category(&self) -> StatusEffectCategory { + match self { + #effect_to_category_arms + } + } + + /// Gets the color of this effect. + pub const fn color(&self) -> u32 { + match self { + #effect_to_color_arms + } + } + + /// Gets whether this effect is instant. + pub const fn instant(&self) -> bool { + match self { + #effect_to_instant_arms + } + } + + /// An array of all effects. + pub const ALL: [Self; #effect_count] = [#(Self::#effect_variants,)*]; + } + }) +} diff --git a/crates/valence_generated/extracted/effects.json b/crates/valence_generated/extracted/effects.json new file mode 100644 index 000000000..ac730ae22 --- /dev/null +++ b/crates/valence_generated/extracted/effects.json @@ -0,0 +1,266 @@ +[ + { + "id": 1, + "name": "speed", + "translation_key": "effect.minecraft.speed", + "color": 3402751, + "instant": false, + "category": "Beneficial" + }, + { + "id": 2, + "name": "slowness", + "translation_key": "effect.minecraft.slowness", + "color": 9154528, + "instant": false, + "category": "Harmful" + }, + { + "id": 3, + "name": "haste", + "translation_key": "effect.minecraft.haste", + "color": 14270531, + "instant": false, + "category": "Beneficial" + }, + { + "id": 4, + "name": "mining_fatigue", + "translation_key": "effect.minecraft.mining_fatigue", + "color": 4866583, + "instant": false, + "category": "Harmful" + }, + { + "id": 5, + "name": "strength", + "translation_key": "effect.minecraft.strength", + "color": 16762624, + "instant": false, + "category": "Beneficial" + }, + { + "id": 6, + "name": "instant_health", + "translation_key": "effect.minecraft.instant_health", + "color": 16262179, + "instant": true, + "category": "Beneficial" + }, + { + "id": 7, + "name": "instant_damage", + "translation_key": "effect.minecraft.instant_damage", + "color": 11101546, + "instant": true, + "category": "Harmful" + }, + { + "id": 8, + "name": "jump_boost", + "translation_key": "effect.minecraft.jump_boost", + "color": 16646020, + "instant": false, + "category": "Beneficial" + }, + { + "id": 9, + "name": "nausea", + "translation_key": "effect.minecraft.nausea", + "color": 5578058, + "instant": false, + "category": "Harmful" + }, + { + "id": 10, + "name": "regeneration", + "translation_key": "effect.minecraft.regeneration", + "color": 13458603, + "instant": false, + "category": "Beneficial" + }, + { + "id": 11, + "name": "resistance", + "translation_key": "effect.minecraft.resistance", + "color": 9520880, + "instant": false, + "category": "Beneficial" + }, + { + "id": 12, + "name": "fire_resistance", + "translation_key": "effect.minecraft.fire_resistance", + "color": 16750848, + "instant": false, + "category": "Beneficial" + }, + { + "id": 13, + "name": "water_breathing", + "translation_key": "effect.minecraft.water_breathing", + "color": 10017472, + "instant": false, + "category": "Beneficial" + }, + { + "id": 14, + "name": "invisibility", + "translation_key": "effect.minecraft.invisibility", + "color": 16185078, + "instant": false, + "category": "Beneficial" + }, + { + "id": 15, + "name": "blindness", + "translation_key": "effect.minecraft.blindness", + "color": 2039587, + "instant": false, + "category": "Harmful" + }, + { + "id": 16, + "name": "night_vision", + "translation_key": "effect.minecraft.night_vision", + "color": 12779366, + "instant": false, + "category": "Beneficial" + }, + { + "id": 17, + "name": "hunger", + "translation_key": "effect.minecraft.hunger", + "color": 5797459, + "instant": false, + "category": "Harmful" + }, + { + "id": 18, + "name": "weakness", + "translation_key": "effect.minecraft.weakness", + "color": 4738376, + "instant": false, + "category": "Harmful" + }, + { + "id": 19, + "name": "poison", + "translation_key": "effect.minecraft.poison", + "color": 8889187, + "instant": false, + "category": "Harmful" + }, + { + "id": 20, + "name": "wither", + "translation_key": "effect.minecraft.wither", + "color": 7561558, + "instant": false, + "category": "Harmful" + }, + { + "id": 21, + "name": "health_boost", + "translation_key": "effect.minecraft.health_boost", + "color": 16284963, + "instant": false, + "category": "Beneficial" + }, + { + "id": 22, + "name": "absorption", + "translation_key": "effect.minecraft.absorption", + "color": 2445989, + "instant": false, + "category": "Beneficial" + }, + { + "id": 23, + "name": "saturation", + "translation_key": "effect.minecraft.saturation", + "color": 16262179, + "instant": true, + "category": "Beneficial" + }, + { + "id": 24, + "name": "glowing", + "translation_key": "effect.minecraft.glowing", + "color": 9740385, + "instant": false, + "category": "Neutral" + }, + { + "id": 25, + "name": "levitation", + "translation_key": "effect.minecraft.levitation", + "color": 13565951, + "instant": false, + "category": "Harmful" + }, + { + "id": 26, + "name": "luck", + "translation_key": "effect.minecraft.luck", + "color": 5882118, + "instant": false, + "category": "Beneficial" + }, + { + "id": 27, + "name": "unluck", + "translation_key": "effect.minecraft.unluck", + "color": 12624973, + "instant": false, + "category": "Harmful" + }, + { + "id": 28, + "name": "slow_falling", + "translation_key": "effect.minecraft.slow_falling", + "color": 15978425, + "instant": false, + "category": "Beneficial" + }, + { + "id": 29, + "name": "conduit_power", + "translation_key": "effect.minecraft.conduit_power", + "color": 1950417, + "instant": false, + "category": "Beneficial" + }, + { + "id": 30, + "name": "dolphins_grace", + "translation_key": "effect.minecraft.dolphins_grace", + "color": 8954814, + "instant": false, + "category": "Beneficial" + }, + { + "id": 31, + "name": "bad_omen", + "translation_key": "effect.minecraft.bad_omen", + "color": 745784, + "instant": false, + "category": "Neutral" + }, + { + "id": 32, + "name": "hero_of_the_village", + "translation_key": "effect.minecraft.hero_of_the_village", + "color": 4521796, + "instant": false, + "category": "Beneficial" + }, + { + "id": 33, + "name": "darkness", + "translation_key": "effect.minecraft.darkness", + "color": 2696993, + "instant": false, + "category": "Harmful" + } +] \ No newline at end of file diff --git a/crates/valence_generated/src/lib.rs b/crates/valence_generated/src/lib.rs index 53df8ae19..6a1620f46 100644 --- a/crates/valence_generated/src/lib.rs +++ b/crates/valence_generated/src/lib.rs @@ -16,3 +16,7 @@ pub mod packet_id { pub mod chunk_view { include!(concat!(env!("OUT_DIR"), "/chunk_view.rs")); } + +pub mod status_effects { + include!(concat!(env!("OUT_DIR"), "/status_effects.rs")); +} diff --git a/crates/valence_protocol/src/lib.rs b/crates/valence_protocol/src/lib.rs index b0294cdcf..e200795d8 100644 --- a/crates/valence_protocol/src/lib.rs +++ b/crates/valence_protocol/src/lib.rs @@ -81,7 +81,7 @@ pub use raw::RawBytes; use serde::{Deserialize, Serialize}; pub use sound::Sound; pub use text::Text; -pub use valence_generated::{block, packet_id}; +pub use valence_generated::{block, packet_id, status_effects}; pub use valence_ident::Ident; pub use valence_protocol_macros::{Decode, Encode, Packet}; pub use var_int::VarInt; diff --git a/extractor/copy_extractor_output.sh b/extractor/copy_extractor_output.sh index 0bbdabb49..1efa1a8b3 100755 --- a/extractor/copy_extractor_output.sh +++ b/extractor/copy_extractor_output.sh @@ -8,7 +8,7 @@ set -euxo pipefail cd "$(dirname "$0")" cp run/valence_extractor_output/{entities,misc}.json ../crates/valence_entity/extracted/ -cp run/valence_extractor_output/{blocks,items,packets,sounds}.json ../crates/valence_generated/extracted/ +cp run/valence_extractor_output/{blocks,effects,items,packets,sounds}.json ../crates/valence_generated/extracted/ cp run/valence_extractor_output/translation_keys.json ../crates/valence_lang/extracted/ cp run/valence_extractor_output/{registry_codec.dat,tags.json} ../crates/valence_registry/extracted/ cp run/valence_extractor_output/packets.json ../tools/packet_inspector/extracted/ diff --git a/extractor/src/main/java/rs/valence/extractor/Main.java b/extractor/src/main/java/rs/valence/extractor/Main.java index b31433111..22f274480 100644 --- a/extractor/src/main/java/rs/valence/extractor/Main.java +++ b/extractor/src/main/java/rs/valence/extractor/Main.java @@ -42,6 +42,7 @@ public void onInitialize() { var extractors = new Extractor[]{ new Blocks(), + new Effects(), new Enchants(), new Entities(), new Misc(), diff --git a/extractor/src/main/java/rs/valence/extractor/ValenceUtils.java b/extractor/src/main/java/rs/valence/extractor/ValenceUtils.java new file mode 100644 index 000000000..847da11ab --- /dev/null +++ b/extractor/src/main/java/rs/valence/extractor/ValenceUtils.java @@ -0,0 +1,34 @@ +package rs.valence.extractor; + +/** + * Utility class for various methods. + */ +public class ValenceUtils { + private ValenceUtils() { + throw new UnsupportedOperationException("This class cannot be instantiated"); + } + + /** + * Converts a string to PascalCase. + * + * @param str The string to convert. + * @return The converted string. + */ + public static String toPascalCase(String str) { + StringBuilder result = new StringBuilder(); + boolean capitalizeNext = true; + + for (char c : str.toCharArray()) { + if (Character.isWhitespace(c) || c == '_') { + capitalizeNext = true; + } else if (capitalizeNext) { + result.append(Character.toUpperCase(c)); + capitalizeNext = false; + } else { + result.append(Character.toLowerCase(c)); + } + } + + return result.toString(); + } +} diff --git a/extractor/src/main/java/rs/valence/extractor/extractors/Effects.java b/extractor/src/main/java/rs/valence/extractor/extractors/Effects.java new file mode 100644 index 000000000..2d899c7b0 --- /dev/null +++ b/extractor/src/main/java/rs/valence/extractor/extractors/Effects.java @@ -0,0 +1,38 @@ +package rs.valence.extractor.extractors; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.minecraft.registry.Registries; +import rs.valence.extractor.Main; +import rs.valence.extractor.ValenceUtils; + +public class Effects implements Main.Extractor { + public Effects() { + } + + @Override + public String fileName() { + return "effects.json"; + } + + @Override + public JsonElement extract() { + var effectsJson = new JsonArray(); + + for (var effect : Registries.STATUS_EFFECT) { + var effectJson = new JsonObject(); + + effectJson.addProperty("id", Registries.STATUS_EFFECT.getRawId(effect)); + effectJson.addProperty("name", Registries.STATUS_EFFECT.getId(effect).getPath()); + effectJson.addProperty("translation_key", effect.getTranslationKey()); + effectJson.addProperty("color", effect.getColor()); + effectJson.addProperty("instant", effect.isInstant()); + effectJson.addProperty("category", ValenceUtils.toPascalCase(effect.getCategory().name())); + + effectsJson.add(effectJson); + } + + return effectsJson; + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index dfffdb3db..bee173f9b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,6 +71,7 @@ use valence_server::layer::LayerPlugin; use valence_server::message::MessagePlugin; use valence_server::movement::MovementPlugin; use valence_server::op_level::OpLevelPlugin; +pub use valence_server::protocol::status_effects; use valence_server::resource_pack::ResourcePackPlugin; use valence_server::status::StatusPlugin; use valence_server::teleport::TeleportPlugin;