1use crate::components::*;
5use crate::reflect::Field;
6use kvas_ecs::{Entity, World};
7use serde_json::Value;
8
9pub struct ComponentInfo {
12 pub name: &'static str,
14 pub removable: bool,
16 pub register: fn(&mut World),
18 pub add: fn(&mut World, Entity),
20 pub remove: fn(&mut World, Entity),
22 pub has: fn(&World, Entity) -> bool,
24 pub to_json: fn(&World, Entity) -> Option<Value>,
26 pub from_json: fn(&mut World, Entity, Value) -> Result<(), String>,
28 pub validate: fn(&World, Entity) -> Result<(), String>,
31 pub edit: for<'a> fn(&'a mut World, Entity, &mut dyn FnMut(Vec<Field<'a>>)),
33}
34
35fn info<T: Component>() -> ComponentInfo {
36 ComponentInfo {
37 name: <T as kvas_ecs::Component>::NAME,
38 removable: T::REMOVABLE,
39 register: |world| world.register::<T>(),
40 add: |w, e| w.insert(e, T::default()),
41 remove: |w, e| {
42 w.remove::<T>(e);
43 },
44 has: |w, e| w.has::<T>(e),
45 to_json: |w, e| w.get::<T>(e).and_then(|c| serde_json::to_value(c).ok()),
46 from_json: |w, e, v| {
47 w.insert(e, serde_json::from_value::<T>(v).map_err(|err| format!("{}: {err}", <T as kvas_ecs::Component>::NAME))?);
48 Ok(())
49 },
50 validate: |world, entity| {
51 let Some(component) = world.get::<T>(entity) else {
52 return Ok(());
53 };
54 let json = serde_json::to_value(component).map_err(|e| e.to_string())?;
55 serde_json::from_value::<T>(json).map(|_| ()).map_err(|e| format!("{}: {e}", <T as kvas_ecs::Component>::NAME))
56 },
57 edit: |w, e, f| {
58 if let Some(c) = w.get_mut::<T>(e) {
59 f(c.fields());
60 }
61 },
62 }
63}
64
65pub struct Registry {
68 pub components: Vec<ComponentInfo>,
70}
71
72impl Default for Registry {
73 fn default() -> Self {
74 Self {
75 components: vec![
76 info::<Name>(),
77 info::<Tags>(),
78 info::<UiElement>(),
79 info::<Lifetime>(),
80 info::<Visibility>(),
81 info::<MoveTween>(),
82 info::<TransformAnimation>(),
83 info::<NeuralAgent>(),
84 info::<Transform>(),
85 info::<MeshRenderer>(),
86 info::<EditableMesh>(),
87 info::<Shape>(),
88 info::<Camera>(),
89 info::<Light>(),
90 info::<RigidBody>(),
91 info::<Collider>(),
92 info::<Joint>(),
93 info::<Scripts>(),
94 info::<Spin>(),
95 info::<Bob>(),
96 info::<Pulse>(),
97 info::<Wobble>(),
98 info::<ColorCycle>(),
99 info::<Cloth>(),
100 info::<Grass>(),
101 info::<Tree>(),
102 info::<Decal>(),
103 info::<VoxelSmoke>(),
104 info::<VoxelClouds>(),
105 info::<VoxelWater>(),
106 info::<Switch>(),
107 info::<Slider>(),
108 info::<AudioSource>(),
109 info::<MeshModifiers>(),
110 info::<TexturePaint>(),
111 info::<ParticleEmitter>(),
112 info::<Animator>(),
113 info::<CharacterIk>(),
114 info::<Ragdoll>(),
115 ],
116 }
117 }
118}
119
120impl Registry {
121 pub fn register_world(&self, world: &mut World) {
124 for component in &self.components { (component.register)(world); }
125 }
126
127 pub fn get(&self, name: &str) -> Option<&ComponentInfo> {
129 self.components.iter().find(|c| c.name == name)
130 }
131
132 pub fn register<T: Component>(&mut self, world: &mut World) {
136 world.register::<T>();
137 let name = <T as kvas_ecs::Component>::NAME;
138 if self.get(name).is_none() {
139 self.components.push(info::<T>());
140 }
141 }
142
143 pub fn of_entity<'a>(&'a self, world: &World, entity: Entity) -> Vec<&'a ComponentInfo> {
145 self.components.iter().filter(|c| (c.has)(world, entity)).collect()
146 }
147
148 pub fn validate_entity(&self, world: &World, entity: Entity) -> Vec<String> {
151 if !world.is_alive(entity) {
152 return vec!["entity is not alive".into()];
153 }
154 self.components.iter().filter_map(|component| (component.validate)(world, entity).err()).collect()
155 }
156
157 pub fn add_named(&self, world: &mut World, entity: Entity, name: &str) -> Result<bool, String> {
160 if !world.is_alive(entity) {
161 return Err("entity is not alive".into());
162 }
163 let info = self.get(name).ok_or_else(|| format!("unknown component '{name}'"))?;
164 if (info.has)(world, entity) {
165 return Ok(false);
166 }
167 (info.add)(world, entity);
168 Ok(true)
169 }
170
171 pub fn remove_named(&self, world: &mut World, entity: Entity, name: &str) -> Result<bool, String> {
174 if !world.is_alive(entity) {
175 return Err("entity is not alive".into());
176 }
177 let info = self.get(name).ok_or_else(|| format!("unknown component '{name}'"))?;
178 if !info.removable {
179 return Err(format!("component '{name}' cannot be removed"));
180 }
181 let present = (info.has)(world, entity);
182 (info.remove)(world, entity);
183 Ok(present)
184 }
185
186 pub fn copy_entity(&self, world: &mut World, from: Entity, to: Entity) {
188 for c in &self.components {
189 if let Some(v) = (c.to_json)(world, from) {
190 let _ = (c.from_json)(world, to, v);
191 }
192 }
193 }
194}