use core::{
any,
fmt::Debug,
hash::{Hash, Hasher},
};
use bevy::prelude::*;
use deterministic_hash::DeterministicHasher;
use log::debug;
use serde::{Deserialize, Serialize};
use xxhash_rust::xxh3::Xxh3Default;
#[derive(Resource, Default)]
pub struct ProtocolHasher(DeterministicHasher<Xxh3Default>);
impl ProtocolHasher {
pub fn add_custom<T: Hash + Debug>(&mut self, value: T) {
debug!("adding `{value:?}`");
value.hash(&mut self.0);
}
pub(crate) fn replicate<R>(&mut self, priority: usize) {
debug!(
"adding replication rule `{}` with priority {priority}",
ShortName::of::<R>()
);
self.hash::<R>(ProtocolPart::Replicate {
priority: priority as u64,
});
}
pub(crate) fn replicate_bundle<B>(&mut self) {
debug!(
"adding replication rule for bundle `{}`",
ShortName::of::<B>()
);
self.hash::<B>(ProtocolPart::ReplicateBundle);
}
pub(crate) fn add_client_message<E>(&mut self) {
debug!("adding client message `{}`", ShortName::of::<E>());
self.hash::<E>(ProtocolPart::ClientMessage);
}
pub(crate) fn add_client_event<E>(&mut self) {
debug!("adding client event `{}`", ShortName::of::<E>());
self.hash::<E>(ProtocolPart::ClientEvent);
}
pub(crate) fn add_server_message<E>(&mut self) {
debug!("adding server message `{}`", ShortName::of::<E>());
self.hash::<E>(ProtocolPart::ServerMessage);
}
pub(crate) fn add_server_event<E>(&mut self) {
debug!("adding server event `{}`", ShortName::of::<E>());
self.hash::<E>(ProtocolPart::ServerEvent);
}
pub(crate) fn make_message_independent<E>(&mut self) {
debug!("making message `{}` independent", ShortName::of::<E>());
self.hash::<E>(ProtocolPart::IndependentMessage);
}
pub(crate) fn make_event_independent<E>(&mut self) {
debug!("making event `{}` independent", ShortName::of::<E>());
self.hash::<E>(ProtocolPart::IndependentEvent);
}
pub(crate) fn track_mutate_messages(&mut self) {
debug!("enabling mutate message tracking");
ProtocolPart::TrackMutateMessages.hash(&mut self.0);
}
fn hash<T>(&mut self, part: ProtocolPart) {
part.hash(&mut self.0);
any::type_name::<T>().hash(&mut self.0);
}
pub(crate) fn finish(self) -> ProtocolHash {
let hash = self.0.finish();
debug!("calculated hash: {hash}");
ProtocolHash(hash)
}
}
#[derive(Hash)]
#[repr(u8)]
enum ProtocolPart {
Replicate { priority: u64 },
ReplicateBundle,
ClientMessage,
ClientEvent,
ServerMessage,
ServerEvent,
IndependentMessage,
IndependentEvent,
TrackMutateMessages,
}
#[derive(Resource, Event, Serialize, Deserialize, Reflect, Debug, PartialEq, Eq, Clone, Copy)]
pub struct ProtocolHash(u64);
#[derive(Event, Serialize, Deserialize)]
pub struct ProtocolMismatch;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty() {
assert_eq!(
ProtocolHasher::default().finish(),
ProtocolHasher::default().finish()
);
}
#[test]
fn wrong_order() {
let mut hasher1 = ProtocolHasher::default();
hasher1.replicate::<StructA>(1);
hasher1.replicate::<StructB>(1);
let mut hasher2 = ProtocolHasher::default();
hasher2.replicate::<StructB>(1);
hasher2.replicate::<StructA>(1);
assert_ne!(hasher1.finish(), hasher2.finish());
}
#[test]
fn wrong_priority() {
let mut hasher1 = ProtocolHasher::default();
hasher1.replicate::<StructA>(1);
let mut hasher2 = ProtocolHasher::default();
hasher2.replicate::<StructA>(0);
assert_ne!(hasher1.finish(), hasher2.finish());
}
#[test]
fn different_parts() {
let mut hasher1 = ProtocolHasher::default();
hasher1.add_server_message::<StructA>();
let mut hasher2 = ProtocolHasher::default();
hasher2.add_client_message::<StructA>();
assert_ne!(hasher1.finish(), hasher2.finish());
}
#[test]
fn mismatch() {
let mut hasher1 = ProtocolHasher::default();
let mut hasher2 = ProtocolHasher::default();
for hasher in [&mut hasher1, &mut hasher2] {
hasher.replicate::<StructA>(1);
hasher.add_server_message::<StructB>();
hasher.add_server_event::<StructC>();
hasher.add_client_message::<StructB>();
hasher.add_client_event::<StructC>();
}
hasher1.add_custom(0);
hasher2.add_custom(1);
assert_ne!(hasher1.finish(), hasher2.finish());
}
#[test]
fn full_match() {
let mut hasher1 = ProtocolHasher::default();
let mut hasher2 = ProtocolHasher::default();
for hasher in [&mut hasher1, &mut hasher2] {
hasher.replicate::<StructA>(1);
hasher.add_server_message::<StructB>();
hasher.add_server_event::<StructC>();
hasher.add_client_message::<StructB>();
hasher.add_client_event::<StructC>();
hasher.track_mutate_messages();
hasher.add_custom(0usize);
}
const EXPECTED: ProtocolHash = ProtocolHash(7833509759262497991);
assert_eq!(hasher1.finish(), EXPECTED);
assert_eq!(hasher2.finish(), EXPECTED);
}
struct StructA;
struct StructB;
struct StructC;
}