lightyear_core 0.26.4

Core types shared by lightyear crates
Documentation
//! Module to handle the various possible ClientIds
use core::fmt::{Display, Formatter};
use core::net::SocketAddr;
use lightyear_serde::reader::{ReadInteger, Reader};
use lightyear_serde::writer::WriteInteger;
use lightyear_serde::{SerializationError, ToBytes};
use serde::{Deserialize, Serialize};

use bevy_derive::Deref;
use bevy_ecs::component::Component;
use bevy_reflect::Reflect;

/// Stores the PeerId of the local peer for the connection
#[derive(
    Debug, Component, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Reflect, Deref,
)]
pub struct LocalId(pub PeerId);

impl Display for LocalId {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        core::fmt::Debug::fmt(&self.0, f)
    }
}

/// Stores the PeerId of the remote peer that we are connected to
#[derive(
    Debug, Component, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Reflect, Deref,
)]
pub struct RemoteId(pub PeerId);

impl Display for RemoteId {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        core::fmt::Debug::fmt(&self.0, f)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Reflect)]
pub enum PeerId {
    Entity(u64),
    /// A raw connection where the IO Link is used to associate a long-term id.
    /// The id is the SocketAddr of the link.
    #[reflect(ignore)]
    Raw(SocketAddr),
    /// A client id that is unique between netcode connections
    Netcode(u64),
    /// The client id of a steam user
    Steam(u64),
    /// A local client to use when running in HostServer mode
    Local(u64),
    /// Refers to the server
    Server,
}

impl ToBytes for PeerId {
    fn bytes_len(&self) -> usize {
        match self {
            PeerId::Entity(_) => 1 + 8,
            PeerId::Raw(socket_addr) => {
                // 1 byte for variant + address bytes + 2 bytes for port
                match socket_addr {
                    SocketAddr::V4(_) => 1 + 4 + 2,
                    SocketAddr::V6(_) => 1 + 16 + 2,
                }
            }
            PeerId::Netcode(_) => 1 + 8,
            PeerId::Steam(_) => 1 + 8,
            PeerId::Local(_) => 1 + 8,
            PeerId::Server => 1,
        }
    }

    fn to_bytes(&self, buffer: &mut impl WriteInteger) -> Result<(), SerializationError> {
        match self {
            PeerId::Entity(id) => {
                buffer.write_u8(0)?;
                buffer.write_u64(*id)?;
            }
            PeerId::Raw(socket) => {
                match socket {
                    SocketAddr::V4(v4) => {
                        buffer.write_u8(1)?;
                        // Write IPv4 bytes
                        for byte in v4.ip().octets().iter() {
                            buffer.write_u8(*byte)?;
                        }
                        // Write port
                        buffer.write_u16(v4.port())?;
                    }
                    SocketAddr::V6(v6) => {
                        buffer.write_u8(2)?;
                        // Write IPv6 bytes
                        for segment in v6.ip().segments().iter() {
                            buffer.write_u16(*segment)?;
                        }
                        // Write port
                        buffer.write_u16(v6.port())?;
                    }
                }
            }
            PeerId::Netcode(id) => {
                buffer.write_u8(3)?;
                buffer.write_u64(*id)?;
            }
            PeerId::Steam(id) => {
                buffer.write_u8(4)?;
                buffer.write_u64(*id)?;
            }
            PeerId::Local(id) => {
                buffer.write_u8(5)?;
                buffer.write_u64(*id)?;
            }
            PeerId::Server => {
                buffer.write_u8(6)?;
            }
        }
        Ok(())
    }

    fn from_bytes(buffer: &mut Reader) -> Result<Self, SerializationError>
    where
        Self: Sized,
    {
        match buffer.read_u8()? {
            0 => Ok(PeerId::Entity(buffer.read_u64()?)),
            1 => {
                // Read IPv4 address
                let a = buffer.read_u8()?;
                let b = buffer.read_u8()?;
                let c = buffer.read_u8()?;
                let d = buffer.read_u8()?;
                let port = buffer.read_u16()?;
                let addr = SocketAddr::new(
                    core::net::IpAddr::V4(core::net::Ipv4Addr::new(a, b, c, d)),
                    port,
                );
                Ok(PeerId::Raw(addr))
            }
            2 => {
                // Read IPv6 address
                let mut segments = [0u16; 8];
                for segment in &mut segments {
                    *segment = buffer.read_u16()?;
                }
                let port = buffer.read_u16()?;
                let addr = SocketAddr::new(
                    core::net::IpAddr::V6(core::net::Ipv6Addr::from(segments)),
                    port,
                );
                Ok(PeerId::Raw(addr))
            }
            3 => Ok(PeerId::Netcode(buffer.read_u64()?)),
            4 => Ok(PeerId::Steam(buffer.read_u64()?)),
            5 => Ok(PeerId::Local(buffer.read_u64()?)),
            6 => Ok(PeerId::Server),
            _ => Err(SerializationError::InvalidValue),
        }
    }
}

impl PeerId {
    // TODO: add impl From<ClientId> for u64?
    /// Convert a ClientId to a u64 representation
    pub fn to_bits(&self) -> u64 {
        match self {
            PeerId::Entity(x) => *x,
            PeerId::Raw(socket) => {
                // Create a simple hash for the IP address
                // Just for differentiation - not meant to be a secure hash
                match socket {
                    SocketAddr::V4(v4) => {
                        let octets = v4.ip().octets();
                        let mut result: u64 = 0x0200; // Prefix for IPv4
                        result |= (octets[0] as u64) << 24;
                        result |= (octets[1] as u64) << 16;
                        result |= (octets[2] as u64) << 8;
                        result |= octets[3] as u64;
                        result |= (v4.port() as u64) << 32;
                        result
                    }
                    SocketAddr::V6(v6) => {
                        // Simple hash for IPv6 - fold the segments
                        let segments = v6.ip().segments();
                        let mut hash: u64 = 0x0600; // Prefix for IPv6
                        for (i, &segment) in segments.iter().enumerate() {
                            hash ^= (segment as u64) << ((i % 4) * 16);
                        }
                        hash ^= (v6.port() as u64) << 48;
                        hash
                    }
                }
            }
            PeerId::Netcode(x) => *x,
            PeerId::Steam(x) => *x,
            PeerId::Local(x) => *x,
            PeerId::Server => 1,
        }
    }

    pub fn is_local(&self) -> bool {
        matches!(self, PeerId::Local(_))
    }
}

impl core::fmt::Display for PeerId {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        core::fmt::Debug::fmt(self, f)
        // match self {
        //     ClientId::Netcode(id) => write!(f, "NetcodeClientId({})", id),
        //     ClientId::Steam(id) => write!(f, "SteamClientId({})", id),
        //     ClientId::LocalClient => write!(f, "LocalClientId"),
        // }
    }
}