twgame 0.11.0

DDNet physics implementation
Documentation
//! Id types for Tees and Players for accessing in slot map
//! and identifying from Teehistorian and useable for TwSnap `SnapId`

use slotmap::{new_key_type, Key, KeyData};
use twgame_core::twsnap::SnapId;
use vek::num_integer::Integer;

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TeamId {
    id: i32,
}

impl TeamId {
    pub fn from_i32(id: i32) -> Self {
        Self { id }
    }
    pub fn team0() -> Self {
        Self { id: 0 }
    }
    pub fn is_team0(&self) -> bool {
        self.id == 0
    }
    pub fn to_i32(self) -> i32 {
        self.id
    }
}

new_key_type! { pub struct PlayerUid; }

/// this struct essantially simulates a SlotMap with value () to insert keys
/// with abitrary index to SecondarySlotMaps. This is likely dangerous.
/// SlotMap doesn't give any guarantees for the values of the keys.
/// Therefore I've fixed the slotmap version to a specific version.
/// Care need to be taken when updating slotmap.
#[derive(Debug, Default)]
pub struct PlayerUidGenerator {
    players: Vec<u32>,
}

impl PlayerUidGenerator {
    pub fn player_join(&mut self, idx: u32) -> PlayerUid {
        if idx as usize >= self.players.len() {
            self.players.resize(idx as usize + 1, 0);
        }
        let version = self.players.get_mut(idx as usize).unwrap();
        assert!(
            version.is_even(),
            "player_join called for already existing player"
        );
        *version += 1;

        PlayerUid::from_idx_version(idx, *version)
    }
    pub fn player_leave(&mut self, idx: u32) -> PlayerUid {
        let version = self
            .players
            .get_mut(idx as usize)
            .expect("player_leave called for non-existing player");
        assert!(
            version.is_odd(),
            "player_leave called for non-existing player"
        );
        let pid = PlayerUid::from_idx_version(idx, *version);
        *version += 1;
        pid
    }
    pub fn get(&self, idx: u32) -> Option<PlayerUid> {
        let version = self.players.get(idx as usize)?;
        if version.is_even() {
            return None;
        }
        Some(PlayerUid::from_idx_version(idx, *version))
    }
}

// PlayerUid interacts with Snap, so need to some helpers
impl PlayerUid {
    pub fn snap_id(&self) -> SnapId {
        let id = self.data().as_ffi() & 0xffff_ffff;
        SnapId((id as u32).checked_sub(1).unwrap())
    }

    fn from_idx_version(idx: u32, version: u32) -> Self {
        let key = (u64::from(version) << 32) | u64::from(idx + 1);
        let mut this = Self::null();
        this.0 = KeyData::from_ffi(key);
        this
    }
}

#[derive(Debug, Clone)]
pub struct MapSnapIdGenerator {
    next_projectile_id: u16,
    next_laser_id: u16,
    next_pickup_id: u16,
}

impl Default for MapSnapIdGenerator {
    fn default() -> Self {
        Self::new()
    }
}

impl MapSnapIdGenerator {
    pub fn new() -> Self {
        Self {
            next_projectile_id: 0,
            next_laser_id: 0,
            next_pickup_id: 0,
        }
    }
    pub fn finalize(self) -> SnapIdGenerator {
        SnapIdGenerator {
            num_map_projectile: self.next_projectile_id,
            next_projectile_id: self.next_projectile_id,
            num_map_laser: self.next_laser_id,
            next_laser_id: self.next_laser_id,
        }
    }

    pub fn next_projectile(&mut self) -> SnapId {
        let snap_id = SnapId(self.next_projectile_id as u32);
        self.next_projectile_id = self
            .next_projectile_id
            .checked_add(1)
            .expect("too many map projectiles");
        snap_id
    }

    pub fn next_laser(&mut self) -> SnapId {
        let snap_id = SnapId(self.next_laser_id as u32);
        self.next_laser_id = self
            .next_laser_id
            .checked_add(1)
            .expect("too many map laser");
        snap_id
    }

    pub fn next_pickup(&mut self) -> SnapId {
        let snap_id = SnapId(self.next_pickup_id as u32);
        self.next_pickup_id = self
            .next_pickup_id
            .checked_add(1)
            .expect("too many map pickups");
        snap_id
    }
}

#[derive(Debug)]
/// Generates SnapIds globally throughout the world in a DDNet snap compatible way
/// Assumes that projectiles/laser don't live long and wraps around. Allows to allocate
/// a few ids for map projectiles to skip over.
pub struct SnapIdGenerator {
    num_map_projectile: u16,
    next_projectile_id: u16,
    num_map_laser: u16,
    next_laser_id: u16,
}

impl SnapIdGenerator {
    pub fn next_projectile(&mut self) -> SnapId {
        let snap_id = SnapId(self.next_projectile_id as u32);
        self.next_projectile_id = self
            .next_projectile_id
            .checked_add(1)
            .unwrap_or(self.num_map_projectile);
        snap_id
    }

    pub fn next_laser(&mut self) -> SnapId {
        let snap_id = SnapId(self.next_laser_id as u32);
        self.next_laser_id = self
            .next_laser_id
            .checked_add(1)
            .unwrap_or(self.num_map_laser);
        snap_id
    }
}