1use std::ops::{Deref, DerefMut};
10
11use bevy_app::prelude::*;
12use bevy_ecs::prelude::*;
13use chunkedge_ident::{ident, Ident};
14use chunkedge_nbt::serde::ser::CompoundSerializer;
15use serde::{Deserialize, Serialize};
16use tracing::error;
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 DimensionTypeId {
89 pub fn new(value: u16) -> Self {
90 DimensionTypeId(value)
91 }
92 pub fn get_value(&self) -> u16 {
93 self.0
94 }
95}
96
97impl RegistryIdx for DimensionTypeId {
98 const MAX: usize = u16::MAX as usize;
99
100 fn to_index(self) -> usize {
101 self.0 as usize
102 }
103
104 fn from_index(idx: usize) -> Self {
105 Self(idx as u16)
106 }
107}
108
109impl Deref for DimensionTypeRegistry {
110 type Target = Registry<DimensionTypeId, DimensionType>;
111
112 fn deref(&self) -> &Self::Target {
113 &self.reg
114 }
115}
116
117impl DerefMut for DimensionTypeRegistry {
118 fn deref_mut(&mut self) -> &mut Self::Target {
119 &mut self.reg
120 }
121}
122
123#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
124#[serde(deny_unknown_fields)]
125pub struct DimensionType {
126 pub ambient_light: f32,
127 pub bed_works: bool,
128 pub coordinate_scale: f64,
129 pub effects: DimensionEffects,
130 #[serde(skip_serializing_if = "Option::is_none")]
131 pub fixed_time: Option<i32>,
132 pub has_ceiling: bool,
133 pub has_raids: bool,
134 pub has_skylight: bool,
135 pub height: i32,
136 pub infiniburn: String,
137 pub logical_height: i32,
138 pub min_y: i32,
139 pub monster_spawn_block_light_limit: i32,
140 pub monster_spawn_light_level: MonsterSpawnLightLevel,
141 pub natural: bool,
142 pub piglin_safe: bool,
143 pub respawn_anchor_works: bool,
144 pub ultrawarm: bool,
145}
146
147impl Default for DimensionType {
148 fn default() -> Self {
149 Self {
150 ambient_light: 0.0,
151 bed_works: true,
152 coordinate_scale: 1.0,
153 effects: DimensionEffects::default(),
154 fixed_time: None,
155 has_ceiling: false,
156 has_raids: true,
157 has_skylight: true,
158 height: 384,
159 infiniburn: "#minecraft:infiniburn_overworld".into(),
160 logical_height: 384,
161 min_y: -64,
162 monster_spawn_block_light_limit: 0,
163 monster_spawn_light_level: MonsterSpawnLightLevel::Int(7),
164 natural: true,
165 piglin_safe: false,
166 respawn_anchor_works: false,
167 ultrawarm: false,
168 }
169 }
170}
171
172#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default, Debug)]
174pub enum DimensionEffects {
175 #[serde(rename = "minecraft:overworld")]
176 #[default]
177 Overworld,
178 #[serde(rename = "minecraft:the_nether")]
179 TheNether,
180 #[serde(rename = "minecraft:the_end")]
181 TheEnd,
182}
183
184#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)]
185#[serde(untagged)]
186pub enum MonsterSpawnLightLevel {
187 Int(i32),
188 Tagged(MonsterSpawnLightLevelTagged),
189}
190
191#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)]
192#[serde(tag = "type")]
193pub enum MonsterSpawnLightLevelTagged {
194 #[serde(rename = "minecraft:uniform")]
195 Uniform {
196 min_inclusive: i32,
197 max_inclusive: i32,
198 },
199}
200
201impl From<i32> for MonsterSpawnLightLevel {
202 fn from(value: i32) -> Self {
203 Self::Int(value)
204 }
205}