Looking for a pattern of callback object registry


#1

Here’s an example:

use std::collections::HashMap;
use std::rc::Rc;
use std::cell::RefCell;

type Id = i32; // id of each player

enum UserInput {
    Punch(Id),
    GiveUp,
}

struct Player {
    battle : Option<Rc<RefCell<Battle>>>,
    hp : i32,
}

impl Player {
    fn new() -> Self {
        Player {
            battle : None,
            hp : 10
        }
    }
    fn user_input(&mut self, input : UserInput) {
        match input {
            UserInput::Punch(who) => {
                self.punch(who);
            }
            UserInput::GiveUp => {
                self.give_up();
            }
        }
    }
    fn punch(&mut self, enemy : Id) {
        match self.battle {
            Some(ref rc) => {
                match rc.borrow().players.get(&enemy) {
                    Some(player) => {
                        player.borrow_mut().hp -= 1;
                        println!("punches player {}, hp={}", enemy, player.borrow_mut().hp);
                    }
                    None => {
                        println!("has no enemy player {}!", enemy);
                    }
                }
            }
            None => {
                println!("has not in battle!");
            }
        }
    }
    fn give_up(&mut self) {
        println!("gives up!");
        self.hp = 0;
    }
}

struct Battle {
    players : HashMap<Id, Rc<RefCell<Player>>>
}

impl Battle {
    fn new() -> Self {
        Battle {
            players : HashMap::new()
        }
    }
    
    fn login(battle_ref : &Rc<RefCell<Battle>>, id : Id) {
        let player_ref = Rc::new(RefCell::new(Player::new()));
        player_ref.borrow_mut().battle = Some(battle_ref.clone());
        battle_ref.borrow_mut().players.insert(id, player_ref);
    }
    
    fn user_input(&self, user_id : Id, input : UserInput) {
        match self.players.get(&user_id) {
            Some(user) => {
                print!("player {} ", user_id);
                user.borrow_mut().user_input(input);
            }
            None => {
                println!("no such player {}!", user_id);
            }
        }
    
    }
}

fn main() {
    let battle_ref = Rc::new(RefCell::new(Battle::new()));
    Battle::login(&battle_ref, 1);
    Battle::login(&battle_ref, 2);
    battle_ref.borrow().user_input(1, UserInput::Punch(2));
    battle_ref.borrow().user_input(2, UserInput::GiveUp);
}

My idea is that a registry class ‘Battle’ organizes a set of handler class ‘Player’, and the Player class will do its job of whatever game logic. But things become complicated when such game logic requires to use the registry. I find a way to solve this but it seems insane to use a dozen of '<>'s for such a basic structure. You know my ‘battle’ is just a sugar for Option<Rc<RefCell<HashMap<Id, Rc<RefCell<Player>>>>>>.
My questions:
Is my way a good way to implement this kind of registry?
And what’s the best/common pattern of ‘registry’?
And can you reform my stupid Battle::login() to take a common &mut self or something?