#![warn(missing_docs)]
#[cfg(test)]
#[macro_use]
extern crate lazy_static;
use std::{
os::raw::c_int,
sync::{
atomic::{AtomicUsize, Ordering},
Arc,
},
};
use enet_sys::{enet_deinitialize, enet_host_create, enet_initialize, enet_linked_version};
mod address;
mod event;
mod host;
mod packet;
mod peer;
pub use enet_sys::ENetVersion as EnetVersion;
pub use crate::{
address::Address,
event::Event,
host::{BandwidthLimit, ChannelLimit, Host},
packet::{Packet, PacketMode},
peer::{Peer, PeerPacket, PeerState},
};
const ENET_UNINITIALIZED: usize = 1;
const ENET_INITIALIZED: usize = 2;
const ENET_DEINITIALIZED: usize = 3;
static ENET_STATUS: AtomicUsize = AtomicUsize::new(ENET_UNINITIALIZED);
#[derive(Debug, Clone)]
struct EnetKeepAlive;
#[derive(Debug, Clone)]
pub struct Enet {
keep_alive: Arc<EnetKeepAlive>,
}
#[derive(thiserror::Error, Debug)]
#[error("enet failure, returned '{}'", .0)]
pub struct Error(pub c_int);
#[derive(thiserror::Error, Debug)]
pub enum InitializationError {
#[error("ENet has already been initialized before")]
AlreadyInitialized,
#[error("ENet has already been deinitialized before")]
AlreadyDeinitialized,
#[error("enet_initialize failed (with '{}')", .0)]
Error(c_int),
}
impl Enet {
pub fn new() -> Result<Enet, InitializationError> {
match ENET_STATUS.compare_exchange(
ENET_UNINITIALIZED,
ENET_INITIALIZED,
Ordering::SeqCst,
Ordering::SeqCst,
) {
Ok(_) => (),
Err(ENET_INITIALIZED) => return Err(InitializationError::AlreadyInitialized),
Err(ENET_DEINITIALIZED) => return Err(InitializationError::AlreadyDeinitialized),
Err(u) => panic!(
"enet-rs internal error; unexpected value in ENET_STATUS (new): {}",
u
),
};
let r = unsafe { enet_initialize() };
if r != 0 {
return Err(InitializationError::Error(r));
}
Ok(Enet {
keep_alive: Arc::new(EnetKeepAlive),
})
}
pub fn create_host<T>(
&self,
address: Option<&Address>,
max_peer_count: enet_sys::size_t,
max_channel_count: ChannelLimit,
incoming_bandwidth: BandwidthLimit,
outgoing_bandwidth: BandwidthLimit,
) -> Result<Host<T>, Error> {
let addr = address.map(Address::to_enet_address);
let inner = unsafe {
enet_host_create(
addr.as_ref()
.map(|p| p as *const _)
.unwrap_or(std::ptr::null()),
max_peer_count,
max_channel_count.to_enet_val(),
incoming_bandwidth.to_enet_u32(),
outgoing_bandwidth.to_enet_u32(),
)
};
if inner.is_null() {
return Err(Error(0));
}
Ok(Host::new(self.keep_alive.clone(), inner))
}
}
pub fn linked_version() -> EnetVersion {
unsafe { enet_linked_version() }
}
impl Drop for EnetKeepAlive {
fn drop(&mut self) {
match ENET_STATUS.compare_exchange(
ENET_INITIALIZED,
ENET_DEINITIALIZED,
Ordering::SeqCst,
Ordering::SeqCst,
) {
Ok(_) => (),
Err(other) => panic!(
"enet-rs internal error; unexpected value in ENET_STATUS (drop): {}",
other
),
};
unsafe {
enet_deinitialize();
}
}
}
#[cfg(test)]
mod tests {
use super::{BandwidthLimit, ChannelLimit, Enet};
lazy_static! {
static ref ENET: Enet = Enet::new().unwrap();
}
#[test]
fn test_enet_new() {
let _ = *ENET; assert!(Enet::new().is_err());
}
#[test]
fn test_host_create_localhost() {
use std::net::Ipv4Addr;
use crate::Address;
let enet = &ENET;
enet.create_host::<()>(
Some(&Address::new(Ipv4Addr::LOCALHOST, 12345)),
1,
ChannelLimit::Maximum,
BandwidthLimit::Unlimited,
BandwidthLimit::Unlimited,
)
.unwrap();
}
}