mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'aweinstock/botclient' into 'master'
move botclient to client/src/bin/bot See merge request veloren/veloren!1886
This commit is contained in:
71
Cargo.lock
generated
71
Cargo.lock
generated
@ -1510,6 +1510,12 @@ dependencies = [
|
|||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "endian-type"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "enum-iterator"
|
name = "enum-iterator"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
@ -1708,6 +1714,16 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fs2"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fsevent"
|
name = "fsevent"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@ -3137,6 +3153,15 @@ dependencies = [
|
|||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nibble_vec"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
|
||||||
|
dependencies = [
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nix"
|
name = "nix"
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
@ -3972,6 +3997,16 @@ version = "0.6.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb"
|
checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "radix_trie"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
|
||||||
|
dependencies = [
|
||||||
|
"endian-type",
|
||||||
|
"nibble_vec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.7.3"
|
version = "0.7.3"
|
||||||
@ -4333,6 +4368,29 @@ dependencies = [
|
|||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustyline"
|
||||||
|
version = "8.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9e1b597fcd1eeb1d6b25b493538e5aa19629eb08932184b85fef931ba87e893"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"dirs-next",
|
||||||
|
"fs2",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"memchr",
|
||||||
|
"nix 0.20.0",
|
||||||
|
"radix_trie",
|
||||||
|
"scopeguard",
|
||||||
|
"smallvec",
|
||||||
|
"unicode-segmentation",
|
||||||
|
"unicode-width",
|
||||||
|
"utf8parse",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
@ -5410,6 +5468,12 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
@ -5462,20 +5526,27 @@ dependencies = [
|
|||||||
name = "veloren-client"
|
name = "veloren-client"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-channel",
|
||||||
"authc",
|
"authc",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
"clap",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
"image",
|
"image",
|
||||||
"num 0.4.0",
|
"num 0.4.0",
|
||||||
"rayon",
|
"rayon",
|
||||||
|
"ron",
|
||||||
|
"rustyline",
|
||||||
|
"serde",
|
||||||
"specs",
|
"specs",
|
||||||
|
"termcolor",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"vek 0.14.1",
|
"vek 0.14.1",
|
||||||
"veloren-common",
|
"veloren-common",
|
||||||
"veloren-common-base",
|
"veloren-common-base",
|
||||||
|
"veloren-common-ecs",
|
||||||
"veloren-common-net",
|
"veloren-common-net",
|
||||||
"veloren-common-sys",
|
"veloren-common-sys",
|
||||||
"veloren-network",
|
"veloren-network",
|
||||||
|
32
Cargo.toml
32
Cargo.toml
@ -2,22 +2,22 @@ cargo-features = ["named-profiles","profile-overrides"]
|
|||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"common",
|
"common",
|
||||||
"common/base",
|
"common/base",
|
||||||
"common/ecs",
|
"common/ecs",
|
||||||
"common/net",
|
"common/net",
|
||||||
"common/sys",
|
"common/sys",
|
||||||
"client",
|
"client",
|
||||||
"plugin/api",
|
"plugin/api",
|
||||||
"plugin/derive",
|
"plugin/derive",
|
||||||
"plugin/rt",
|
"plugin/rt",
|
||||||
"server",
|
"server",
|
||||||
"server-cli",
|
"server-cli",
|
||||||
"voxygen",
|
"voxygen",
|
||||||
"voxygen/anim",
|
"voxygen/anim",
|
||||||
"world",
|
"world",
|
||||||
"network",
|
"network",
|
||||||
"network/protocol"
|
"network/protocol"
|
||||||
]
|
]
|
||||||
|
|
||||||
# default profile for devs, fast to compile, okay enough to run, no debug information
|
# default profile for devs, fast to compile, okay enough to run, no debug information
|
||||||
|
@ -7,6 +7,7 @@ edition = "2018"
|
|||||||
[features]
|
[features]
|
||||||
simd = ["vek/platform_intrinsics"]
|
simd = ["vek/platform_intrinsics"]
|
||||||
plugins = ["common-sys/plugins"]
|
plugins = ["common-sys/plugins"]
|
||||||
|
bin_bot = ["common-ecs", "serde", "ron", "clap", "rustyline", "termcolor", "tracing-subscriber", "async-channel"]
|
||||||
|
|
||||||
default = ["simd"]
|
default = ["simd"]
|
||||||
|
|
||||||
@ -29,8 +30,24 @@ vek = { version = "=0.14.1", features = ["serde"] }
|
|||||||
hashbrown = { version = "0.9", features = ["rayon", "serde", "nightly"] }
|
hashbrown = { version = "0.9", features = ["rayon", "serde", "nightly"] }
|
||||||
authc = { git = "https://gitlab.com/veloren/auth.git", rev = "fb3dcbc4962b367253f8f2f92760ef44d2679c9a" }
|
authc = { git = "https://gitlab.com/veloren/auth.git", rev = "fb3dcbc4962b367253f8f2f92760ef44d2679c9a" }
|
||||||
|
|
||||||
|
#bot only
|
||||||
|
async-channel = { version = "1.6", optional = true }
|
||||||
|
common-ecs = { package = "veloren-common-ecs", path = "../common/ecs", optional = true }
|
||||||
|
serde = { version = "1.0", features = [ "rc", "derive" ], optional = true }
|
||||||
|
ron = { version = "0.6", default-features = false, optional = true }
|
||||||
|
clap = { version = "2.33", optional = true }
|
||||||
|
rustyline = { version = "8.0.0", optional = true }
|
||||||
|
## logging
|
||||||
|
termcolor = { version = "1.1", optional = true }
|
||||||
|
tracing-subscriber = {version = "0.2.3", default-features = false, features = ["env-filter", "fmt", "chrono", "ansi", "smallvec", "tracing-log"], optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tracing-subscriber = { version = "0.2.3", default-features = false, features = ["fmt", "chrono", "ansi", "smallvec"] }
|
tracing-subscriber = { version = "0.2.3", default-features = false, features = ["fmt", "chrono", "ansi", "smallvec"] }
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "chat-cli"
|
name = "chat-cli"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "bot"
|
||||||
|
#authors = ["Avi Weinstock <aweinstock314@gmail.com>"]
|
||||||
|
required-features = ["bin_bot"]
|
||||||
|
207
client/src/bin/bot/main.rs
Normal file
207
client/src/bin/bot/main.rs
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
#![feature(str_split_once)]
|
||||||
|
|
||||||
|
#[macro_use] extern crate serde;
|
||||||
|
|
||||||
|
use authc::AuthClient;
|
||||||
|
use common::{clock::Clock, comp};
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{sync::Arc, time::Duration};
|
||||||
|
use tokio::runtime::Runtime;
|
||||||
|
use tracing::{info, trace, warn};
|
||||||
|
use veloren_client::{addr::ConnectionArgs, Client};
|
||||||
|
|
||||||
|
mod settings;
|
||||||
|
mod tui;
|
||||||
|
|
||||||
|
use settings::Settings;
|
||||||
|
use tui::Cmd;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct BotCreds {
|
||||||
|
username: String,
|
||||||
|
password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
tui::init_logging();
|
||||||
|
|
||||||
|
let settings = Settings::load();
|
||||||
|
info!("Settings: {:?}", settings);
|
||||||
|
|
||||||
|
let (_tui, cmds) = tui::Tui::new();
|
||||||
|
let mut bc = BotClient::new(settings);
|
||||||
|
'outer: loop {
|
||||||
|
loop {
|
||||||
|
match cmds.try_recv() {
|
||||||
|
Ok(cmd) => bc.cmd(cmd),
|
||||||
|
Err(async_channel::TryRecvError::Empty) => break,
|
||||||
|
Err(async_channel::TryRecvError::Closed) => break 'outer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bc.tick();
|
||||||
|
}
|
||||||
|
info!("shutdown complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BotClient {
|
||||||
|
settings: Settings,
|
||||||
|
runtime: Arc<Runtime>,
|
||||||
|
menu_client: Client,
|
||||||
|
bot_clients: HashMap<String, Client>,
|
||||||
|
clock: Clock,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_client(runtime: &Arc<Runtime>, server: &str) -> Client {
|
||||||
|
let runtime2 = Arc::clone(&runtime);
|
||||||
|
let view_distance: Option<u32> = None;
|
||||||
|
runtime.block_on(async {
|
||||||
|
let connection_args = ConnectionArgs::resolve(server, false)
|
||||||
|
.await
|
||||||
|
.expect("DNS resolution failed");
|
||||||
|
Client::new(connection_args, view_distance, runtime2)
|
||||||
|
.await
|
||||||
|
.expect("Failed to connect to server")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BotClient {
|
||||||
|
pub fn new(settings: Settings) -> BotClient {
|
||||||
|
let runtime = Arc::new(Runtime::new().unwrap());
|
||||||
|
let menu_client: Client = make_client(&runtime, &settings.server);
|
||||||
|
let clock = Clock::new(Duration::from_secs_f64(1.0 / 60.0));
|
||||||
|
BotClient {
|
||||||
|
settings,
|
||||||
|
runtime,
|
||||||
|
menu_client,
|
||||||
|
bot_clients: HashMap::new(),
|
||||||
|
clock,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tick(&mut self) {
|
||||||
|
self.clock.tick();
|
||||||
|
for (username, client) in self.bot_clients.iter_mut() {
|
||||||
|
//trace!("cl {:?}: {:?}", username, client.character_list());
|
||||||
|
trace!(?username, "tick");
|
||||||
|
let msgs: Result<Vec<veloren_client::Event>, veloren_client::Error> =
|
||||||
|
client.tick(comp::ControllerInputs::default(), self.clock.dt(), |_| {});
|
||||||
|
/*trace!(
|
||||||
|
"msgs {:?}: {:?} {:?}",
|
||||||
|
username,
|
||||||
|
msgs,
|
||||||
|
client.character_list()
|
||||||
|
);*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cmd(&mut self, cmd: Cmd) {
|
||||||
|
match cmd {
|
||||||
|
Cmd::Register {
|
||||||
|
prefix,
|
||||||
|
password,
|
||||||
|
count,
|
||||||
|
} => self.handle_register(&prefix, &password, count),
|
||||||
|
Cmd::Login { prefix } => self.handle_login(&prefix),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_register(&mut self, prefix: &str, password: &str, count: Option<usize>) {
|
||||||
|
let usernames = match count {
|
||||||
|
Some(n) => (0..n)
|
||||||
|
.into_iter()
|
||||||
|
.map(|i| format!("{}{:03}", prefix, i))
|
||||||
|
.collect::<Vec<String>>(),
|
||||||
|
None => vec![prefix.to_string()],
|
||||||
|
};
|
||||||
|
info!("usernames: {:?}", usernames);
|
||||||
|
if let Some(auth_addr) = self.menu_client.server_info().auth_provider.as_ref() {
|
||||||
|
let (scheme, authority) = auth_addr.split_once("://").expect("invalid auth url");
|
||||||
|
let scheme = scheme
|
||||||
|
.parse::<authc::Scheme>()
|
||||||
|
.expect("invalid auth url scheme");
|
||||||
|
let authority = authority
|
||||||
|
.parse::<authc::Authority>()
|
||||||
|
.expect("invalid auth url authority");
|
||||||
|
|
||||||
|
let authc = AuthClient::new(scheme, authority).expect("couldn't connect to , insecure");
|
||||||
|
for username in usernames.iter() {
|
||||||
|
if self
|
||||||
|
.settings
|
||||||
|
.bot_logins
|
||||||
|
.iter()
|
||||||
|
.any(|x| &*x.username == &*username)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match self.runtime.block_on(authc.register(username, password)) {
|
||||||
|
Ok(()) => {
|
||||||
|
self.settings.bot_logins.push(BotCreds {
|
||||||
|
username: username.to_string(),
|
||||||
|
password: password.to_string(),
|
||||||
|
});
|
||||||
|
self.settings.save_to_file_warn();
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
warn!("error registering {:?}: {:?}", username, e);
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!("Server's auth_provider is None");
|
||||||
|
}
|
||||||
|
info!("register done");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_login(&mut self, prefix: &str) {
|
||||||
|
let creds: Vec<_> = self
|
||||||
|
.settings
|
||||||
|
.bot_logins
|
||||||
|
.iter()
|
||||||
|
.filter(|x| x.username.starts_with(prefix))
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
for cred in creds.iter() {
|
||||||
|
let runtime = Arc::clone(&self.runtime);
|
||||||
|
|
||||||
|
let server = self.settings.server.clone();
|
||||||
|
let client = self
|
||||||
|
.bot_clients
|
||||||
|
.entry(cred.username.clone())
|
||||||
|
.or_insert_with(|| make_client(&runtime, &server));
|
||||||
|
|
||||||
|
// TODO: log the clients in in parallel instead of in series
|
||||||
|
if let Err(e) = runtime.block_on(client.register(
|
||||||
|
cred.username.clone(),
|
||||||
|
cred.password.clone(),
|
||||||
|
|_| true,
|
||||||
|
)) {
|
||||||
|
warn!("error logging in {:?}: {:?}", cred.username, e);
|
||||||
|
}
|
||||||
|
/*let body = comp::body::biped_large::Body {
|
||||||
|
species: comp::body::biped_large::Species::Dullahan,
|
||||||
|
body_type: comp::body::biped_large::BodyType::Male,
|
||||||
|
};*/
|
||||||
|
let body = comp::body::humanoid::Body {
|
||||||
|
species: comp::body::humanoid::Species::Human,
|
||||||
|
body_type: comp::body::humanoid::BodyType::Male,
|
||||||
|
hair_style: 0,
|
||||||
|
beard: 0,
|
||||||
|
eyes: 0,
|
||||||
|
accessory: 0,
|
||||||
|
hair_color: 0,
|
||||||
|
skin: 0,
|
||||||
|
eye_color: 0,
|
||||||
|
};
|
||||||
|
client.create_character(
|
||||||
|
cred.username.clone(),
|
||||||
|
Some("common.items.weapons.sword.starter".to_string()),
|
||||||
|
body.into(),
|
||||||
|
);
|
||||||
|
//client.create_character(cred.username.clone(),
|
||||||
|
// Some("common.items.debug.admin_stick".to_string()), body.into());
|
||||||
|
}
|
||||||
|
info!("login done");
|
||||||
|
}
|
||||||
|
}
|
74
client/src/bin/bot/settings.rs
Normal file
74
client/src/bin/bot/settings.rs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
use super::BotCreds;
|
||||||
|
use std::{fs, path::PathBuf};
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
|
pub fn data_dir() -> PathBuf {
|
||||||
|
let mut path = common_base::userdata_dir_workspace!();
|
||||||
|
path.push("botclient");
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Settings {
|
||||||
|
pub server: String,
|
||||||
|
pub bot_logins: Vec<BotCreds>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Settings {
|
||||||
|
fn default() -> Settings {
|
||||||
|
Settings {
|
||||||
|
server: "localhost".to_string(),
|
||||||
|
bot_logins: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Settings {
|
||||||
|
pub fn load() -> Self {
|
||||||
|
let path = Self::get_settings_path();
|
||||||
|
|
||||||
|
if let Ok(file) = fs::File::open(&path) {
|
||||||
|
match ron::de::from_reader(file) {
|
||||||
|
Ok(s) => return s,
|
||||||
|
Err(e) => {
|
||||||
|
warn!(?e, "Failed to parse setting file! Fallback to default.");
|
||||||
|
// Rename the corrupted settings file
|
||||||
|
let mut new_path = path.to_owned();
|
||||||
|
new_path.pop();
|
||||||
|
new_path.push("settings.invalid.ron");
|
||||||
|
if let Err(e) = std::fs::rename(&path, &new_path) {
|
||||||
|
warn!(?e, ?path, ?new_path, "Failed to rename settings file.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This is reached if either:
|
||||||
|
// - The file can't be opened (presumably it doesn't exist)
|
||||||
|
// - Or there was an error parsing the file
|
||||||
|
let default_settings = Self::default();
|
||||||
|
default_settings.save_to_file_warn();
|
||||||
|
default_settings
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_to_file_warn(&self) {
|
||||||
|
if let Err(e) = self.save_to_file() {
|
||||||
|
warn!(?e, "Failed to save settings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_to_file(&self) -> std::io::Result<()> {
|
||||||
|
let path = Self::get_settings_path();
|
||||||
|
if let Some(dir) = path.parent() {
|
||||||
|
fs::create_dir_all(dir)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ron = ron::ser::to_string_pretty(self, ron::ser::PrettyConfig::default()).unwrap();
|
||||||
|
fs::write(path, ron.as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_settings_path() -> PathBuf {
|
||||||
|
let mut path = data_dir();
|
||||||
|
path.push("settings.ron");
|
||||||
|
path
|
||||||
|
}
|
||||||
|
}
|
114
client/src/bin/bot/tui.rs
Normal file
114
client/src/bin/bot/tui.rs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
use clap::{App, AppSettings, Arg, SubCommand};
|
||||||
|
use std::{thread, time::Duration};
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
|
pub fn init_logging() {
|
||||||
|
use termcolor::{ColorChoice, StandardStream};
|
||||||
|
use tracing::Level;
|
||||||
|
use tracing_subscriber::{filter::LevelFilter, EnvFilter, FmtSubscriber};
|
||||||
|
const RUST_LOG_ENV: &str = "RUST_LOG";
|
||||||
|
let filter = EnvFilter::from_env(RUST_LOG_ENV).add_directive(LevelFilter::INFO.into());
|
||||||
|
let subscriber = FmtSubscriber::builder()
|
||||||
|
.with_max_level(Level::ERROR)
|
||||||
|
.with_env_filter(filter);
|
||||||
|
|
||||||
|
subscriber
|
||||||
|
.with_writer(|| StandardStream::stdout(ColorChoice::Auto))
|
||||||
|
.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Cmd {
|
||||||
|
Register {
|
||||||
|
prefix: String,
|
||||||
|
password: String,
|
||||||
|
count: Option<usize>,
|
||||||
|
},
|
||||||
|
Login {
|
||||||
|
prefix: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Tui {
|
||||||
|
_handle: thread::JoinHandle<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tui {
|
||||||
|
pub fn new() -> (Self, async_channel::Receiver<Cmd>) {
|
||||||
|
let (mut commands_s, commands_r) = async_channel::unbounded();
|
||||||
|
|
||||||
|
let handle = thread::spawn(move || {
|
||||||
|
thread::sleep(Duration::from_millis(20));
|
||||||
|
let mut readline = rustyline::Editor::<()>::new();
|
||||||
|
loop {
|
||||||
|
match readline.readline("\n\nbotclient> ") {
|
||||||
|
Ok(cmd) => {
|
||||||
|
let keep_going = Self::process_command(&cmd, &mut commands_s);
|
||||||
|
readline.add_history_entry(cmd);
|
||||||
|
if !keep_going {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
(Self { _handle: handle }, commands_r)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_command(cmd: &str, command_s: &mut async_channel::Sender<Cmd>) -> bool {
|
||||||
|
let matches = App::new("veloren-botclient")
|
||||||
|
.version(common::util::DISPLAY_VERSION_LONG.as_str())
|
||||||
|
.author("The veloren devs <https://gitlab.com/veloren/veloren>")
|
||||||
|
.about("The veloren bot client allows logging in as a horde of bots for load-testing")
|
||||||
|
.setting(AppSettings::NoBinaryName)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("register")
|
||||||
|
.about("Register more bots with the auth server")
|
||||||
|
.args(&[
|
||||||
|
Arg::with_name("prefix").required(true),
|
||||||
|
Arg::with_name("password").required(true),
|
||||||
|
Arg::with_name("count"),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("login")
|
||||||
|
.about("Login all registered bots whose username starts with a prefix")
|
||||||
|
.args(&[Arg::with_name("prefix").required(true)]),
|
||||||
|
)
|
||||||
|
.subcommand(SubCommand::with_name("tick").about("Handle ticks for all logged in bots"))
|
||||||
|
.get_matches_from_safe(cmd.split(" "));
|
||||||
|
use clap::ErrorKind::*;
|
||||||
|
match matches {
|
||||||
|
Ok(matches) => {
|
||||||
|
if match matches.subcommand() {
|
||||||
|
("register", Some(matches)) => command_s.try_send(Cmd::Register {
|
||||||
|
prefix: matches.value_of("prefix").unwrap().to_string(),
|
||||||
|
password: matches.value_of("password").unwrap().to_string(),
|
||||||
|
count: matches
|
||||||
|
.value_of("count")
|
||||||
|
.and_then(|x| x.parse::<usize>().ok()),
|
||||||
|
}),
|
||||||
|
("login", Some(matches)) => command_s.try_send(Cmd::Login {
|
||||||
|
prefix: matches.value_of("prefix").unwrap().to_string(),
|
||||||
|
}),
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e)
|
||||||
|
if [HelpDisplayed, MissingRequiredArgument, UnknownArgument].contains(&e.kind) =>
|
||||||
|
{
|
||||||
|
println!("{}", e.message);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{:?}", e);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
@ -68,6 +68,7 @@ use vek::*;
|
|||||||
|
|
||||||
const PING_ROLLING_AVERAGE_SECS: usize = 10;
|
const PING_ROLLING_AVERAGE_SECS: usize = 10;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Chat(comp::ChatMsg),
|
Chat(comp::ChatMsg),
|
||||||
InviteComplete {
|
InviteComplete {
|
||||||
@ -177,7 +178,7 @@ pub struct Client {
|
|||||||
|
|
||||||
/// Holds data related to the current players characters, as well as some
|
/// Holds data related to the current players characters, as well as some
|
||||||
/// additional state to handle UI.
|
/// additional state to handle UI.
|
||||||
#[derive(Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct CharacterList {
|
pub struct CharacterList {
|
||||||
pub characters: Vec<CharacterItem>,
|
pub characters: Vec<CharacterItem>,
|
||||||
pub loading: bool,
|
pub loading: bool,
|
||||||
|
Reference in New Issue
Block a user