1use std::{sync::Arc, time::Duration};
2
3use bevy::{
4 app::PluginGroupBuilder,
5 asset::{AssetPlugin, ChangeWatcher},
6 prelude::*,
7 tasks::tick_global_task_pools_on_main_thread,
8 time::TimePlugin,
9};
10use inbuilt::*;
11
12pub type UpdateFn = extern "C" fn(data: *const InbuiltEntityData, len: usize);
13pub type CreateFn = extern "C" fn(data: *const InbuiltEntityData, len: usize);
14pub type DestroyFn = extern "C" fn(entity: *const UnityEntity, len: usize);
15
16#[derive(Component, Copy, Clone)]
17pub struct InstantiateEntity {
18 pub entity: UnityEntity,
19}
20
21#[derive(Component, Copy, Clone)]
22pub struct DestroyEntity {
23 pub entity: UnityEntity,
24}
25
26#[derive(Component)]
27pub struct PrefabComponent {
28 pub ref_id: i32,
29 pub guid: UnityEntity,
30}
31
32#[repr(C)]
33pub struct PrefabData {
34 pub ref_id: i32,
35 pub guids: *mut UnityEntity,
36 pub len: usize,
37}
38
39#[derive(Resource)]
40pub struct CallbacksNonSend {
41 create_fn: Arc<CreateFn>,
42 update_fn: Arc<UpdateFn>,
43 destroy_fn: Arc<DestroyFn>,
44}
45
46pub(crate) struct UnityPlugins {
47 base_path: String,
48 update_fn: Arc<UpdateFn>,
49 create_fn: Arc<CreateFn>,
50 destroy_fn: Arc<DestroyFn>,
51}
52
53impl UnityPlugins {
54 pub(crate) fn new(
55 base_path: String,
56 create_fn: CreateFn,
57 update_fn: UpdateFn,
58 destroy_fn: DestroyFn,
59 ) -> UnityPlugins {
60 UnityPlugins {
61 base_path,
62 update_fn: Arc::new(update_fn),
63 create_fn: Arc::new(create_fn),
64 destroy_fn: Arc::new(destroy_fn),
65 }
66 }
67}
68
69impl Plugin for UnityPlugins {
70 fn build(&self, app: &mut App) {
71 let update_fn = self.update_fn.clone();
72 let create_fn = self.create_fn.clone();
73 let destroy_fn = self.destroy_fn.clone();
74 app.add_plugins(BasePlugins)
75 .add_plugins(AssetPlugin {
76 asset_folder: self.base_path.clone(),
77 watch_for_changes: ChangeWatcher::with_delay(Duration::from_millis(200)),
78 })
79 .insert_non_send_resource(CallbacksNonSend {
80 create_fn,
81 update_fn,
82 destroy_fn,
83 })
84 .add_systems(
85 PostUpdate,
86 (
87 create_unity_system,
88 update_unity_system,
89 destroy_unity_system,
90 ),
91 );
92 }
93}
94
95fn create_unity_system(
96 callbacks_res: NonSend<CallbacksNonSend>,
97 mut commands: Commands,
98 created: Query<(Entity, &InstantiateEntity, &Transform)>,
99) {
100 let mut creates = vec![];
101 let mut counts = vec![];
102 created.iter().for_each(|(_, _, transform)| {
103 let mut count = 0;
104 creates.push(InbuiltData {
105 ty: InbuiltTypes::UnityTransform,
106 value: InbuiltComponents {
107 UnityTransform: transform.into(),
108 },
109 });
110
111 count += 1;
112 counts.push(count);
113 });
114
115 let mut count = 0;
116 let creates = created
117 .iter()
118 .enumerate()
119 .map(|(index, (entity, guid, _))| {
120 let comp_count = counts[index];
121 let ptr = creates[count..(count + comp_count)].as_ptr();
122 count += comp_count;
123
124 let mut comm = commands.entity(entity);
125 comm.despawn();
126
127 InbuiltEntityData {
128 entity: guid.entity,
129 data: ptr,
130 len: comp_count,
131 }
132 })
133 .collect::<Vec<InbuiltEntityData>>();
134
135 let ptr = creates.as_ptr();
136
137 (callbacks_res.create_fn)(ptr, creates.len());
138}
139
140fn update_unity_system(
141 callbacks_res: NonSend<CallbacksNonSend>,
142 entities: Query<(&UnityEntity, &GlobalTransform), Changed<GlobalTransform>>, ) {
144 let mut updates = vec![];
145 let mut counts = vec![];
146 entities.iter().for_each(|(_, transform)| {
147 let mut count = 0;
148 updates.push(InbuiltData {
149 ty: InbuiltTypes::UnityTransform,
150 value: InbuiltComponents {
151 UnityTransform: transform.into(),
152 },
153 });
154
155 count += 1;
156 counts.push(count);
157 });
158
159 let mut count = 0;
160 let updates = entities
161 .iter()
162 .enumerate()
163 .map(|(index, (unity_entity, _))| {
164 let comp_count = counts[index];
165 let ptr = updates[count..(count + comp_count)].as_ptr();
166 count += comp_count;
167
168 InbuiltEntityData {
169 entity: *unity_entity,
170 data: ptr,
171 len: comp_count,
172 }
173 })
174 .collect::<Vec<InbuiltEntityData>>();
175
176 let ptr = updates.as_ptr();
177 (callbacks_res.update_fn)(ptr, updates.len());
178}
179
180fn destroy_unity_system(
181 mut commands: Commands,
182 callbacks_res: NonSend<CallbacksNonSend>,
183 destroyed: Query<(Entity, &DestroyEntity)>,
184) {
185 let destroyed_entities = destroyed
186 .iter()
187 .map(|(entity, v)| {
188 let mut comm = commands.entity(entity);
189 comm.despawn();
190
191 v.entity
192 })
193 .collect::<Vec<UnityEntity>>();
194
195 (callbacks_res.destroy_fn)(destroyed_entities.as_ptr(), destroyed_entities.len());
196}
197
198pub(crate) fn start_app(app: &mut App) {
199 while !app.ready() {
201 #[cfg(not(target_arch = "wasm32"))]
202 tick_global_task_pools_on_main_thread();
203 }
204 app.finish();
205 app.cleanup();
206}
207
208struct BasePlugins;
209
210impl PluginGroup for BasePlugins {
211 fn build(self) -> PluginGroupBuilder {
212 PluginGroupBuilder::start::<Self>()
213 .add(TaskPoolPlugin::default())
214 .add(TypeRegistrationPlugin)
215 .add(FrameCountPlugin)
216 .add(TransformPlugin)
217 .add(HierarchyPlugin)
218 .add(TimePlugin)
219 }
220}