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
Question: Can I infer the size of the component type when visiting storage pools? #827
Comments
Quite the opposite, this is possible since v3.9, that is the last version available upstream. |
@skypjack Thanks for the reply! Hmmm... I am actually using v3.9! But if I make a basic registry I cannot call Back to my question, I am very sorry but I think I do need more details. I assume that the type info would be what I get by |
🤔 As for your question, the type info can help you to find a meta type with which you can elaborate the |
If What I am trying to do is to iterate all the component pools and read the byte data directly without manually registering this code per component type. I am still experimenting but I have two concrete goals right now: Compute a checksum for all existing components and copy entities from one registry to the other without knowing upfront which components where registered. I can elaborate on why I want to do the later, but I think that goes beyond the specific question. I am not very familiar with reflection on C++, is there any resource you'd recommend to get me started? Thank you! |
Uhm... is it the single include file? Maybe it's out-of-date? Otherwise, the
To copy the storage, at the moment you can iterate the pool, then
If this sounds like something that could work for you, I can prepare a super small example if you like. Let me know. 👋 |
This is certainly of interest to me, so I would very much appreciate an example! |
Sorry, I've been busy today and I haven't found the time to pack a proper example. |
This does let me move forward, thank you very much for the example! Purely for academic interest, what I was hoping for is a way to do this without having to upfront register my components for this, as you do in your example ( I have not looked into my issues running the untyped storage, but if I find it I will let you know! Thanks! |
Well, technically speaking it's possible already as: for(auto [id, storage]: src_registry.storage()) {
auto &&other = dst_registry.storage(storage.type());
for(auto entity: storage) {
other.emplace(entity, storage.get(entity);
}
} Morever, as I said before, I'm working on something to make it a single line instruction. However, this is only meant to work across storage classes and not across registries. |
Hey! So that snippet looks exactly like what I need at this moment (still experimenting!). While looking at it, though, I had to figure out why I cannot access The commit that adds that method was done the next day! :P |
🤔 Wait a moment. This is from v3.9: [[nodiscard]] auto storage() ENTT_NOEXCEPT {
return iterable_adaptor{internal::storage_proxy_iterator{pools.begin()}, internal::storage_proxy_iterator{pools.end()}};
} That is, this is possible with v3.9 already: for(auto [id, storage]: src_registry.storage()) {
// ...
} I really don't know why you don't see it but, well, it's there. 😅 |
😲 Oh, this I don't know actually, I've always referenced the git tag. What's wrong there? The single include file or the tarball? EDIT The commit points to this version of the registry and the |
Hello! A few updates on this. Finally got around field testing these ideas. First is that your example above needs some tweaks: Your example:
My (compiling) code:
I had to use Another issue I discovered is that if the target registry has not had the component type initialized this will result in null memory access. I think I can deal with this, though. |
|
Thank you @chengts95! Any recommendations on how to make sure the storage has the pool, sort of adding/removing a component just to be sure? I would like to use |
To put storage in the pool, you have to use a template because C++ types don't exist at runtime. |
Thank you for the reply. I understand that. My question is what would be a good way to create the pool on the target registry (yes using type specific templates) if no entity on the target registry has received the component yet. Currently, to get around this I am adding and removing a component of the type on a fake entity, but I wonder if there is a call for such purpose, along the lines of |
This is what I am settling with for now:
The rest (the transfer part) is as above, and as a proof of concept this works pretty well. |
Yeah, the non-template for (auto [id, source]: registry.storage()) {
auto& target = registry.vtable_for(source.type().hash()).emplace(other_registry);
// ...
} Though, take this with a grain of salt at the moment, I really don't know how it will look like (and any suggestions is welcome in this sense 🙂). |
Thank you, @skypjack. Looking forward to that! Any recommended alternatives to my hack above in the mean time? |
@Ramito |
@skypjack but then if I don't do anything with it I get a no-discard error, since it gets compiled out. I think it'd be nice to have a call to just ensure it's there without any intention to sue it at the time. |
Yeah, I want to remove the |
😁 Thanks! |
Maybe that is true, a vtable for constructing storage by type id. I believe the main problem is to
void* registry::emplace_storage(const id_type id , function_ptr storage_constructor) {
auto &&cpool = pools[id];
if(!cpool) {
cpool.reset( storage_constructor());
cpool->bind(forward_as_any(*this));
}
return cpool.get();
}
|
This is how I implement the storage construction void copyReg(const entt::registry ®istry, entt::registry &other)
{
other.assign(registry.data(), registry.data() + registry.size(), registry.released());
for (const auto [id, storage] : registry.storage())
{
auto newstorage = registry.construct_storage(id);
if (newstorage)
{
auto &&dst = other.emplace_or_replace_storage(id, newstorage);
if (dst)
{
for (auto &&i : storage)
{
dst->emplace(i, storage.get(i)); //i have to use this way because insert missed some entities.
}
}
}
}
}
//registry.hpp
//...
//private:
// dense_hash_map<id_type, base_type *(*)(), identity> vtable;
base_type *emplace_or_replace_storage(const id_type id, base_type *storage) {
auto &&cpool = pools[id];
// if(!cpool) {
cpool.reset(storage);
cpool->bind(forward_as_any(*this));
//}
return cpool.get();
}
base_type *construct_storage(const id_type id) const {
const auto f = vtable.find(id);
if(f != vtable.end()) {
auto func = f->second;
return func();
}
return nullptr;
}
template<typename Component>
[[nodiscard]] auto &assure(const id_type id = type_hash<Component>::value()) {
static_assert(std::is_same_v<Component, std::decay_t<Component>>, "Non-decayed types not allowed");
auto &&cpool = pools[id];
if(!cpool) {
vtable[id] = []() { return static_cast<base_type *>(new storage_type<Component>{}); };
cpool.reset(new storage_type<Component>{});
cpool->bind(forward_as_any(*this));
}
return static_cast<storage_type<Component> &>(*cpool);
} The problem is the vtable need to be reproduced in other registry too... |
Mmm probably the vtable should work the other way around, that is: registry.vtable_for(id).setup(other_registry); This way you have a function with full knowledge that triggers a |
I remember the constructor's pointer cannot be obtained, so I save a lambda function to hashmap. I don't know what is the return type or the instance's location of that vtable_for(). For me, i only did this I have tested my copyReg function in my application and it works well. |
What's the current status on duplicating an entity with all components (possibly to a different registry)? None of the examples listed even compile for me with 3.9.0. This one:
Fails on this line, specifically the argument of the storage method: And it's the same on this version:
Also, from what i understand from the discussion, this method only works if the destination storage has already been initialized by creating a component of that type. Is there any alternative solution? |
You're right, this is available upstream but not part of v3.9: auto& target_store = dest.storage(source_store.type().hash()); While this:
Hopefully will be addressed before the next version. With v3.9 you can get close using the storage |
I've been using poly_storage to copy entities between registries (both to copy entire registries, which I do from the editors registry to the games registry when you press "play", and also for single entities, which I use for "prototype"/prefab entities that can be instantiated by copying from the prototype to the live entities). I do it like this and it seems to work well enough, for the past ~10 months. I'm wondering how these new changes impact that? Will I be able to implement this then without custom poly_storage? Any tradeoffs I should know about? Thanks :) |
It turned out that this solution is waaaaaay simpler but also incomplete in a sense. Something that I'll fix soon but still. |
Funny enough, after struggling with this feature for a while I realized that EnTT already offers everything needed to get what I want in a flexible and customizable way and without library additions. CC @Qix- who put the TODO in the |
Hello!
I am trying to generically (without upfront knowledge of the component types) read the byte contents of all existing pools.
Is this possible?
I assume the only way I can access the pools generically is by using
visit
:(BTW, the documentation has this example but it seems to me this is no longer possible:
)
The text was updated successfully, but these errors were encountered: