1use std::ops::{Deref, DerefMut};
10
11use bevy_app::prelude::*;
12use bevy_ecs::prelude::*;
13use serde::{Deserialize, Serialize};
14use tracing::error;
15use valence_ident::{ident, Ident};
16use valence_nbt::serde::CompoundSerializer;
17
18use crate::codec::{RegistryCodec, RegistryValue};
19use crate::{Registry, RegistryIdx, RegistrySet};
20pub struct DimensionTypePlugin;
21
22impl Plugin for DimensionTypePlugin {
23 fn build(&self, app: &mut App) {
24 app.init_resource::<DimensionTypeRegistry>()
25 .add_systems(PreStartup, load_default_dimension_types)
26 .add_systems(
27 PostUpdate,
28 update_dimension_type_registry.before(RegistrySet),
29 );
30 }
31}
32
33fn load_default_dimension_types(mut reg: ResMut<DimensionTypeRegistry>, codec: Res<RegistryCodec>) {
35 let mut helper = move || -> anyhow::Result<()> {
36 for value in codec.registry(DimensionTypeRegistry::KEY) {
37 let mut dimension_type = DimensionType::deserialize(value.element.clone())?;
38
39 dimension_type.ambient_light = 1.0;
42
43 reg.insert(value.name.clone(), dimension_type);
44 }
45
46 Ok(())
47 };
48
49 if let Err(e) = helper() {
50 error!("failed to load default dimension types from registry codec: {e:#}");
51 }
52}
53
54fn update_dimension_type_registry(
57 reg: Res<DimensionTypeRegistry>,
58 mut codec: ResMut<RegistryCodec>,
59) {
60 if reg.is_changed() {
61 let dimension_types = codec.registry_mut(DimensionTypeRegistry::KEY);
62
63 dimension_types.clear();
64
65 dimension_types.extend(reg.iter().map(|(_, name, dim)| {
66 RegistryValue {
67 name: name.into(),
68 element: dim
69 .serialize(CompoundSerializer)
70 .expect("failed to serialize dimension type"),
71 }
72 }));
73 }
74}
75
76#[derive(Resource, Default, Debug)]
77pub struct DimensionTypeRegistry {
78 reg: Registry<DimensionTypeId, DimensionType>,
79}
80
81impl DimensionTypeRegistry {
82 pub const KEY: Ident<&'static str> = ident!("dimension_type");
83}
84
85#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
86pub struct DimensionTypeId(u16);
87
88impl RegistryIdx for DimensionTypeId {
89 const MAX: usize = u16::MAX as usize;
90
91 fn to_index(self) -> usize {
92 self.0 as usize
93 }
94
95 fn from_index(idx: usize) -> Self {
96 Self(idx as u16)
97 }
98}
99
100impl Deref for DimensionTypeRegistry {
101 type Target = Registry<DimensionTypeId, DimensionType>;
102
103 fn deref(&self) -> &Self::Target {
104 &self.reg
105 }
106}
107
108impl DerefMut for DimensionTypeRegistry {
109 fn deref_mut(&mut self) -> &mut Self::Target {
110 &mut self.reg
111 }
112}
113
114#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
115#[serde(deny_unknown_fields)]
116pub struct DimensionType {
117 pub ambient_light: f32,
118 pub bed_works: bool,
119 pub coordinate_scale: f64,
120 pub effects: DimensionEffects,
121 #[serde(skip_serializing_if = "Option::is_none")]
122 pub fixed_time: Option<i32>,
123 pub has_ceiling: bool,
124 pub has_raids: bool,
125 pub has_skylight: bool,
126 pub height: i32,
127 pub infiniburn: String,
128 pub logical_height: i32,
129 pub min_y: i32,
130 pub monster_spawn_block_light_limit: i32,
131 pub monster_spawn_light_level: MonsterSpawnLightLevel,
132 pub natural: bool,
133 pub piglin_safe: bool,
134 pub respawn_anchor_works: bool,
135 pub ultrawarm: bool,
136}
137
138impl Default for DimensionType {
139 fn default() -> Self {
140 Self {
141 ambient_light: 0.0,
142 bed_works: true,
143 coordinate_scale: 1.0,
144 effects: DimensionEffects::default(),
145 fixed_time: None,
146 has_ceiling: false,
147 has_raids: true,
148 has_skylight: true,
149 height: 384,
150 infiniburn: "#minecraft:infiniburn_overworld".into(),
151 logical_height: 384,
152 min_y: -64,
153 monster_spawn_block_light_limit: 0,
154 monster_spawn_light_level: MonsterSpawnLightLevel::Int(7),
155 natural: true,
156 piglin_safe: false,
157 respawn_anchor_works: false,
158 ultrawarm: false,
159 }
160 }
161}
162
163#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default, Debug)]
165pub enum DimensionEffects {
166 #[serde(rename = "minecraft:overworld")]
167 #[default]
168 Overworld,
169 #[serde(rename = "minecraft:the_nether")]
170 TheNether,
171 #[serde(rename = "minecraft:the_end")]
172 TheEnd,
173}
174
175#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)]
176#[serde(untagged)]
177pub enum MonsterSpawnLightLevel {
178 Int(i32),
179 Tagged(MonsterSpawnLightLevelTagged),
180}
181
182#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)]
183#[serde(tag = "type", content = "value")]
184pub enum MonsterSpawnLightLevelTagged {
185 #[serde(rename = "minecraft:uniform")]
186 Uniform {
187 min_inclusive: i32,
188 max_inclusive: i32,
189 },
190}
191
192impl From<i32> for MonsterSpawnLightLevel {
193 fn from(value: i32) -> Self {
194 Self::Int(value)
195 }
196}