Use a fixed state hasher in bevy_reflect for deterministic Reflect::reflect_hash() across processes (#7583)
# Objective - bevy_ggrs uses `reflect_hash` in order to produce checksums for its world snapshots. These checksums are sent between clients in order to detect desyncronization. - However, since we currently use `async::AHasher` with the `std` feature, this means that hashes will always be different for different peers, even if the state is identical. - This means bevy_ggrs needs a way to get a deterministic (fixed) hash. ## Solution - ~~Add a feature to use `bevy_utils::FixedState` for the hasher used by bevy_reflect.~~ - Always use `bevy_utils::FixedState` for initializing the bevy_reflect hasher. --- ## Changelog - bevy_reflect now uses a fixed state for its hasher, which means the output of `Reflect::reflect_hash` is now deterministic across processes.
This commit is contained in:
parent
04256735f6
commit
18cfb226db
@ -5,7 +5,7 @@
|
||||
//! the derive helper attribute for `Reflect`, which looks like:
|
||||
//! `#[reflect(PartialEq, Default, ...)]` and `#[reflect_value(PartialEq, Default, ...)]`.
|
||||
|
||||
use crate::fq_std::{FQAny, FQDefault, FQOption};
|
||||
use crate::fq_std::{FQAny, FQOption};
|
||||
use crate::utility;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use quote::quote_spanned;
|
||||
@ -225,7 +225,7 @@ impl ReflectTraits {
|
||||
&TraitImpl::Implemented(span) => Some(quote_spanned! {span=>
|
||||
fn reflect_hash(&self) -> #FQOption<u64> {
|
||||
use ::core::hash::{Hash, Hasher};
|
||||
let mut hasher: #bevy_reflect_path::ReflectHasher = #FQDefault::default();
|
||||
let mut hasher = #bevy_reflect_path::utility::reflect_hasher();
|
||||
Hash::hash(&#FQAny::type_id(self), &mut hasher);
|
||||
Hash::hash(self, &mut hasher);
|
||||
#FQOption::Some(Hasher::finish(&hasher))
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
utility::NonGenericTypeInfoCell, DynamicInfo, Reflect, ReflectMut, ReflectOwned, ReflectRef,
|
||||
TypeInfo, Typed,
|
||||
utility::{reflect_hasher, NonGenericTypeInfoCell},
|
||||
DynamicInfo, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, Typed,
|
||||
};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
@ -340,7 +340,7 @@ impl<'a> ExactSizeIterator for ArrayIter<'a> {}
|
||||
/// Returns the `u64` hash of the given [array](Array).
|
||||
#[inline]
|
||||
pub fn array_hash<A: Array>(array: &A) -> Option<u64> {
|
||||
let mut hasher = crate::ReflectHasher::default();
|
||||
let mut hasher = reflect_hasher();
|
||||
std::any::Any::type_id(array).hash(&mut hasher);
|
||||
array.len().hash(&mut hasher);
|
||||
for value in array.iter() {
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
use crate::{Enum, Reflect, ReflectRef, VariantType};
|
||||
use crate::{utility::reflect_hasher, Enum, Reflect, ReflectRef, VariantType};
|
||||
use std::fmt::Debug;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
/// Returns the `u64` hash of the given [enum](Enum).
|
||||
#[inline]
|
||||
pub fn enum_hash<TEnum: Enum>(value: &TEnum) -> Option<u64> {
|
||||
let mut hasher = crate::ReflectHasher::default();
|
||||
let mut hasher = reflect_hasher();
|
||||
std::any::Any::type_id(value).hash(&mut hasher);
|
||||
value.variant_name().hash(&mut hasher);
|
||||
value.variant_type().hash(&mut hasher);
|
||||
|
||||
@ -8,7 +8,7 @@ use crate::{
|
||||
VariantInfo, VariantType,
|
||||
};
|
||||
|
||||
use crate::utility::{GenericTypeInfoCell, NonGenericTypeInfoCell};
|
||||
use crate::utility::{reflect_hasher, GenericTypeInfoCell, NonGenericTypeInfoCell};
|
||||
use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value};
|
||||
use bevy_utils::{Duration, Instant};
|
||||
use bevy_utils::{HashMap, HashSet};
|
||||
@ -993,7 +993,7 @@ impl Reflect for Cow<'static, str> {
|
||||
}
|
||||
|
||||
fn reflect_hash(&self) -> Option<u64> {
|
||||
let mut hasher = crate::ReflectHasher::default();
|
||||
let mut hasher = reflect_hasher();
|
||||
Hash::hash(&std::any::Any::type_id(self), &mut hasher);
|
||||
Hash::hash(self, &mut hasher);
|
||||
Some(hasher.finish())
|
||||
@ -1101,7 +1101,7 @@ impl Reflect for &'static Path {
|
||||
}
|
||||
|
||||
fn reflect_hash(&self) -> Option<u64> {
|
||||
let mut hasher = crate::ReflectHasher::default();
|
||||
let mut hasher = reflect_hasher();
|
||||
Hash::hash(&std::any::Any::type_id(self), &mut hasher);
|
||||
Hash::hash(self, &mut hasher);
|
||||
Some(hasher.finish())
|
||||
|
||||
@ -2,7 +2,7 @@ use std::any::{Any, TypeId};
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use crate::utility::NonGenericTypeInfoCell;
|
||||
use crate::utility::{reflect_hasher, NonGenericTypeInfoCell};
|
||||
use crate::{
|
||||
DynamicInfo, FromReflect, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, Typed,
|
||||
};
|
||||
@ -378,7 +378,7 @@ impl<'a> ExactSizeIterator for ListIter<'a> {}
|
||||
/// Returns the `u64` hash of the given [list](List).
|
||||
#[inline]
|
||||
pub fn list_hash<L: List>(list: &L) -> Option<u64> {
|
||||
let mut hasher = crate::ReflectHasher::default();
|
||||
let mut hasher = reflect_hasher();
|
||||
std::any::Any::type_id(list).hash(&mut hasher);
|
||||
list.len().hash(&mut hasher);
|
||||
for value in list.iter() {
|
||||
|
||||
@ -9,7 +9,6 @@ use std::{
|
||||
};
|
||||
|
||||
use crate::utility::NonGenericTypeInfoCell;
|
||||
pub use bevy_utils::AHasher as ReflectHasher;
|
||||
|
||||
/// An immutable enumeration of "kinds" of reflected type.
|
||||
///
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
//! Helpers for working with Bevy reflection.
|
||||
|
||||
use crate::TypeInfo;
|
||||
use bevy_utils::HashMap;
|
||||
use bevy_utils::{FixedState, HashMap};
|
||||
use once_cell::race::OnceBox;
|
||||
use parking_lot::RwLock;
|
||||
use std::any::{Any, TypeId};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
hash::BuildHasher,
|
||||
};
|
||||
|
||||
/// A container for [`TypeInfo`] over non-generic types, allowing instances to be stored statically.
|
||||
///
|
||||
@ -147,3 +150,15 @@ impl GenericTypeInfoCell {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Deterministic fixed state hasher to be used by implementors of [`Reflect::reflect_hash`].
|
||||
///
|
||||
/// Hashes should be deterministic across processes so hashes can be used as
|
||||
/// checksums for saved scenes, rollback snapshots etc. This function returns
|
||||
/// such a hasher.
|
||||
///
|
||||
/// [`Reflect::reflect_hash`]: crate::Reflect
|
||||
#[inline]
|
||||
pub fn reflect_hasher() -> bevy_utils::AHasher {
|
||||
FixedState.build_hasher()
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user