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