Crate tentacli

source ·
Expand description

TentaCLI is embeddable, extendable console client for WoW 3.3.5a server.

You can use it directly by compiling with cargo build, or you can incorporate it as a library in your own application. Also you can implement own feature set and pass it to the run() method. See Feature trait and RunOptions.

What this client can do:

  • it can parse basic packet set, such as SMSG_MESSAGECHAT or SMSG_UPDATE_OBJECT
  • it allows you to login on any server, but you can enter the world only on servers without Warden anti-cheat
  • you can use autoselect options in config file to set default Realm/Character and avoid the step of selecting this data manually
  • if installed with ui feature (installed by default), it allows scrolling the packets history using keyboard and seeing the details for each packet
  • if installed with console feature, it will display only minimal output
  • if installed without any feature, client will output nothing (but you still can provide own output feature)
  • you can implement own packet processors and send them using custom features
  • you can pass external data storage to the tentacli using CreateOptions

§Examples

use std::collections::BTreeMap;
use anyhow::{Result as AnyResult};
use tokio::task::JoinHandle;

use tentacli::async_broadcast::{BroadcastSender, BroadcastReceiver};
use tentacli::{Client, CreateOptions, RunOptions};
use tentacli_traits::{Feature, FeatureError};
use tentacli_traits::types::{HandlerOutput, ProcessorFunction, ProcessorResult};

#[tokio::main]
async fn main() {
    #[derive(Default)]
    pub struct MyFeature {
        _receiver: Option<BroadcastReceiver<HandlerOutput>>,
        _sender: Option<BroadcastSender<HandlerOutput>>,
    }

    impl Feature for MyFeature {
        fn set_broadcast_channel(
            &mut self,
            sender: BroadcastSender<HandlerOutput>,
            receiver: BroadcastReceiver<HandlerOutput>
        ) {
            self._sender = Some(sender);
            self._receiver = Some(receiver);
        }

        fn get_tasks(&mut self) -> AnyResult<Vec<JoinHandle<()>>> {
            let mut receiver = self._receiver.as_mut().ok_or(FeatureError::ReceiverNotFound)?.clone();

            let handle_smth = || {
                tokio::spawn(async move {
                    loop {
                        if let Ok(output) = receiver.recv().await {
                            match output {
                                HandlerOutput::SuccessMessage(message, _) => {
                                    println!("{}", message);
                                }
                                _ => {}
                            }
                        }
                    }
                })
            };

            Ok(vec![handle_smth()])
        }

        fn get_login_processors(&self) -> Vec<ProcessorFunction> {
            vec![]
        }

        fn get_realm_processors(&self) -> Vec<ProcessorFunction> {
            vec![]
        }

        fn get_one_time_handler_maps(&self) -> Vec<BTreeMap<u16, ProcessorResult>> {
            vec![]
        }

        fn get_initial_processors(&self) -> Vec<ProcessorFunction> {
            vec![]
        }
    }

    let options = RunOptions {
        external_features: vec![Box::new(MyFeature::default())],
        account: "account_name",
        config_path: "./dir/another_dir/ConfigFileName.yml",
        dotenv_path: "./path/to/.env"
    };

    // ... pass options to the client
    // Client::new(CreateOptions::default()).run(options).await.unwrap();
}

Modules§

Structs§