amethyst/legion

Failed World::clone_from(), because of missing Duplicate::register_clone(), asserts

singalen opened this issue · 0 comments

This code tries to implement the request from #269 – to deserialize into an existing World and to find out what exactly we have deserialized.

I didn't realize one needs Duplicate::register_clone() or Duplicate::register_copy() in order to copy/clone anything between Worlds.

As a result, the following crashes:

use std::{fs, fmt, io};
use std::ops::Range;
use std::path::Path;
use std::error::Error;
use std::fmt::{Display, Formatter};

use serde::de::DeserializeSeed;

use legion::{Entity, World};
use legion::serialize::Canon;
use legion::world::{Merger, Duplicate, Allocate};

use crate::item::{Item, Headwear};
use crate::components::Render;
use crate::prelude::storage::{Archetype, Components, ArchetypeWriter};


impl Registry {
    
    pub fn new() -> Self {
        let mut result = Self { registry: legion::Registry::<String>::default() };

        result.registry.register::<Render>("render".to_string());
        result.registry.register::<Item>("item".to_string());
        result.registry.register::<Headwear>("headwear".to_string());

        result
    }
    
    fn deser(&self, item_name: &str) -> Result<World, PrefabError> {
        let path = Path::new("data/items").join(item_name).with_extension("json");
        let json = fs::read_to_string(path)?;
        let json_val: serde_json::Value = serde_json::from_str(&json)?;

        let entity_serializer = Canon::default();

        let w = self.registry
            .as_deserialize(&entity_serializer)
            .deserialize(json_val)?;

        Ok(w)
    }

    pub fn load(&self, item_name: &str, world: &mut World) -> Result<Vec<Entity>, PrefabError> {
        struct PrefabMerger {
            pub dup: Duplicate,
            pub entities: Vec<Entity>,
        }
        
        impl Merger for PrefabMerger {
            fn assign_id(&mut self, existing: Entity, allocator: &mut Allocate) -> Entity {
                let id = self.dup.assign_id(existing, allocator);
                self.entities.push(id);
                id
            }

            fn merge_archetype(&mut self, src_entity_range: Range<usize>, src_arch: &Archetype, src_components: &Components, dst: &mut ArchetypeWriter) {
                self.dup.merge_archetype(src_entity_range, src_arch, src_components, dst)
            }
        }
        
        impl PrefabMerger {
            pub fn new() -> Self { 
                Self { dup: Duplicate::default(), entities: vec![] } 
            }
        }

        let mut mirage_world = self.deser(item_name)?;
        let mut merger = PrefabMerger::new();
        world.clone_from(&mut mirage_world, &legion::any(), &mut merger);

        Ok(merger.entities)
    }
}


#[cfg(test)]
mod tests {    
    #[test] 
    fn cask() -> Result<(), PrefabError> {
        let mut world = World::default();
        let reg = Registry::new();
        let entities = reg.load("one_cask", &mut world)?;
        assert_eq!(1, entities.len());
        
        Ok(())
    }
}

It fails with the following stack:

   3: core::panicking::assert_failed
             at /Users/vic/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/panicking.rs:143:5
   4: <legion::internals::insert::ArchetypeWriter as core::ops::drop::Drop>::drop
             at /Users/vic/.cargo/registry/src/github.com-1ecc6299db9ec823/legion-0.4.0/src/internals/insert.rs:113:9
   5: core::ptr::drop_in_place<legion::internals::insert::ArchetypeWriter>
             at /Users/vic/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:192:1
   6: legion::internals::world::World::clone_from
             at /Users/vic/.cargo/registry/src/github.com-1ecc6299db9ec823/legion-0.4.0/src/internals/world.rs:691:9
   7: rifrl::item::prefabs::Registry::load
             at ./src/item/prefabs.rs:103:9
   8: rifrl::item::prefabs::tests::cask
             at ./src/item/prefabs.rs:183:24

in impl Drop for ArchetypeWriter::drop():

        assert_eq!(
            self.claimed.count_ones() as usize,
            self.archetype.layout().component_types().len()
        );

I guess this should not crash, but instead just clone nothing. Or, maybe, it should, but with a more clear error message.