pub struct Tape<I> { /* private fields */ }Expand description
A recorded run: one intent per tick, plus the canonical world hash each tick left behind, both in tick order.
A tape is the artifact a bug report carries: the seed, what the player asked for on every tick, and what the world looked like. Serialisable, so it can be written next to the save it came from.
Implementations§
Source§impl<I> Tape<I>
impl<I> Tape<I>
Sourcepub const fn new(seed: u64) -> Self
pub const fn new(seed: u64) -> Self
An empty tape for a run seeded with seed.
The seed is carried because a tape without it is not replayable: the
factory a replay is handed reads seed to rebuild the same
starting world.
Sourcepub fn intents_mut(&mut self) -> &mut [I]
pub fn intents_mut(&mut self) -> &mut [I]
Every recorded intent, to edit a tape before replaying it.
Rewriting a tick’s intent while keeping the recorded hashes is how a divergence is bisected: the replay then reports that tick (or says the edit changed nothing), instead of a hunt through a whole session.
Sourcepub fn hashes(&self) -> &[u64]
pub fn hashes(&self) -> &[u64]
Every recorded hash, in tick order. Shorter than the tape when the intent tail was never hashed.
Sourcepub fn hash(&self, tick: u64) -> Option<u64>
pub fn hash(&self, tick: u64) -> Option<u64>
The recorded world hash of tick, or None if that tick was not hashed.
Sourcepub fn record_intent(&mut self, intent: I) -> u64
pub fn record_intent(&mut self, intent: I) -> u64
Appends one tick’s intent. Returns the tick it landed on.
Call it before the tick runs, so the tape is in the order the game saw.
Sourcepub fn record_hash(&mut self, world_hash: u64)
pub fn record_hash(&mut self, world_hash: u64)
Appends the canonical world hash of the tick just recorded, in tick
order: tick n’s hash is the world after tick n ran.
A tape may stop hashing early (an intent-only tail cannot be verified,
see verify), but never hashes twice for one tick, and
never more hashes than ticks.
§Panics
If every recorded intent already has a hash. That is a recording loop hashing twice for one tick, which would misalign every later comparison.
Source§impl<I> Tape<I>
impl<I> Tape<I>
Sourcepub fn replay<F>(&self, setup: F) -> Vec<u64>
pub fn replay<F>(&self, setup: F) -> Vec<u64>
Replays the tape into a fresh world and returns the canonical hash of every tick, in tick order.
setup is called once per replay and must rebuild the same starting
world every time, so a caller can replay one tape twice and diff the two
logs itself, or compare them against a golden list.
Sourcepub fn replay_with<F, C>(&self, setup: F, commit: C) -> Vec<u64>
pub fn replay_with<F, C>(&self, setup: F, commit: C) -> Vec<u64>
replay, running commit on each tick’s intent after
Intent is set and before the tick runs.
This is the hook for a game whose systems read their own action resources:
let commit = |world: &mut World, input: &Press| {
world.resource_mut::<Keys>().0 = *input;
};
let hashes = tape.replay_with(|| fresh(seed), commit);Sourcepub fn verify<F>(&self, setup: F) -> Result<(), VerifyError>
pub fn verify<F>(&self, setup: F) -> Result<(), VerifyError>
Replays the tape and compares every tick’s hash against the recording, returning the first tick that differs.
The replay stops at the divergence: a world that already disagrees is not
worth stepping further, and a game that would panic on it panics here
instead of reporting the tick it went wrong at. Pair this with
record_intent and
record_hash.
Trait Implementations§
Source§impl<'de, I> Deserialize<'de> for Tape<I>where
I: Deserialize<'de>,
impl<'de, I> Deserialize<'de> for Tape<I>where
I: Deserialize<'de>,
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl<I: Eq> Eq for Tape<I>
impl<I: PartialEq> StructuralPartialEq for Tape<I>
Auto Trait Implementations§
impl<I> Freeze for Tape<I>
impl<I> RefUnwindSafe for Tape<I>where
Vec<I>: RefUnwindSafe,
impl<I> Send for Tape<I>
impl<I> Sync for Tape<I>
impl<I> Unpin for Tape<I>
impl<I> UnsafeUnpin for Tape<I>where
Vec<I>: UnsafeUnpin,
impl<I> UnwindSafe for Tape<I>where
Vec<I>: UnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> ConditionalSend for Twhere
T: Send,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<T> IntoResult<T> for T
impl<T> IntoResult<T> for T
Source§fn into_result(self) -> Result<T, RunSystemError>
fn into_result(self) -> Result<T, RunSystemError>
Source§impl<T> Serialize for T
impl<T> Serialize for T
fn erased_serialize(&self, serializer: &mut dyn Serializer) -> Result<(), Error>
fn do_erased_serialize( &self, serializer: &mut dyn Serializer, ) -> Result<(), ErrorImpl>
Source§impl<T> Template for T
impl<T> Template for T
Source§fn build_template(
&self,
_context: &mut TemplateContext<'_, '_>,
) -> Result<<T as Template>::Output, BevyError>
fn build_template( &self, _context: &mut TemplateContext<'_, '_>, ) -> Result<<T as Template>::Output, BevyError>
entity context to produce a Template::Output.Source§fn clone_template(&self) -> T
fn clone_template(&self) -> T
Clone.