Skip to content

Conversation

Trashtalk217
Copy link
Contributor

@Trashtalk217 Trashtalk217 commented Oct 2, 2025

This is largely identical to #20934, except we store resource data on a ResourceComponent<R: Resource> component on a singleton entity. This has the benefit of sidestepping the annoying double-derive problem of having to both derive Component and Resource for each resource.

For more information check out the original PR description #20934.

Closes #20934, #19711, #17485 and is part of #19731.

Other Changes

Deprecate Components methods

This PR deprecates Components::get_valid_resource_id(type_id: TypeId), and Components::get_resource_id(type_id: TypeId). This is because resources (excluding non-send) are not registered with their TypeId anymore. Instead they're registered by the TypeId of ResourceComponent<SomeResource>. This changes the API as follows:

world.components().get_resource_id(TypeId::of::<SomeResource>())

becomes

world.components().get_resource_id(TypeId::of::<ResourceComponent<SomeResource>>())

This becomes confusing, as the method name suggests we're dealing with resources, while in order to use it correctly, we must already be aware that resources are actually hidden behind a component. The same is true for the get_valid_resource_id method. Both are being deprecated in favour of either resource_id and valid_resource_id for when the type is available, or get_id or get_valid_id when it isn't. Using the latter two does require the user to wrap the type in ResourceComponent<_> in order for it to work correctly.

Registering a Resource Type

When registering a resource type manually through app.register_type::<R>() or through the AppTypeRegistry, a user must wrap the type in ResourceComponent<R> in order for it to be properly registered.

However, when using the untyped API's like get_resource_by_id, the user can simply read::<R>() since ResourceComponent<_> is transparent.

Resources implement MapEntities by default

In order to have MapEntities working with resources, #[derive(Resource)] now automatically implements MapEntities. This makes it such that

#[derive(Resource, MapEntities)]
struct Foo {
    #[entities]
    entity_a: Entity
}

no longer compiles. Instead write:

#[derive(Resource)]
struct Foo {
    #[entities]
    entity_a: Entity
}

@Trashtalk217 Trashtalk217 added C-Feature A new feature, making something new possible A-ECS Entities, components, systems, and events S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Oct 2, 2025
@alice-i-cecile alice-i-cecile self-requested a review October 2, 2025 19:56
@alice-i-cecile alice-i-cecile added the M-Needs-Release-Note Work that should be called out in the blog due to impact label Oct 2, 2025
@janis-bhm
Copy link
Contributor

the MapEntities issue could possibly be fixed by adding a map_entities function to the derive macro like Component has.

Copy link
Contributor

@chescock chescock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooh, this is exciting! I like the ResourceComponent<R: Resource> approach! It looks good overall, and most of my comments are style nits.

I think the world_mut() in the hooks is unsound, though, so we'll need to fix something there.

I'm also curious about the reasons for despawning the whole entity when replacing the resource or in resource_scope. I had been expecting to preserve the entity and just add and remove the component, but I haven't thought through what the tradeoffs are between the two approaches.

@alice-i-cecile alice-i-cecile added S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Oct 4, 2025
@alice-i-cecile alice-i-cecile added this to the 0.18 milestone Oct 4, 2025
Copy link
Contributor

@MichalGniadek MichalGniadek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exciting changes!


/// Type-erased equivalent of [`Components::valid_resource_id()`].
#[inline]
#[deprecated(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. If I'm understanding this PR correctly, the behaviour of this function changes? So if I had a code get_valid_resource_id(resource_type_id) and upgraded bevy, the function call would start returning unexpected results, correct? In that case, I think it would be better to remove this function completely instead of just deprecating it (users will have to change the code anyways).
  2. If I just have a TypeId and don't know the type R, is there still some way for me to check if it's registered? (for example, I could get the type IDs from iterating over the whole TypeRegistry registrations and checking for ReflectResource type data). If not, one option would be to add these function to ReflectResource.

(the same applies to get_resource_id)

Copy link
Contributor Author

@Trashtalk217 Trashtalk217 Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're right, but I'll leave it to a clean-up PR. I really want to start wrapping this up.

Copy link
Contributor

@ElliottjPierce ElliottjPierce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't kept up much with the changes from 0.17, so I did not do a deep dive review for this code. But at a glance, I don't see any major issues with the implementation.

My knowledge here is purely from my experimentation in my own ecs prototypes. At least, I think that's why my review was requested, so I'm reviewing more the concept than the code for now.

Bevy has kind of put itself in a difficult place because we present entities and resources fundamentally differently to users: Res vs Single, etc. From my research, an ideal world would have no distinction: a "resource" is just a component where it just so happens that there can only ever be one of them. Then, its some pretty thin utilities over the top, and you're done.

I haven't been following this feature super closely, but from what I can tell, that's not the direction Bevy wants to take–IIUC, mostly for compatibility reasons. That's why we have to make sure resources don't appear in queries, etc. That is, the hard part here is making sure that a resource entity doesn't happen to also have a Transform component. In my prototype, I see no reason to disallow that, but I can understand the compatibility concerns for Bevy. (Ex: The Bevy way is to have a TargetedEnemy(Option<Entity>) resource, not a ThisEntityIsTargeted resource that also lives on the targeted entity.)

So the question is: how do we have it both ways? How can resources be on entities, but not be treated like entities? I think this pr (again, at a glance) does a pretty good job of walking that line.

My only real concern is that this might make it harder to create and use resources that do not correspond to rust types. That might not be an issues, but IIRC, it was supported before, and it's worth mentioning.

But I don't see anything "bad" about this approach in concept, given the compatibility concerns.

Hopefully that all made sense. There's a lot of moving parts here.

@hukasu
Copy link
Contributor

hukasu commented Oct 4, 2025

Closes #20934, #19711, #17485 and is part of #19731.

writing it like this only links the first issue as being closed by this PR

@Trashtalk217 Trashtalk217 added S-Needs-Review Needs reviewer attention (from anyone!) to move forward S-Needs-Help The author needs help finishing this PR. and removed S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged labels Oct 6, 2025
@Trashtalk217
Copy link
Contributor Author

If anyone can help me out with sound-ifying the resource component hooks, I'd appreciate it. My last commit made an attempt, but it seems to fail in a pretty unexpected way.

@Trashtalk217 Trashtalk217 removed the S-Needs-Help The author needs help finishing this PR. label Oct 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Feature A new feature, making something new possible M-Needs-Release-Note Work that should be called out in the blog due to impact S-Needs-Review Needs reviewer attention (from anyone!) to move forward
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants