Skip to content

Instantly share code, notes, and snippets.

@caspark
Created May 17, 2024 05:48
Show Gist options
  • Save caspark/62a092d3068ffba7781008b2828b5320 to your computer and use it in GitHub Desktop.
Save caspark/62a092d3068ffba7781008b2828b5320 to your computer and use it in GitHub Desktop.
Example of how to clone world in hecs
use hecs::{ColumnBatchBuilder, ColumnBatchType, Component, TypeUnknownToCloner, World};
fn try_add_type_to_batch<T: Component>(
archetype: &hecs::Archetype,
batch_type: &mut ColumnBatchType,
) {
if archetype.has::<T>() {
batch_type.add::<T>();
}
}
fn try_clone_column<T: Component + Clone>(
archetype: &hecs::Archetype,
batch: &mut ColumnBatchBuilder,
) {
if let Some((column, mut writer)) = archetype.get::<&T>().zip(batch.writer::<T>()) {
for item in column.iter() {
if let Err(_) = writer.push(item.clone()) {
unreachable!("push should always succeed since batch was sized to match archetype");
}
}
}
}
fn try_copy_column<T: Component + Copy>(
archetype: &hecs::Archetype,
batch: &mut ColumnBatchBuilder,
) {
if let Some((column, mut writer)) = archetype.get::<&T>().zip(batch.writer::<T>()) {
//FIXME would want to add writer.extend() to add column in batch
for item in column.iter() {
if let Err(_) = writer.push(*item) {
unreachable!("push should always succeed since batch was sized to match archetype");
}
}
}
}
fn clone_world(world: &World) -> Result<World, TypeUnknownToCloner> {
let mut cloned = World::new();
//FIXME need to have a way to update `cloned`'s entity allocater state to match that of `world`
// (or implement this as World::try_clone(), or accept entity ids can be different)
for archetype in world.archetypes() {
let mut batch = ColumnBatchType::new();
// types have to be listed one by one here
try_add_type_to_batch::<String>(archetype, &mut batch);
try_add_type_to_batch::<i32>(archetype, &mut batch);
let mut batch = batch.into_batch(archetype.ids().len() as u32);
//FIXME and types need to be listed again here. Seems like this requires a macro to DRY?
// (which then means we can't have entity ids be the same... unless we rework this
// function to accept a struct for building batch type)
try_clone_column::<String>(archetype, &mut batch);
try_copy_column::<i32>(archetype, &mut batch);
let batch = batch.build().expect("batch should be complete");
let handles = &cloned
.reserve_entities(archetype.ids().len() as u32)
.collect::<Vec<_>>();
cloned.flush();
cloned.spawn_column_batch_at(handles, batch);
}
Ok(cloned)
}
pub fn main() {
let int0 = 0;
let int1 = 1;
let str0 = "Ada".to_owned();
let str1 = "Bob".to_owned();
let mut world0 = World::new();
let entity0 = world0.spawn((int0, str0));
let entity1 = world0.spawn((int1, str1));
let world1 = clone_world(&world0).expect("clone should succeed");
assert_eq!(
world0.len(),
world1.len(),
"cloned world should have same entity count as original world"
);
type AllComponentsQuery = (&'static i32, &'static String);
for entity in [entity0, entity1] {
let w0_e = world0.entity(entity).expect("w0 entity should exist");
let w1_e = world1.entity(entity).expect("w1 entity should exist");
assert!(w0_e.satisfies::<AllComponentsQuery>());
assert!(w1_e.satisfies::<AllComponentsQuery>());
assert_eq!(
w0_e.query::<AllComponentsQuery>().get().unwrap(),
w1_e.query::<AllComponentsQuery>().get().unwrap()
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment