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

Make Entity (de)serializable #52

Closed
bestouff opened this issue Dec 12, 2019 · 8 comments
Closed

Make Entity (de)serializable #52

bestouff opened this issue Dec 12, 2019 · 8 comments

Comments

@bestouff
Copy link
Contributor

Hi,

For a networking game I'd like to use Entity as an identification for players (a player client would send to the server an Entity along other data).
If this is sound, would it be possible to make Entity (de)serializable to be able to send it in network packets ?

@minecrawler
Copy link

minecrawler commented Dec 12, 2019

In an ECS, in theory, an entity is a virtual value. It does not exist and acts as glue for components. From that perspective, I think using Entity is a bad idea, because you want to send nothing as identification.

In the case of Legion (and other ECS implementations), Entity contains some data. For example the index, which is a linear incremented integer value. Syncing on Entity within Legion means syncing the u32 index, which imho is a bad idea. You'd have to sync the game worlds of all clients, so they use the same index for the same entities which results in the same player object in the game.

I propose the following: Your client registers with the server and the server assigns your player a private token (which allows updating your own data - always remember that users are evil by default). At the same time, every player also gets a public token, which all other clients may use for identification (for example to query a player's status). The client may stay connected or make separate calls, and it can use the given token for identification when sending requests to the server. You can store your private token in your global game storage and add a component for all player objects, which holds the public token for them (or add it to you Player component).

@bestouff
Copy link
Contributor Author

bestouff commented Dec 12, 2019

Ok, so say I store some kind of Id in each "player entity". I may receive packets from each client quite often with e.g. commands from the players, along with an Id field.
How do I update the player entities with the data from the clients ? Do I have to do a linear search via a filter for the matching Id ?

I'd rather have a mean to a direct access to a player entity components from an Id.

@OvermindDL1
Copy link

This is why legion needs to support component grouping like the EnTT ECS library. Something like an ID component (I generally go with a UUID if it's a lot of potential entities) really should be in it's own bimap and not packed with the rest of the data.

@bestouff
Copy link
Contributor Author

While waiting for this to happen, what would you suggest to access a given entity in O(1) time ?

@OvermindDL1
Copy link

How many unique ID's do you expect? O(1) is hard to get unless you want to allocate an array for all possible values of it or so, which is easy if it's a low amount. Regardless, an array would work, using an id as the index and the value is the entity ID, keep an ID component on the entity for the reverse mapping and make it immutable. Keep a 'free list' (queue or so) of deactivated ID's as entities are destroy'd.

@bestouff
Copy link
Contributor Author

So basically I'm using Entity as direct access handle, but via an indirection through an array which index I can serialize.
OK thanks, will do.

@bestouff
Copy link
Contributor Author

bestouff commented Jan 4, 2020

FWIW I did the separate array + freelist trick, but that was too much back-and-forth for nothing IMHO. I finally resorted to make Entity serializable via this piece of code:

use legion::entity::Entity;
use serde::{Deserialize, Serialize};
use std::convert::{From, Into};

type EType = u64;

#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct Serdent(EType);

impl From<Entity> for Serdent {
    fn from(entity: Entity) -> Self {
        let etype: EType = unsafe { std::mem::transmute(entity) };
        Serdent(etype)
    }
}

impl Into<Entity> for Serdent {
    fn into(self) -> Entity {
        unsafe { std::mem::transmute(self.0) }
    }
}

@solarretrace
Copy link

So I hit some of the same issues trying to implement save states, and while it obviously doesn't make any sense to deserialize entities and use them with the ECS directly, I would like to avoid having to use something like enum MaybeEntity { Id(u32), Entity(legion::Entity) } on every component that refers to another entity. If Entity had a public constructor, I could at least serialize them and deserialize them safely as ids. As it is, I also have to rely on transmute and hope that doesn't break anything.

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

4 participants