use bevy::prelude::*;
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Reflect)]
pub struct PlayerId(pub u8);
impl PlayerId {
#[must_use]
pub const fn new(id: u8) -> Self {
Self(id)
}
#[must_use]
pub const fn id(self) -> u8 {
self.0
}
}
#[derive(Debug, Clone, Copy, Component, Reflect)]
pub struct Player {
pub id: PlayerId,
pub active: bool,
}
impl Player {
#[must_use]
pub const fn new(id: u8) -> Self {
Self {
id: PlayerId(id),
active: true,
}
}
#[must_use]
pub fn one() -> Self {
Self::new(0)
}
#[must_use]
pub fn two() -> Self {
Self::new(1)
}
}
#[derive(Debug, Clone, Resource)]
pub struct ControllerOwnership {
pub owners: HashMap<Entity, PlayerId>,
pub assignments: HashMap<PlayerId, Entity>,
pub auto_assign: bool,
}
impl Default for ControllerOwnership {
fn default() -> Self {
Self {
owners: HashMap::new(),
assignments: HashMap::new(),
auto_assign: true,
}
}
}
impl ControllerOwnership {
pub fn assign(&mut self, gamepad: Entity, player: PlayerId) {
if let Some(old_gamepad) = self.assignments.insert(player, gamepad) {
self.owners.remove(&old_gamepad);
}
self.owners.insert(gamepad, player);
}
pub fn unassign_gamepad(&mut self, gamepad: Entity) {
if let Some(player) = self.owners.remove(&gamepad) {
self.assignments.remove(&player);
}
}
pub fn unassign_player(&mut self, player: PlayerId) {
if let Some(gamepad) = self.assignments.remove(&player) {
self.owners.remove(&gamepad);
}
}
#[must_use]
pub fn get_owner(&self, gamepad: Entity) -> Option<PlayerId> {
self.owners.get(&gamepad).copied()
}
#[must_use]
pub fn get_gamepad(&self, player: PlayerId) -> Option<Entity> {
self.assignments.get(&player).copied()
}
#[must_use]
pub fn is_assigned(&self, gamepad: Entity) -> bool {
self.owners.contains_key(&gamepad)
}
#[must_use]
pub fn get_unassigned(&self, all_gamepads: &[Entity]) -> Vec<Entity> {
all_gamepads
.iter()
.filter(|g| !self.is_assigned(**g))
.copied()
.collect()
}
}
#[derive(Debug, Clone, Message)]
pub struct ControllerAssigned {
pub gamepad: Entity,
pub player: PlayerId,
}
#[derive(Debug, Clone, Message)]
pub struct ControllerUnassigned {
pub gamepad: Entity,
pub player: PlayerId,
}
#[derive(Debug, Clone, Message)]
pub struct AssignControllerRequest {
pub gamepad: Entity,
pub player: PlayerId,
}
pub fn handle_assignment_requests(
mut requests: MessageReader<AssignControllerRequest>,
mut ownership: ResMut<ControllerOwnership>,
mut assigned_events: MessageWriter<ControllerAssigned>,
) {
for request in requests.read() {
ownership.assign(request.gamepad, request.player);
assigned_events.write(ControllerAssigned {
gamepad: request.gamepad,
player: request.player,
});
}
}
pub fn auto_assign_controllers(
mut ownership: ResMut<ControllerOwnership>,
gamepads: Query<Entity, Added<Gamepad>>,
mut assigned_events: MessageWriter<ControllerAssigned>,
) {
if !ownership.auto_assign {
return;
}
for gamepad in gamepads.iter() {
if ownership.is_assigned(gamepad) {
continue;
}
for player_id in 0..4 {
let player = PlayerId(player_id);
if ownership.get_gamepad(player).is_none() {
ownership.assign(gamepad, player);
assigned_events.write(ControllerAssigned { gamepad, player });
break;
}
}
}
}
pub fn handle_controller_disconnections(
mut ownership: ResMut<ControllerOwnership>,
mut removed_gamepads: RemovedComponents<Gamepad>,
mut unassigned_events: MessageWriter<ControllerUnassigned>,
) {
for gamepad in removed_gamepads.read() {
if let Some(player) = ownership.get_owner(gamepad) {
ownership.unassign_gamepad(gamepad);
unassigned_events.write(ControllerUnassigned { gamepad, player });
}
}
}
pub(crate) fn register_multiplayer_types(app: &mut App) {
app.register_type::<PlayerId>()
.register_type::<Player>()
.init_resource::<ControllerOwnership>()
.add_message::<ControllerAssigned>()
.add_message::<ControllerUnassigned>()
.add_message::<AssignControllerRequest>();
}
pub(crate) fn add_multiplayer_systems(app: &mut App) {
app.add_systems(
Update,
(
handle_assignment_requests,
auto_assign_controllers,
handle_controller_disconnections,
)
.chain(),
);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_player_id_new() {
let id = PlayerId::new(3);
assert_eq!(id.id(), 3);
}
#[test]
fn test_player_new() {
let player = Player::new(2);
assert_eq!(player.id.id(), 2);
assert!(player.active);
}
#[test]
fn test_player_one_two() {
let p1 = Player::one();
let p2 = Player::two();
assert_eq!(p1.id.id(), 0);
assert_eq!(p2.id.id(), 1);
assert!(p1.active);
assert!(p2.active);
}
#[test]
fn test_controller_ownership_default() {
let ownership = ControllerOwnership::default();
assert_eq!(ownership.owners.len(), 0);
assert_eq!(ownership.assignments.len(), 0);
assert!(ownership.auto_assign);
}
#[test]
fn test_controller_ownership_assign() {
let mut ownership = ControllerOwnership::default();
let gamepad = Entity::from_bits(100);
let player = PlayerId::new(0);
ownership.assign(gamepad, player);
assert_eq!(ownership.get_owner(gamepad), Some(player));
assert_eq!(ownership.get_gamepad(player), Some(gamepad));
}
#[test]
fn test_controller_ownership_reassign() {
let mut ownership = ControllerOwnership::default();
let gamepad1 = Entity::from_bits(100);
let gamepad2 = Entity::from_bits(200);
let player = PlayerId::new(0);
ownership.assign(gamepad1, player);
ownership.assign(gamepad2, player);
assert_eq!(ownership.get_gamepad(player), Some(gamepad2));
assert_eq!(ownership.get_owner(gamepad1), None);
}
#[test]
fn test_controller_ownership_unassign_gamepad() {
let mut ownership = ControllerOwnership::default();
let gamepad = Entity::from_bits(100);
let player = PlayerId::new(0);
ownership.assign(gamepad, player);
ownership.unassign_gamepad(gamepad);
assert_eq!(ownership.get_owner(gamepad), None);
assert_eq!(ownership.get_gamepad(player), None);
}
#[test]
fn test_controller_ownership_unassign_player() {
let mut ownership = ControllerOwnership::default();
let gamepad = Entity::from_bits(100);
let player = PlayerId::new(0);
ownership.assign(gamepad, player);
ownership.unassign_player(player);
assert_eq!(ownership.get_owner(gamepad), None);
assert_eq!(ownership.get_gamepad(player), None);
}
#[test]
fn test_controller_ownership_get_methods() {
let mut ownership = ControllerOwnership::default();
let gamepad = Entity::from_bits(123);
let player = PlayerId::new(1);
ownership.assign(gamepad, player);
assert_eq!(ownership.get_owner(gamepad), Some(player));
assert_eq!(ownership.get_gamepad(player), Some(gamepad));
let other_gamepad = Entity::from_bits(999);
let other_player = PlayerId::new(99);
assert_eq!(ownership.get_owner(other_gamepad), None);
assert_eq!(ownership.get_gamepad(other_player), None);
}
#[test]
fn test_controller_assigned_event() {
let gamepad = Entity::from_bits(50);
let player = PlayerId::new(2);
let event = ControllerAssigned { gamepad, player };
assert_eq!(event.gamepad, gamepad);
assert_eq!(event.player, player);
}
#[test]
fn test_controller_unassigned_event() {
let gamepad = Entity::from_bits(50);
let player = PlayerId::new(2);
let event = ControllerUnassigned { gamepad, player };
assert_eq!(event.gamepad, gamepad);
assert_eq!(event.player, player);
}
#[test]
fn test_assign_controller_request() {
let gamepad = Entity::from_bits(75);
let player = PlayerId::new(3);
let request = AssignControllerRequest { gamepad, player };
assert_eq!(request.gamepad, gamepad);
assert_eq!(request.player, player);
}
}