Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add back hot reloading for Scenes #4552

Closed
wants to merge 3 commits into from

Conversation

nicopap
Copy link
Contributor

@nicopap nicopap commented Apr 21, 2022

SceneSpawner refactor

This PR reworks the scene_spawner.rs module in bevy_scene. After
hunting for the bug causing #3759, I determined that it was the
culprit. Having difficulties parsing the code myself, I decided
to refactor it.

Relationship with merging of Scene and DynamicScene

There has been discussion about getting rid of either Scene or
DynamicScene, or merging the two.

This PR does not merge Scene and DynamicScene it only limits
itself to refactoring scene_spawner.rs. However, the changes
introduced will likely make it easier to transition to an
implementation where only a single Scene type exists.

Improvements

  • Merge code in SceneSpawner for managing DynamicScene and Scene.
  • Add documentation to SceneSpawner methods.
  • Add write_to_world method to Scene (does the same as DynamicScene::write_to_world)

Tests

The tests would require manipulating ResMut<Assets<T>>, which I don't
know how to do. So I left them out.

Further cleanup

Removing duplication in API surface

An earlier version of this PR merged the SceneSpawner::dynamic_foo and
SceneSpawner::foo methods by making them generic over the handle they accept.
However, it was judged too burdensome for the user. It required adding type specification
on existing code, since it doesn't play nice with asset_server.load() which introduces
an unbound type variable.

User migration guide

After the second commit, the migration guide consists of:

  • Change all calls to SceneSpawner::despawn to SceneSpawner::despawn_dynamic (note that you should be using SceneBundles and removeing the Handle<Scene> and Handle<DynamicScene> component instead in any case)

Question for reviewers

  • Should we revert the <T: Into<SceneHandle>> change? Now that scenes are mostly added through SceneBundle, there is not as much use of the SceneSpawner API with unbound generic types.

@github-actions github-actions bot added the S-Needs-Triage This issue needs to be labelled label Apr 21, 2022
nicopap added a commit to nicopap/bevy that referenced this pull request Apr 21, 2022
* Merge code in `SceneSpawner` for managing `DynamicScene` and `Scene`.
  * Add back hot reloading for `Scene`s, fixes bevyengine#3759
  * Add ability to despawn `Scene`s
  * Add ability to spawn `DynamicScene` as child of existing entities
* Add documentation to `SceneSpawner` methods.
* Add `write_to_world` method to `Scene` (does the same
  as `DynamicScene::write_to_world`)

This fixes bevyengine#3759 see this commit's merging PR for further details about
this change.

Merge PR: bevyengine#4552
@nicopap nicopap marked this pull request as ready for review April 21, 2022 13:03
@alice-i-cecile alice-i-cecile added A-Scenes Serialized ECS data stored on the disk C-Bug An unexpected or incorrect behavior P-Regression Functionality that used to work but no longer does. Add a test for this! C-Feature A new feature, making something new possible and removed S-Needs-Triage This issue needs to be labelled labels Apr 21, 2022
@nicopap
Copy link
Contributor Author

nicopap commented Apr 22, 2022

I'm going to remove the generic parameter. I don't like it and I think it's a major ergonomic loss. I'll not rebase so that it's possible to revert the removal if deemed appropriate.

Comment on lines +22 to +70
pub fn write_to_world(
&self,
world: &mut World,
entity_map: &mut EntityMap,
) -> Result<(), SceneSpawnError> {
let type_registry = world.resource::<TypeRegistryArc>().clone();
let type_registry = type_registry.read();
for archetype in self.world.archetypes().iter() {
for scene_entity in archetype.entities() {
let entity = entity_map
.entry(*scene_entity)
.or_insert_with(|| world.spawn().id());
for component_id in archetype.components() {
let component_info = self
.world
.components()
.get_info(component_id)
.expect("component_ids in archetypes should have ComponentInfo");

let reflect_component = type_registry
.get(component_info.type_id().unwrap())
.ok_or_else(|| SceneSpawnError::UnregisteredType {
type_name: component_info.name().to_string(),
})
.and_then(|registration| {
registration.data::<ReflectComponent>().ok_or_else(|| {
SceneSpawnError::UnregisteredComponent {
type_name: component_info.name().to_string(),
}
})
})?;
reflect_component.copy_component(&self.world, world, *scene_entity, *entity);
}
}
}
for registration in type_registry.iter() {
if let Some(map_entities_reflect) = registration.data::<ReflectMapEntities>() {
map_entities_reflect
.map_entities(world, entity_map)
.unwrap();
}
}
Ok(())
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note: this was extracted from the SceneSpawner::spawn_sync_internal method. I think it belongs here, the same way the code for instantiating a DynamicScene belongs in dynamic_scene.rs

}

pub fn spawn(&mut self, scene_handle: Handle<Scene>) -> InstanceId {
/// Spawn a dynamic scene. See [`SceneSpawner::spawn`].
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The short doc strings with reference to the relevant Scene implementation is to avoid repeating the same doc string over and over. Since we have here 5 methods that are basically identical with a single DynamicScene, deduplicating the doc helps tremendously.

/// This will only update the world when [`scene_spawner_system`] runs, see
/// [`SceneSpawner::despawn_sync`] for a method with immediate world
/// update.
pub fn despawn(&mut self, scene_handle: Handle<Scene>) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is one of the only API breaking change. Before, this method accepted a Handle<DynamicScene>, which is surprising, given that spawn, spawn_sync and spawn_as_child all accept a Handle<Scene> I think it's justified to change the API to be more consistent. The new equivalent method is despawn_dynamic

nicopap added a commit to nicopap/bevy that referenced this pull request Apr 27, 2022
* Merge code in `SceneSpawner` for managing `DynamicScene` and `Scene`.
  * Add back hot reloading for `Scene`s, fixes bevyengine#3759
  * Add ability to despawn `Scene`s
  * Add ability to spawn `DynamicScene` as child of existing entities
* Add documentation to `SceneSpawner` methods.
* Add `write_to_world` method to `Scene` (does the same
  as `DynamicScene::write_to_world`)

This fixes bevyengine#3759 see this commit's merging PR for further details about
this change.

Merge PR: bevyengine#4552
@alice-i-cecile alice-i-cecile added the M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide label May 2, 2022
@nicopap nicopap marked this pull request as draft June 10, 2022 06:46
* Merge code in `SceneSpawner` for managing `DynamicScene` and `Scene`.
  * Add back hot reloading for `Scene`s, fixes bevyengine#3759
* Add documentation to `SceneSpawner` methods.
* Add `write_to_world` method to `Scene` (does the same
  as `DynamicScene::write_to_world`)

This fixes bevyengine#3759 see this commit's merging PR for further details about
this change.

Merge PR: bevyengine#4552

A previous version of this change merged `SceneSpawner::*{,_dynamic}`,
it made it too difficult to use the `SceneSpawner` so it was reverted.
@nicopap nicopap marked this pull request as ready for review June 18, 2022 15:01
}

#[derive(Debug, Clone)]
struct SpawnCommand {
Copy link
Member

Choose a reason for hiding this comment

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

I think we should avoid "Command" in the name of things that do not impl the Command trait

Copy link
Member

@alice-i-cecile alice-i-cecile left a comment

Choose a reason for hiding this comment

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

The code and doc changes LGTM, and I've verified this works locally.

bors r+

@bors
Copy link
Contributor

bors bot commented Oct 10, 2022

Merge conflict.

@alice-i-cecile alice-i-cecile added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Oct 10, 2022
@alice-i-cecile
Copy link
Member

@nicopap merge conflicts, sorry! Should be simple ones.

@alice-i-cecile
Copy link
Member

[11:29 AM] Gibooonus: I updated the test case I designed in #3759 with more entities, and now it just crashes

Removing the Ready-For-Final-Review label until this is fixed.

@alice-i-cecile alice-i-cecile removed the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Oct 17, 2022
@nicopap nicopap marked this pull request as draft November 4, 2022 11:28
@nicopap
Copy link
Contributor Author

nicopap commented Feb 11, 2023

This will require an entire rewrite soon enough. I'll close it

@nicopap nicopap closed this Feb 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Scenes Serialized ECS data stored on the disk C-Bug An unexpected or incorrect behavior C-Feature A new feature, making something new possible M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide P-Regression Functionality that used to work but no longer does. Add a test for this!
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Scenes do not hot reload
3 participants