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

Saveload overhaul #337

Merged
merged 12 commits into from
Apr 16, 2018
Merged

Saveload overhaul #337

merged 12 commits into from
Apr 16, 2018

Conversation

torkleyy
Copy link
Member

@torkleyy torkleyy commented Jan 13, 2018

TODO

  • Commit messages need to be improved
  • There is some weird lifetime error that I don't really understand

Motivation for this PR

  • Rename some items to make the code easier to understand
  • Remove a couple of unnecessary bounds
  • Allow passing references of storages (previously you could not reuse the WriteStorage)

Fixes #138
Fixes #282


This change is Reviewable

@torkleyy
Copy link
Member Author

torkleyy commented Jan 13, 2018

This is the error I'm getting:

error[E0623]: lifetime mismatch
  --> src/saveload/de.rs:47:13
   |
35 |         &'b mut self,
   |         ------------ these two types are declared with different lifetimes...
36 |         entities: &'b EntitiesRes,
37 |         markers: &'b mut WriteStorage<'a, M>,
   |                          -------------------
...
47 |             markers,
   |             ^^^^^^^ ...but data from `self` flows into `markers` here

@zakarumych
Copy link
Member

@torkleyy the problem is in "de.rs" line 129

@torkleyy
Copy link
Member Author

OMG this is such a stupid mistake... Thanks for your help!

@zakarumych
Copy link
Member

The error message is confusing. I've spotted it by chance.

struct DeserializeEntity<'a: 'b, 'b, 's, E, M: Marker, S: 's> {
allocator: &'b mut M::Allocator,
entities: &'b EntitiesRes,
storages: &'s mut S,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you really need different lifetime here?

fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
write!(formatter, "Sequence of serialized entities")
}
/// Error may occur during serialization or deserialization of component
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

during deserialization.

@torkleyy
Copy link
Member Author

torkleyy commented Feb 2, 2018

@Xaeroxe is going to continue this PR, so here's what I planned:

It turned out that the current code doesn't really allow multiple allocators and has some weird interaction with the marker component storage.

Therefore, I've been thinking that it would make sense to put the entity -> marker and marker -> entity mapping both into the allocator (also see these messages on Gitter). You'll likely be using a BitSet in order to allow joining over the markers.

That's all I remember so far, please feel free to ask if something doesn't make sense.

@Xaeroxe
Copy link
Member

Xaeroxe commented Feb 10, 2018

Currently looking into simplifying the marker type by stripping away what I feel may be metadata not appropriate to store here. Some examples:

  • Marker::Identifier and Marker::id(): This feels extraneous, a marker's primary purpose should be to identify, rather than it being just one property of them. We're cloning these things all the time so allowing additional metadata to be associated with them may result in confusing behavior as users lose track of where their metadata actually is.

  • Marker::update(): Similar to the above, this feels excessive and easy to get confused on. This kind of meta data also doesn't exactly fulfill its proposed use case imo as the actual update signature doesn't provide enough context to give a meaningful audit of changes. In addition to this, the constant cloning of these markers means this data might even be misleading in some instances.

I've removed the Component impl for the Marker as well, since it was preventing use of multiple allocators with the same marker type for varying purposes. I'm also making the MarkerAllocator trait able to map in the opposite direction in order to make up for this removal.

@torkleyy
Copy link
Member Author

@Xaeroxe I was also confused by this at the beginning, but @omni-viral said that it can be useful for e.g. a generational id or something. I'm not quite sure about it.

@Xaeroxe
Copy link
Member

Xaeroxe commented Feb 10, 2018

This metadata may be useful in the right contexts, I just don't think this is the right place to be trying to store and generate it. It should be handled by something with a more clear picture of what exactly it's trying to track. Typically the user, with their own data structures.

@torkleyy
Copy link
Member Author

I'm also in favor of simplifying it, I just left it as is because @omni-viral told me so. Let's wait for them to respond.

@zakarumych
Copy link
Member

I'm all for simplifying. But would it be possible to implement otherwise?

@Xaeroxe
Copy link
Member

Xaeroxe commented Feb 10, 2018

Can you clarify what you're referring to when you say "it"?

@zakarumych
Copy link
Member

Network synchronization that checks for sequence id and rejects if entity in packet comes with less sequence id then one already in the world.

@Xaeroxe
Copy link
Member

Xaeroxe commented Feb 10, 2018

Oh, yes that will definitely be possible. That kind of meta data could be stored in another component that the network system itself queries and writes to. It can exist completely separate from the marker system.

@zakarumych
Copy link
Member

Ok. And how deserializer would know how to deal with it?

@Xaeroxe
Copy link
Member

Xaeroxe commented Feb 13, 2018

I don't believe it's the deserializer's job to perform that kind of filtering. It should be handled in the network layer.

@zakarumych
Copy link
Member

zakarumych commented Feb 13, 2018

Actually it's not deserializer. It's loader.
And if loader will overwrite components with outdated data you won't be able to restore them.

@Xaeroxe
Copy link
Member

Xaeroxe commented Mar 17, 2018

I'm finally getting back to this. I see your point now @omni-viral .

If a marker's type is meant to be a semantic detail that controls how the underlying identifier is used during loading I'm no longer sure what we gain from making these not components @torkleyy . Can you please provide a use case where we needed a single marker type to have multiple storages?

@Xaeroxe
Copy link
Member

Xaeroxe commented Mar 17, 2018

Rebased, review comments addressed, example fixed, formatted with rustfmt 0.3.4-nightly. I think this is ready for review.

@@ -161,8 +161,7 @@ impl Errors {
#[derive(Derivative)]
#[derivative(Default(bound = ""))]
pub struct Merge<F> {
#[derivative(Default(value = "PhantomData"))]
future_type: PhantomData<F>,
#[derivative(Default(value = "PhantomData"))] future_type: PhantomData<F>,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, is this some new rustfmt setting again?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this attribute necessary at all?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, otherwise F would have to implement Default.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. PhantomData implements default for any type parameter.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does now but I think it was a rather recent fix which I don't want to rely on yet.

Copy link
Member

@zakarumych zakarumych Mar 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does at least since 1.0.0

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh, the attribute on PhantomData isn't required, but we need derivative here.

Copy link
Member Author

@torkleyy torkleyy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. @Xaeroxe Please squash the commits.

@torkleyy
Copy link
Member Author

I think it's good as-is, so feel free to merge @WaDelma

Copy link
Member

@WaDelma WaDelma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good!

&mut ser,
).unwrap_or_else(|e| eprintln!("Error: {}", e));
// TODO: Specs should return an error which combines serialization
// and component errors.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this TODO to be left to future PR?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

ReadStorage<'a, U64Marker>,
);

fn run(&mut self, (ents, pos, vel, markers): Self::SystemData) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/vel/mass/

@@ -48,7 +48,7 @@ fn main() {
let entity_list: Vec<_> = (&*data.entities).join().collect();

// Remove all components
for (entity, _) in (&*data.entities, &data.comp.check()).join() {
for (entity, _) in (&*data.entities, data.comp.mask().clone()).join() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't you use drain here?

/// Allocator for this `Marker`
type Allocator: MarkerAllocator<Self>;

/// Get this marker internal id
/// Get this marker internal id.
/// This value may never change.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the update method says that changes in return value doesn't cause UB, I think the wording should be relaxed.
Maybe something like: "The value of this method should be constant".

fn mark<'m>(&mut self, entity: Entity, storage: &'m mut WriteStorage<M>) -> (&'m M, bool) {
let mut new = false;

let marker = storage.entry(entity).unwrap().or_insert_with(|| {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is with this unwrap here? Is it expected to not panic? I would change it to expect that contains explanation.

serseq.serialize_element(&EntityData::<M, Self::Data> {
marker: marker.clone(),
components: self.serialize_entity(entity, &ids)
.map_err(ser::Error::custom)?, // TODO: revise
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does the TODO here mean? If it's still relevant, it should be made more informative.

})?;
}
}
to_serialize = add;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is bit hard to decipher, but I don't know how to improve it.
Also you could reuse to_serialize and only do two allocations, but I don't know if it's worth it.


// fn join(&'b mut self) -> Self::Join {
// self
// }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove commented code


// fn join(&'c mut self) -> Self::Join {
// &mut **self
// }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove commented code

@torkleyy
Copy link
Member Author

torkleyy commented Mar 18, 2018 via email

@torkleyy torkleyy mentioned this pull request Mar 19, 2018
@UserAB1236872
Copy link
Contributor

I had an issue with saveload that crashed on deserialize with NullStorage, any chance the overhaul fixes this? I was going to open a separate issue, but figured this may be a better place to report it since it's a pretty big overhaul.

@torkleyy
Copy link
Member Author

torkleyy commented Apr 1, 2018

@LinearZoetrope What kind of crash was that? I never experienced any crashes, so can't say if I fixed them.

@UserAB1236872
Copy link
Contributor

I'll have to retest, but if I recall correctly, it was claiming anything stored in NullStorage wasn't present when deserializing.

@torkleyy
Copy link
Member Author

@LinearZoetrope I'll check it and add a test to ensure it's working.

@Xaeroxe
Copy link
Member

Xaeroxe commented Apr 10, 2018

@torkleyy I believe this is ready to be merged.

@Xaeroxe
Copy link
Member

Xaeroxe commented Apr 16, 2018

Ok I've just rebased this, it should be ready to merge. Recommend one more review prior to merging, as the rebase kind of aggressively preferred to keep code from this PR where there were conflicts.

Copy link
Member

@WaDelma WaDelma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but would like another pair of eyes to go over it.

@@ -1,4 +1,4 @@
#![deny(missing_docs)]
#![warn(missing_docs)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So there are missing documentations?

type Data: Serialize;

/// Serialize the components of a single entiy using a entity -> marker mapping.
fn serialize_entity<F>(&self, entity: Entity, ids: F) -> Result<Self::Data, E>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/entiy/entity/

@torkleyy
Copy link
Member Author

torkleyy commented Apr 16, 2018 via email

Copy link
Member Author

@torkleyy torkleyy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

bors r+

bors bot added a commit that referenced this pull request Apr 16, 2018
337: Saveload overhaul r=torkleyy a=torkleyy

## TODO

* [x] Commit messages need to be improved
* [x] There is some weird lifetime error that I don't really understand

## Motivation for this PR

* Rename some items to make the code easier to understand
* Remove a couple of unnecessary bounds
* Allow passing references of storages (previously you could not reuse the `WriteStorage`)

---

Fixes #138 
Fixes #282 

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/slide-rs/specs/337)
<!-- Reviewable:end -->


Co-authored-by: torkleyy <torkleyy@gmail.com>
Co-authored-by: Jacob Kiesel <kieseljake@gmail.com>
@bors
Copy link
Contributor

bors bot commented Apr 16, 2018

Build succeeded

@bors bors bot merged commit 297abd4 into amethyst:master Apr 16, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

saveload tracking issue Serializing/Deserializing the Entity struct
5 participants