1#![forbid(unsafe_code)]
24#![allow(missing_docs)]
25#![allow(clippy::doc_lazy_continuation)]
26#![allow(clippy::mutable_key_type)]
27
28use crate::{
29 core::{
30 parking_lot::MutexGuard,
31 reflect::prelude::*,
32 uuid::{uuid, Uuid},
33 visitor::prelude::*,
34 TypeUuidProvider,
35 },
36 state::ResourceState,
37 untyped::UntypedResource,
38};
39use fxhash::FxHashSet;
40use std::{
41 error::Error,
42 fmt::{Debug, Formatter},
43 future::Future,
44 hash::{Hash, Hasher},
45 marker::PhantomData,
46 ops::{Deref, DerefMut},
47 path::Path,
48 pin::Pin,
49 task::{Context, Poll},
50};
51
52use crate::state::LoadError;
53use crate::untyped::{ResourceHeader, ResourceKind};
54pub use fyrox_core as core;
55use fyrox_core::log::Log;
56use fyrox_core::{combine_uuids, Downcast};
57
58pub mod constructor;
59pub mod entry;
60pub mod event;
61pub mod graph;
62pub mod io;
63pub mod loader;
64pub mod manager;
65pub mod options;
66pub mod state;
67pub mod untyped;
68
69pub const TEXTURE_RESOURCE_UUID: Uuid = uuid!("02c23a44-55fa-411a-bc39-eb7a5eadf15c");
71pub const MODEL_RESOURCE_UUID: Uuid = uuid!("44cd768f-b4ca-4804-a98c-0adf85577ada");
73pub const SOUND_BUFFER_RESOURCE_UUID: Uuid = uuid!("f6a077b7-c8ff-4473-a95b-0289441ea9d8");
75pub const SHADER_RESOURCE_UUID: Uuid = uuid!("f1346417-b726-492a-b80f-c02096c6c019");
77pub const CURVE_RESOURCE_UUID: Uuid = uuid!("f28b949f-28a2-4b68-9089-59c234f58b6b");
79
80pub trait ResourceData: Downcast + Debug + Visit + Send + Reflect {
82 fn type_uuid(&self) -> Uuid;
84
85 fn save(&mut self, #[allow(unused_variables)] path: &Path) -> Result<(), Box<dyn Error>>;
92
93 fn can_be_saved(&self) -> bool;
97}
98
99pub trait TypedResourceData: ResourceData + Default + TypeUuidProvider {}
104
105impl<T> TypedResourceData for T where T: ResourceData + Default + TypeUuidProvider {}
106
107pub trait ResourceLoadError: 'static + Debug + Send + Sync {}
109
110impl<T> ResourceLoadError for T where T: 'static + Debug + Send + Sync {}
111
112pub struct ResourceHeaderGuard<'a, T>
114where
115 T: TypedResourceData,
116{
117 guard: MutexGuard<'a, ResourceHeader>,
118 phantom: PhantomData<T>,
119}
120
121impl<T> ResourceHeaderGuard<'_, T>
122where
123 T: TypedResourceData,
124{
125 pub fn kind(&self) -> &ResourceKind {
126 &self.guard.kind
127 }
128
129 pub fn data(&mut self) -> Option<&mut T> {
130 if let ResourceState::Ok(ref mut data) = self.guard.state {
131 Downcast::as_any_mut(&mut **data).downcast_mut::<T>()
132 } else {
133 None
134 }
135 }
136
137 pub fn data_ref(&self) -> Option<&T> {
138 if let ResourceState::Ok(ref data) = self.guard.state {
139 Downcast::as_any(&**data).downcast_ref::<T>()
140 } else {
141 None
142 }
143 }
144}
145
146#[derive(Debug, Reflect)]
153pub struct Resource<T>
154where
155 T: TypedResourceData,
156{
157 untyped: UntypedResource,
158 #[reflect(hidden)]
159 phantom: PhantomData<T>,
160}
161
162impl<T: TypedResourceData> TypeUuidProvider for Resource<T> {
163 fn type_uuid() -> Uuid {
164 combine_uuids(
165 uuid!("790b1a1c-a997-46c4-ac3b-8565501f0052"),
166 <T as TypeUuidProvider>::type_uuid(),
167 )
168 }
169}
170
171impl<T> Visit for Resource<T>
172where
173 T: TypedResourceData,
174{
175 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
176 if visitor.is_reading() {
181 let mut untyped = UntypedResource::default();
182 if untyped.visit(name, visitor).is_ok() {
183 let untyped_data_type = untyped.type_uuid();
184 if untyped_data_type == <T as TypeUuidProvider>::type_uuid() {
185 self.untyped = untyped;
186 return Ok(());
187 } else {
188 Log::err(format!(
189 "Unable to deserialize untyped resource into its typed \
190 version, because types do not match! Untyped resource has \
191 {untyped_data_type} type, but the required type is {}",
192 <T as TypeUuidProvider>::type_uuid(),
193 ))
194 }
195 }
196 }
197
198 let mut region = visitor.enter_region(name)?;
199
200 if region.is_reading() {
202 let mut old_option_wrapper: Option<UntypedResource> = None;
203 if old_option_wrapper.visit("State", &mut region).is_ok() {
204 self.untyped = old_option_wrapper.unwrap();
205 } else {
206 self.untyped.visit("State", &mut region)?;
207 }
208 } else {
209 self.untyped.visit("State", &mut region)?;
210 }
211
212 Ok(())
213 }
214}
215
216impl<T> PartialEq for Resource<T>
217where
218 T: TypedResourceData,
219{
220 fn eq(&self, other: &Self) -> bool {
221 self.untyped == other.untyped
222 }
223}
224
225impl<T> Eq for Resource<T> where T: TypedResourceData {}
226
227impl<T> Hash for Resource<T>
228where
229 T: TypedResourceData,
230{
231 fn hash<H: Hasher>(&self, state: &mut H) {
232 self.untyped.hash(state)
233 }
234}
235
236impl<T> Resource<T>
237where
238 T: TypedResourceData,
239{
240 #[inline]
242 pub fn new_pending(kind: ResourceKind) -> Self {
243 Self {
244 untyped: UntypedResource::new_pending(kind, <T as TypeUuidProvider>::type_uuid()),
245 phantom: PhantomData,
246 }
247 }
248
249 #[inline]
251 pub fn new_ok(kind: ResourceKind, data: T) -> Self {
252 Self {
253 untyped: UntypedResource::new_ok(kind, data),
254 phantom: PhantomData,
255 }
256 }
257
258 #[inline]
260 pub fn new_load_error(kind: ResourceKind, error: LoadError) -> Self {
261 Self {
262 untyped: UntypedResource::new_load_error(
263 kind,
264 error,
265 <T as TypeUuidProvider>::type_uuid(),
266 ),
267 phantom: PhantomData,
268 }
269 }
270
271 #[inline]
273 pub fn into_untyped(self) -> UntypedResource {
274 self.untyped
275 }
276
277 #[inline]
279 pub fn state(&self) -> ResourceHeaderGuard<'_, T> {
280 let guard = self.untyped.0.lock();
281 ResourceHeaderGuard {
282 guard,
283 phantom: Default::default(),
284 }
285 }
286
287 #[inline]
289 pub fn try_acquire_state(&self) -> Option<ResourceHeaderGuard<'_, T>> {
290 self.untyped.0.try_lock().map(|guard| ResourceHeaderGuard {
291 guard,
292 phantom: Default::default(),
293 })
294 }
295
296 #[inline]
297 pub fn header(&self) -> MutexGuard<'_, ResourceHeader> {
298 self.untyped.0.lock()
299 }
300
301 #[inline]
303 pub fn is_loading(&self) -> bool {
304 matches!(self.untyped.0.lock().state, ResourceState::Pending { .. })
305 }
306
307 #[inline]
309 pub fn is_ok(&self) -> bool {
310 matches!(self.untyped.0.lock().state, ResourceState::Ok(_))
311 }
312
313 #[inline]
315 pub fn is_failed_to_load(&self) -> bool {
316 matches!(self.untyped.0.lock().state, ResourceState::LoadError { .. })
317 }
318
319 #[inline]
321 pub fn use_count(&self) -> usize {
322 self.untyped.use_count()
323 }
324
325 #[inline]
327 pub fn key(&self) -> u64 {
328 self.untyped.key() as u64
329 }
330
331 #[inline]
333 pub fn kind(&self) -> ResourceKind {
334 self.untyped.kind()
335 }
336
337 #[inline]
339 pub fn set_path(&mut self, new_kind: ResourceKind) {
340 self.untyped.set_kind(new_kind);
341 }
342
343 #[inline]
353 pub fn data_ref(&self) -> ResourceDataRef<'_, T> {
354 ResourceDataRef {
355 guard: self.untyped.0.lock(),
356 phantom: Default::default(),
357 }
358 }
359
360 pub fn save(&self, path: &Path) -> Result<(), Box<dyn Error>> {
362 self.untyped.save(path)
363 }
364
365 pub fn save_back(&self) -> Result<(), Box<dyn Error>> {
368 self.untyped.save_back()
369 }
370}
371
372impl<T> Default for Resource<T>
373where
374 T: TypedResourceData,
375{
376 #[inline]
377 fn default() -> Self {
378 Self {
379 untyped: UntypedResource::new_ok(Default::default(), T::default()),
380 phantom: Default::default(),
381 }
382 }
383}
384
385impl<T> Clone for Resource<T>
386where
387 T: TypedResourceData,
388{
389 #[inline]
390 fn clone(&self) -> Self {
391 Self {
392 untyped: self.untyped.clone(),
393 phantom: Default::default(),
394 }
395 }
396}
397
398impl<T> From<UntypedResource> for Resource<T>
399where
400 T: TypedResourceData,
401{
402 #[inline]
403 fn from(untyped: UntypedResource) -> Self {
404 assert_eq!(untyped.type_uuid(), <T as TypeUuidProvider>::type_uuid());
405 Self {
406 untyped,
407 phantom: Default::default(),
408 }
409 }
410}
411
412#[allow(clippy::from_over_into)]
413impl<T> Into<UntypedResource> for Resource<T>
414where
415 T: TypedResourceData,
416{
417 #[inline]
418 fn into(self) -> UntypedResource {
419 self.untyped
420 }
421}
422
423impl<T> Future for Resource<T>
424where
425 T: TypedResourceData,
426{
427 type Output = Result<Self, LoadError>;
428
429 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
430 let mut inner = self.untyped.clone();
431 Pin::new(&mut inner)
432 .poll(cx)
433 .map(|r| r.map(|_| self.clone()))
434 }
435}
436
437#[doc(hidden)]
438pub struct ResourceDataRef<'a, T>
439where
440 T: TypedResourceData,
441{
442 guard: MutexGuard<'a, ResourceHeader>,
443 phantom: PhantomData<T>,
444}
445
446impl<T> ResourceDataRef<'_, T>
447where
448 T: TypedResourceData,
449{
450 #[inline]
451 pub fn as_loaded_ref(&self) -> Option<&T> {
452 match self.guard.state {
453 ResourceState::Ok(ref data) => Downcast::as_any(&**data).downcast_ref(),
454 _ => None,
455 }
456 }
457
458 #[inline]
459 pub fn as_loaded_mut(&mut self) -> Option<&mut T> {
460 match self.guard.state {
461 ResourceState::Ok(ref mut data) => Downcast::as_any_mut(&mut **data).downcast_mut(),
462 _ => None,
463 }
464 }
465}
466
467impl<T> Debug for ResourceDataRef<'_, T>
468where
469 T: TypedResourceData,
470{
471 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
472 match self.guard.state {
473 ResourceState::Pending { .. } => {
474 write!(
475 f,
476 "Attempt to get reference to resource data while it is not loaded! Path is {}",
477 self.guard.kind
478 )
479 }
480 ResourceState::LoadError { .. } => {
481 write!(
482 f,
483 "Attempt to get reference to resource data which failed to load! Path is {}",
484 self.guard.kind
485 )
486 }
487 ResourceState::Ok(ref data) => data.fmt(f),
488 }
489 }
490}
491
492impl<T> Deref for ResourceDataRef<'_, T>
493where
494 T: TypedResourceData,
495{
496 type Target = T;
497
498 fn deref(&self) -> &Self::Target {
499 match self.guard.state {
500 ResourceState::Pending { .. } => {
501 panic!(
502 "Attempt to get reference to resource data while it is not loaded! Path is {}",
503 self.guard.kind
504 )
505 }
506 ResourceState::LoadError { .. } => {
507 panic!(
508 "Attempt to get reference to resource data which failed to load! Path is {}",
509 self.guard.kind
510 )
511 }
512 ResourceState::Ok(ref data) => Downcast::as_any(&**data)
513 .downcast_ref()
514 .expect("Type mismatch!"),
515 }
516 }
517}
518
519impl<T> DerefMut for ResourceDataRef<'_, T>
520where
521 T: TypedResourceData,
522{
523 fn deref_mut(&mut self) -> &mut Self::Target {
524 let header = &mut *self.guard;
525 match header.state {
526 ResourceState::Pending { .. } => {
527 panic!(
528 "Attempt to get reference to resource data while it is not loaded! Path is {}",
529 header.kind
530 )
531 }
532 ResourceState::LoadError { .. } => {
533 panic!(
534 "Attempt to get reference to resource data which failed to load! Path is {}",
535 header.kind
536 )
537 }
538 ResourceState::Ok(ref mut data) => Downcast::as_any_mut(&mut **data)
539 .downcast_mut()
540 .expect("Type mismatch!"),
541 }
542 }
543}
544
545pub fn collect_used_resources(
550 entity: &dyn Reflect,
551 resources_collection: &mut FxHashSet<UntypedResource>,
552) {
553 #[inline(always)]
554 fn type_is<T: Reflect>(entity: &dyn Reflect) -> bool {
555 let mut types_match = false;
556 entity.downcast_ref::<T>(&mut |v| {
557 types_match = v.is_some();
558 });
559 types_match
560 }
561
562 let mut finished = type_is::<Vec<u8>>(entity)
566 || type_is::<Vec<u16>>(entity)
567 || type_is::<Vec<u32>>(entity)
568 || type_is::<Vec<u64>>(entity)
569 || type_is::<Vec<i8>>(entity)
570 || type_is::<Vec<i16>>(entity)
571 || type_is::<Vec<i32>>(entity)
572 || type_is::<Vec<i64>>(entity)
573 || type_is::<Vec<f32>>(entity)
574 || type_is::<Vec<f64>>(entity);
575
576 if finished {
577 return;
578 }
579
580 entity.downcast_ref::<UntypedResource>(&mut |v| {
581 if let Some(resource) = v {
582 resources_collection.insert(resource.clone());
583 finished = true;
584 }
585 });
586
587 if finished {
588 return;
589 }
590
591 entity.as_array(&mut |array| {
592 if let Some(array) = array {
593 for i in 0..array.reflect_len() {
594 if let Some(item) = array.reflect_index(i) {
595 collect_used_resources(item, resources_collection)
596 }
597 }
598
599 finished = true;
600 }
601 });
602
603 if finished {
604 return;
605 }
606
607 entity.as_inheritable_variable(&mut |inheritable| {
608 if let Some(inheritable) = inheritable {
609 collect_used_resources(inheritable.inner_value_ref(), resources_collection);
610
611 finished = true;
612 }
613 });
614
615 if finished {
616 return;
617 }
618
619 entity.as_hash_map(&mut |hash_map| {
620 if let Some(hash_map) = hash_map {
621 for i in 0..hash_map.reflect_len() {
622 if let Some((key, value)) = hash_map.reflect_get_at(i) {
623 collect_used_resources(key, resources_collection);
624 collect_used_resources(value, resources_collection);
625 }
626 }
627
628 finished = true;
629 }
630 });
631
632 if finished {
633 return;
634 }
635
636 entity.fields(&mut |fields| {
637 for field in fields {
638 collect_used_resources(*field, resources_collection);
639 }
640 })
641}