tentacli 15.2.3

Framework for building extensible network protocol clients via modular plugins.
use async_trait::async_trait;
use binrw::BinRead;
use regex::Regex;
use serde::Serialize;
use std::fmt::{Display, Formatter};
use std::sync::Arc;
use tokio::sync::RwLock;

use crate::client::prelude::*;
use crate::plugins::wow::wotlk::config::Config;
use crate::plugins::wow::wotlk::{login, realm};

#[derive(Packet, BinRead, Serialize, FieldsMetadata)]
#[br(little)]
pub struct Incoming {
    skip: [u8; 6],
    realms_count: u16,
    #[br(count = realms_count)]
    realms: Vec<Realm>,
}

pub struct Handler;

#[async_trait]
impl PacketHandler for Handler {
    async fn handle(
        &mut self,
        packet: &mut Packet,
        _: Arc<RwLock<CtxMap>>,
    ) -> anyhow::Result<Vec<HandlerOutput>> {
        let mut outputs = vec![];
        let config: Config = ConfigParser::parse_from_file("wow/wotlk/connection.toml")?;
        let autoselect = config
            .autoselect
            .ok_or_else(|| anyhow::anyhow!("Missing [autoselect]"))?;
        let Incoming { realms, .. } = Incoming::unpack(packet)?;

        if realms.is_empty() {
            return Ok(vec![HandlerOutput::Messages(vec![Message {
                msg_type: MsgType::Error,
                text: "Realmlist is empty".to_string(),
            }])]);
        }

        let items = Arc::new(realms.iter().map(|r| r.to_string()).collect());

        if !autoselect.realm_name.is_empty() {
            let re = Regex::new(&autoselect.realm_name)?;
            if let Some(realm) = realms.into_iter().find(|item| re.is_match(&item.name[..])) {
                let Realm {
                    name,
                    address,
                    server_id,
                    ..
                } = realm;
                let target: ServerLabel = realm::PLUGIN_LABEL;

                outputs.extend(vec![
                    HandlerOutput::Messages(vec![Message {
                        msg_type: MsgType::Info,
                        text: format!("Connecting to \"{name}\"-realm"),
                    }]),
                    HandlerOutput::Requests(vec![
                        Request::Connect(target, address.to_string()),
                        Request::SetContext(Some(Box::new(move |ctx: &mut CtxMap| {
                            ctx.insert(ServerId(server_id));
                        }))),
                        Request::Drop(login::PLUGIN_LABEL),
                    ]),
                ]);
            }
        } else {
            outputs.push(HandlerOutput::Requests(vec![Request::InitChoice(
                ChoiceItems(items),
                Some(Box::new(move |ids: Vec<usize>| {
                    let selected = &realms[ids[0]];
                    let name: String = selected.name.to_string();
                    let address: String = selected.address.to_string();
                    let server_id: u8 = selected.server_id;
                    let target: ServerLabel = realm::PLUGIN_LABEL;

                    Ok(vec![
                        HandlerOutput::Messages(vec![Message {
                            msg_type: MsgType::Info,
                            text: format!("Connecting to \"{name}\"-realm"),
                        }]),
                        HandlerOutput::Requests(vec![
                            Request::Connect(target, address),
                            Request::SetContext(Some(Box::new(move |ctx: &mut CtxMap| {
                                ctx.insert(ServerId(server_id));
                            }))),
                            Request::Drop(login::PLUGIN_LABEL),
                        ]),
                    ])
                })),
            )]));
        }

        Ok(outputs)
    }
}

pub struct ServerId(pub u8);

#[derive(BinRead, Serialize, FieldsMetadata, Debug, Default)]
#[br(little)]
pub struct Realm {
    pub icon: u8,
    pub lock: u8,
    pub flags: u8,
    pub name: NullTerminated<String>,
    pub address: NullTerminated<String>,
    pub population: f32,
    pub characters_amount: u8,
    pub timezone: u8,
    pub server_id: u8,
}

impl Display for Realm {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "[{}]({}) #id: {}, #pop: {}, #tz: {}, #chars: {}",
            self.name,
            self.address,
            self.server_id,
            self.population,
            self.timezone,
            self.characters_amount,
        )
    }
}