Skip to main content

EventKind

Enum EventKind 

Source
pub enum EventKind {
    MoveOrdered {
        army: ArmyId,
        from: TerritoryId,
        to: TerritoryId,
        arrival_at: SimTime,
    },
    ArmyArrived {
        army: ArmyId,
        territory: TerritoryId,
    },
    ReportDispatched {
        recipient: PersonId,
        army: ArmyId,
        arrives_at: SimTime,
    },
    KnowledgeUpdated {
        recipient: PersonId,
        army: ArmyId,
        known_location: TerritoryId,
    },
    DebugFieldChanged {
        entity: EntityRef,
        field: String,
        old_value: String,
        new_value: String,
    },
    Plugin {
        plugin: String,
        event_type: String,
    },
}

Variants§

§

MoveOrdered

Fields

§army: ArmyId
§arrival_at: SimTime
§

ArmyArrived

Fields

§army: ArmyId
§territory: TerritoryId
§

ReportDispatched

Fields

§recipient: PersonId
§army: ArmyId
§arrives_at: SimTime
§

KnowledgeUpdated

Fields

§recipient: PersonId
§army: ArmyId
§known_location: TerritoryId
§

DebugFieldChanged

Fields

§entity: EntityRef
§field: String
§old_value: String
§new_value: String
§

Plugin

Fields

§plugin: String
§event_type: String

Implementations§

Source§

impl EventKind

Source

pub const fn event_type(&self) -> &'static str

Source

pub fn qualified_event_type(&self) -> String

Returns the stable, display-oriented type identity for this event.

Built-in event kinds retain the same snake-case labels returned by Self::event_type. Plugin events are qualified with their registered plugin name and event type, separated by a dot. The two plugin-provided components are preserved as registered; no case folding or additional normalization is applied.

Examples found in repository?
examples/continuous_game_loop.rs (line 351)
344    fn report_new_events(&mut self) {
345        let events = self.authority.events();
346        if self.verbose {
347            for event in &events[self.reported_events..] {
348                println!(
349                    "EVENT: at={} type={} {}",
350                    event.timestamp,
351                    event.kind.qualified_event_type(),
352                    event.summary,
353                );
354            }
355        }
356        self.reported_events = events.len();
357    }
358
359    fn outcome(self, profile: FrameProfile) -> Result<RunOutcome, CanwuError> {
360        Ok(RunOutcome {
361            profile: profile.name,
362            frames: self.frame,
363            time: self.authority.time(),
364            world: self.authority.world(),
365            events: self.authority.events().to_vec(),
366            commands: self.authority.commands().to_vec(),
367            authoritative_state_hash: self.authority.authoritative_state_hash()?,
368            checkpoint_hash: self.authority.checkpoint_hash().to_owned(),
369            canonical_boundaries: self.canonical_boundaries,
370        })
371    }
372}
373
374struct RunOutcome {
375    profile: &'static str,
376    frames: u64,
377    time: SimTime,
378    world: WorldSnapshot,
379    events: Vec<SimEvent>,
380    commands: Vec<CommandRecord>,
381    authoritative_state_hash: String,
382    checkpoint_hash: String,
383    canonical_boundaries: usize,
384}
385
386fn run(profile: FrameProfile, verbose: bool) -> Result<RunOutcome, CanwuError> {
387    let mut host = GameHost::new(verbose)?;
388    if verbose {
389        println!("\n{} render loop", profile.name);
390    }
391
392    host.run_phase(SPEED_SCHEDULE[0], profile)?;
393    host.submit_player_command()?;
394    host.run_phase(SPEED_SCHEDULE[1], profile)?;
395    host.run_phase(SPEED_SCHEDULE[2], profile)?;
396
397    let time_before_pause = host.authority.time();
398    let remainder_before_pause = host.accumulated_sim_nanos;
399    let presentation_before_pause = host.presentation_time();
400    let frames_before_pause = host.frame;
401    assert!(
402        remainder_before_pause > 0,
403        "the pause fixture must preserve a fractional simulation minute"
404    );
405    host.run_phase(SPEED_SCHEDULE[3], profile)?;
406    assert!(
407        host.frame > frames_before_pause,
408        "pause must keep rendering"
409    );
410    assert_eq!(
411        host.authority.time(),
412        time_before_pause,
413        "pause must not advance Canwu"
414    );
415    assert_eq!(
416        host.accumulated_sim_nanos, remainder_before_pause,
417        "pause must not add desired simulation time"
418    );
419    assert_eq!(
420        host.presentation_time(),
421        presentation_before_pause,
422        "pause must freeze presentation time"
423    );
424
425    host.run_phase(SPEED_SCHEDULE[4], profile)?;
426    assert_eq!(
427        host.accumulated_sim_nanos, 0,
428        "the scripted schedule should end on a simulation-minute boundary"
429    );
430    host.outcome(profile)
431}
432
433fn std_duration(duration: SimDuration) -> Duration {
434    let minutes = u64::try_from(duration.as_minutes())
435        .expect("presentation interpolation requires a non-negative duration");
436    Duration::from_secs(
437        minutes
438            .checked_mul(60)
439            .expect("presentation duration must remain representable"),
440    )
441}
442
443fn assert_fps_independent(left: &RunOutcome, right: &RunOutcome) {
444    assert_ne!(
445        left.frames, right.frames,
446        "the render segmentations must differ"
447    );
448    assert_eq!(left.time, right.time, "Canwu time changed with render FPS");
449    assert_eq!(
450        left.world, right.world,
451        "world state changed with render FPS"
452    );
453    assert_eq!(left.events, right.events, "events changed with render FPS");
454    assert_eq!(
455        left.commands, right.commands,
456        "command ordering changed with render FPS"
457    );
458    assert_eq!(
459        left.authoritative_state_hash, right.authoritative_state_hash,
460        "authoritative state hash changed with render FPS"
461    );
462    assert_eq!(
463        left.checkpoint_hash, right.checkpoint_hash,
464        "checkpoint hash changed with render FPS"
465    );
466    assert_eq!(
467        left.canonical_boundaries, right.canonical_boundaries,
468        "canonical boundary count changed with render FPS"
469    );
470}
471
472fn assert_expected_outcome(outcome: &RunOutcome) {
473    let ids = Canwu::demo_ids();
474    let command_at = SimTime::from_minutes(1);
475    let arrival_at = SimTime::from_minutes(1_081);
476    let final_time = SimTime::from_minutes(1_082);
477
478    assert_eq!(outcome.time, final_time, "the scripted final time changed");
479    let army = outcome
480        .world
481        .army(ids.army)
482        .expect("the demo army must remain present");
483    assert_eq!(
484        army.location, ids.eastern_territory,
485        "the scheduled arrival must execute"
486    );
487    assert!(
488        army.transit.is_none(),
489        "the army must no longer be in authoritative transit"
490    );
491
492    assert_eq!(
493        outcome.commands.len(),
494        1,
495        "the move command must be accepted"
496    );
497    let command = &outcome.commands[0];
498    assert_eq!(command.accepted_at, command_at);
499    assert_eq!(command.envelope.expected_time, Some(command_at));
500    assert!(matches!(
501        &command.envelope.command,
502        Command::MoveArmy { army, destination }
503            if *army == ids.army && *destination == ids.eastern_territory
504    ));
505
506    let event_timeline = outcome
507        .events
508        .iter()
509        .map(|event| (event.kind.qualified_event_type(), event.timestamp))
510        .collect::<Vec<_>>();
511    assert_eq!(
512        event_timeline,
513        vec![
514            ("move_ordered".to_owned(), command_at),
515            ("army_arrived".to_owned(), arrival_at),
516            ("knowledge_updated".to_owned(), arrival_at),
517            ("report_dispatched".to_owned(), arrival_at),
518        ],
519        "the expected command and scheduled-arrival event timeline changed"
520    );
521    assert_eq!(
522        outcome.canonical_boundaries, 2,
523        "the command and arrival should settle at two canonical boundaries"
524    );
525}

Trait Implementations§

Source§

impl Clone for EventKind

Source§

fn clone(&self) -> EventKind

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for EventKind

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for EventKind

Source§

fn deserialize<__D>( __deserializer: __D, ) -> Result<EventKind, <__D as Deserializer<'de>>::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Eq for EventKind

Source§

impl PartialEq for EventKind

Source§

fn eq(&self, other: &EventKind) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for EventKind

Source§

fn serialize<__S>( &self, __serializer: __S, ) -> Result<<__S as Serializer>::Ok, <__S as Serializer>::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for EventKind

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.