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

Custom serialization for Entity #386

Open
LPGhatguy opened this issue Dec 18, 2024 · 3 comments
Open

Custom serialization for Entity #386

LPGhatguy opened this issue Dec 18, 2024 · 3 comments

Comments

@LPGhatguy
Copy link

This issue is rough spitballing for how hecs might allow users to define how Entity gets serialized/deserialized.

hecs has been fantastic for our game and we've had a good serialization solution for a long time. One of the warts in our integration, however, is that we have two versions of the Entity type in order to support a custom implementation of Serialize and Deserialize. All of our components use our wrapped version and they automatically get remapped to integers or strings when appropriate during serialization.

In some places, we're able to easily wrap the hecs APIs to only return our custom Entity type, but hecs::Entity usually ends up leaking out. We call that type EphemeralEntity to emphasize that it's meant to be for non-serialized data, but I'd rather not have that type at all!

One option is for us to keep a small fork of hecs where we change the implementation of Serialize and Deserialize on Entity to go through our remapping process. It probably wouldn't be a very big patch, but then we're out of "userspace" hecs which is kind of lame.

A large hammer of an API would be for hecs to expose a global that a user could override in order to implement Serialize and Deserialize. Maybe that'd be in the form of a trait object that mirror's #[serde(with = ...)] and uses erased_serde.

I'm down for any other options in this space that lead to us being able to delete the wrapper around hecs::Entity in our codebase. 😅

@Ralith
Copy link
Owner

Ralith commented Dec 19, 2024

Serialize/Deserialize are not an intrinsic part of hecs' API, so designing functionality to customize their behavior seems strange. Are you concerned about the behavior of the provided serialization modules, or of hecs types in your own serializeable structures? In the former case, the intention is that you vendor and customize those according to your requirements -- they rely entirely public API. In the latter, I'm not sure that the types involved in hecs APIs matter much, since hecs APIs aren't involved in serialization. Are you just looking for a way to save some .into() calls?

@LPGhatguy
Copy link
Author

Are you concerned about the behavior of the provided serialization modules, or of hecs types in your own serializeable structures?

Using specifically hecs::Entity in my own serializable structures. It's the only type in hecs' interface that ends up embedded deep into other data structures that I'm interested in serializing.

Are you just looking for a way to save some .into() calls?

Pretty much! Having two Entity types and having to convert between them in basically every single piece of gameplay code is really rough.

It's pretty easy to define custom serialization for World because it appears so infrequently. Entity comes up a lot and the default deserialization in particular doesn't jive when composing prefabs or worlds.

@Ralith
Copy link
Owner

Ralith commented Dec 19, 2024

Summarizing chat discussion:

  • Relying on derive(Serialize) for real component structures kind of sucks because derive(Serialize) must be application-agnostic, and it's easy to run into cases like (but not limited to) this where you want application-specific logic, or where your serialized representation should differ significantly.
  • Hand-written serialization requires an awful lot of boilerplate, even if it's just translating to another struct that has the derives.
  • Replacing hecs::Entity's Serialize implementation with mutable global state is feasible but an unpleasant hack, even if feature-gated.
  • A custom serialization trait defined downstream, with a derive macro and a declarative macro to forward trivial cases to serde (or whatever), allows downstream code to solve the problem in the general case with O(1) boilerplate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants