2018-06-09 12:35:47 +02:00
|
|
|
use indexmap::IndexMap;
|
2021-03-15 15:34:09 +01:00
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
use log::debug;
|
|
|
|
use log::{error, info, LevelFilter};
|
2021-03-15 22:09:30 +01:00
|
|
|
use rayon::prelude::*;
|
2019-09-30 17:56:03 +02:00
|
|
|
use serde_derive::Deserialize;
|
2020-11-20 08:15:50 +01:00
|
|
|
use simple_logger::SimpleLogger;
|
2021-03-15 14:59:14 +01:00
|
|
|
use std::env;
|
2021-03-14 14:47:26 +01:00
|
|
|
#[cfg(target_os = "windows")]
|
2021-03-14 13:06:29 +01:00
|
|
|
use std::io;
|
2021-03-10 12:22:00 +01:00
|
|
|
use std::io::Error;
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
use std::io::ErrorKind;
|
2023-01-03 11:13:33 +01:00
|
|
|
use std::ops::Deref;
|
2021-05-10 10:40:27 +02:00
|
|
|
use std::path::{Path, PathBuf};
|
2021-03-15 21:15:03 +01:00
|
|
|
use std::time::Instant;
|
2018-06-10 15:02:08 +02:00
|
|
|
use std::{fs, thread, time};
|
2018-06-09 00:06:30 +02:00
|
|
|
use time::Duration;
|
2020-06-28 18:34:31 +02:00
|
|
|
#[cfg(target_os = "windows")]
|
2018-06-09 08:31:15 +02:00
|
|
|
use winreg::RegKey;
|
2019-09-26 22:39:25 +02:00
|
|
|
|
|
|
|
mod cgg_data_source;
|
|
|
|
mod data_source;
|
2019-09-30 17:56:03 +02:00
|
|
|
mod kb_data_source;
|
2021-04-08 13:55:10 +02:00
|
|
|
mod ms_data_source;
|
2021-05-13 14:38:31 +02:00
|
|
|
mod pb_data_source;
|
2019-09-26 22:39:25 +02:00
|
|
|
|
2021-03-13 23:02:26 +01:00
|
|
|
use cgg_data_source::CGGDataSource;
|
2019-09-26 22:39:25 +02:00
|
|
|
use data_source::DataSource;
|
2019-09-30 17:56:03 +02:00
|
|
|
use kb_data_source::KBDataSource;
|
2021-04-08 13:55:10 +02:00
|
|
|
use ms_data_source::MSDataSource;
|
2021-05-13 14:38:31 +02:00
|
|
|
use pb_data_source::PBDataSource;
|
2018-06-04 22:51:18 +02:00
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
struct Realm {
|
2018-06-09 09:07:41 +02:00
|
|
|
v: String,
|
2018-06-04 22:51:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
2021-04-08 13:55:10 +02:00
|
|
|
pub struct Champion {
|
2018-06-09 12:35:47 +02:00
|
|
|
data: IndexMap<String, ChampInfo>,
|
2018-06-04 22:51:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
2021-03-13 23:02:26 +01:00
|
|
|
pub struct ChampInfo {
|
|
|
|
id: String,
|
2018-06-09 11:46:02 +02:00
|
|
|
name: String,
|
2021-03-14 11:21:57 +01:00
|
|
|
key: String,
|
2018-06-07 22:39:25 +02:00
|
|
|
}
|
|
|
|
|
2021-03-10 12:22:00 +01:00
|
|
|
const USER_AGENT_VALUE: &str =
|
2021-11-01 08:36:06 +01:00
|
|
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0";
|
2021-03-15 14:59:14 +01:00
|
|
|
const DEFAULT_LOL_CHAMPS_DIR: &str = ".\\champs";
|
2021-03-15 15:34:09 +01:00
|
|
|
#[cfg(target_os = "windows")]
|
2021-03-15 14:59:14 +01:00
|
|
|
const REG_KEY_LOL_RADS: &str = r"SOFTWARE\WOW6432Node\Riot Games\RADS";
|
2021-03-15 15:34:09 +01:00
|
|
|
#[cfg(target_os = "windows")]
|
2021-03-15 14:59:14 +01:00
|
|
|
const REG_KEY_LOL_INC: &str = r"SOFTWARE\WOW6432Node\Riot Games, Inc\League of Legends";
|
2021-03-15 15:34:09 +01:00
|
|
|
#[cfg(target_os = "windows")]
|
2021-03-15 14:59:14 +01:00
|
|
|
const REG_KEY_WIN_64_UNINSTALL: &str =
|
|
|
|
r"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
|
2021-03-15 15:34:09 +01:00
|
|
|
#[cfg(target_os = "windows")]
|
2021-03-15 14:59:14 +01:00
|
|
|
const REG_KEY_WIN_UNINSTALL: &str = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\";
|
2018-06-08 22:41:01 +02:00
|
|
|
|
2021-03-15 07:46:48 +01:00
|
|
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
2021-03-15 14:59:14 +01:00
|
|
|
let args: Vec<String> = env::args().collect();
|
|
|
|
let mut level = LevelFilter::Info;
|
|
|
|
for s in &args {
|
|
|
|
if s.eq_ignore_ascii_case("-v") || s.eq_ignore_ascii_case("--verbose") {
|
|
|
|
level = LevelFilter::Debug;
|
2021-03-15 21:15:03 +01:00
|
|
|
break;
|
2021-03-15 14:59:14 +01:00
|
|
|
}
|
|
|
|
}
|
2021-03-07 18:02:11 +01:00
|
|
|
SimpleLogger::new()
|
2021-03-15 14:59:14 +01:00
|
|
|
.with_level(level)
|
2021-03-10 12:22:00 +01:00
|
|
|
.with_module_level("ureq", LevelFilter::Error)
|
2021-03-15 21:15:03 +01:00
|
|
|
.with_module_level("rustls", LevelFilter::Error)
|
2021-03-15 07:46:48 +01:00
|
|
|
.init()?;
|
2018-06-04 22:51:18 +02:00
|
|
|
info!("CGG Item Sets");
|
|
|
|
|
2021-03-16 09:12:30 +01:00
|
|
|
let lol_champs_dir: PathBuf = match lol_champ_dir() {
|
|
|
|
Ok(x) => x,
|
|
|
|
Err(_e) => PathBuf::from(DEFAULT_LOL_CHAMPS_DIR),
|
|
|
|
};
|
|
|
|
info!("LoL Champs Folder: {}", lol_champs_dir.to_str().unwrap());
|
|
|
|
|
|
|
|
let client: ureq::Agent = ureq::AgentBuilder::new()
|
|
|
|
.user_agent(USER_AGENT_VALUE)
|
|
|
|
.timeout(Duration::from_secs(10))
|
|
|
|
.build();
|
|
|
|
|
|
|
|
let realm: Realm = client
|
|
|
|
.get("https://ddragon.leagueoflegends.com/realms/euw.json")
|
|
|
|
.call()?
|
|
|
|
.into_json()?;
|
|
|
|
info!("LoL version: {}", realm.v);
|
|
|
|
let champion: Champion = client
|
|
|
|
.get(&format!(
|
|
|
|
"https://ddragon.leagueoflegends.com/cdn/{}/data/en_US/champion.json",
|
|
|
|
realm.v
|
|
|
|
))
|
|
|
|
.call()?
|
|
|
|
.into_json()?;
|
|
|
|
info!("LoL numbers of champs: {}", champion.data.len());
|
|
|
|
|
2023-01-03 11:13:33 +01:00
|
|
|
let data_sources: Vec<Box<dyn DataSource + Sync + Send>> = vec![
|
|
|
|
Box::new(PBDataSource),
|
|
|
|
Box::new(CGGDataSource::new()),
|
|
|
|
Box::new(KBDataSource::new()),
|
|
|
|
Box::new(MSDataSource),
|
2021-05-13 14:38:31 +02:00
|
|
|
];
|
2021-03-17 09:07:01 +01:00
|
|
|
data_sources.par_iter().for_each(|data_source| {
|
2021-03-16 09:12:30 +01:00
|
|
|
let init = Instant::now();
|
2023-01-03 11:13:33 +01:00
|
|
|
execute_data_source(data_source.deref(), &client, &champion, &lol_champs_dir);
|
2021-03-16 09:12:30 +01:00
|
|
|
info!(
|
2021-11-01 08:39:19 +01:00
|
|
|
"{}: done in {}s",
|
2021-03-16 09:12:30 +01:00
|
|
|
data_source.get_alias(),
|
2021-11-01 08:39:19 +01:00
|
|
|
init.elapsed().as_secs_f32()
|
2021-03-16 09:12:30 +01:00
|
|
|
);
|
|
|
|
});
|
2021-03-15 07:46:48 +01:00
|
|
|
Ok(())
|
2018-06-05 00:02:35 +02:00
|
|
|
}
|
|
|
|
|
2021-03-16 10:53:16 +01:00
|
|
|
fn get_champ_from_key(champs: &Champion, key: u32) -> Option<String> {
|
2021-03-13 23:02:26 +01:00
|
|
|
for champ in champs.data.values() {
|
2021-03-16 10:53:16 +01:00
|
|
|
if key.to_string() == champ.key {
|
2021-03-15 07:56:07 +01:00
|
|
|
return Some(champ.id.to_owned());
|
2021-03-13 23:02:26 +01:00
|
|
|
}
|
|
|
|
}
|
2021-03-15 07:56:07 +01:00
|
|
|
None
|
2021-03-13 23:02:26 +01:00
|
|
|
}
|
|
|
|
|
2021-03-15 18:59:49 +01:00
|
|
|
fn execute_data_source(
|
2021-03-16 09:12:30 +01:00
|
|
|
data_source: &(dyn DataSource + Sync + Send),
|
2021-03-15 18:59:49 +01:00
|
|
|
client: &ureq::Agent,
|
|
|
|
champion: &Champion,
|
2021-05-10 10:40:27 +02:00
|
|
|
lol_champs_dir: &Path,
|
2021-03-15 18:59:49 +01:00
|
|
|
) {
|
2021-08-01 10:01:04 +02:00
|
|
|
let champs = data_source.get_champs_with_positions(client, champion);
|
2021-03-15 18:59:49 +01:00
|
|
|
|
|
|
|
info!(
|
|
|
|
"{} numbers of champs: {}",
|
|
|
|
data_source.get_alias(),
|
|
|
|
champs.len()
|
|
|
|
);
|
|
|
|
|
2021-03-15 22:09:30 +01:00
|
|
|
if data_source.get_timeout() == 0 {
|
|
|
|
champs.par_iter().for_each(|(id, positions)| {
|
|
|
|
get_and_write_item_set(
|
|
|
|
data_source,
|
|
|
|
client,
|
|
|
|
champion,
|
|
|
|
lol_champs_dir,
|
2021-03-16 10:53:16 +01:00
|
|
|
*id,
|
2021-03-15 22:09:30 +01:00
|
|
|
positions,
|
2021-03-15 23:00:20 +01:00
|
|
|
);
|
2021-03-15 22:09:30 +01:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
champs.iter().for_each(|(id, positions)| {
|
|
|
|
get_and_write_item_set(
|
|
|
|
data_source,
|
|
|
|
client,
|
|
|
|
champion,
|
|
|
|
lol_champs_dir,
|
2021-03-16 10:53:16 +01:00
|
|
|
*id,
|
2021-03-15 22:09:30 +01:00
|
|
|
positions,
|
2021-03-15 23:00:20 +01:00
|
|
|
);
|
|
|
|
thread::sleep(Duration::from_millis(data_source.get_timeout()));
|
2021-03-15 22:09:30 +01:00
|
|
|
});
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_and_write_item_set(
|
2021-03-16 09:12:30 +01:00
|
|
|
data_source: &(dyn DataSource + Sync + Send),
|
2021-03-15 22:09:30 +01:00
|
|
|
client: &ureq::Agent,
|
|
|
|
champion: &Champion,
|
2021-05-10 10:40:27 +02:00
|
|
|
lol_champs_dir: &Path,
|
2021-03-16 10:53:16 +01:00
|
|
|
id: u32,
|
2021-03-15 23:11:40 +01:00
|
|
|
positions: &[String],
|
2021-03-15 22:09:30 +01:00
|
|
|
) {
|
2021-08-01 10:01:04 +02:00
|
|
|
if let Some(champ_id) = get_champ_from_key(champion, id) {
|
2021-03-16 10:53:16 +01:00
|
|
|
if let Some(champ) = champion.data.get(&champ_id) {
|
|
|
|
if positions.is_empty() {
|
|
|
|
error!("{}: {} empty positions", data_source.get_alias(), &champ_id);
|
|
|
|
} else {
|
|
|
|
let path = lol_champs_dir.join(&champ_id).join("Recommended");
|
|
|
|
fs::create_dir_all(&path).unwrap();
|
2021-08-01 10:01:04 +02:00
|
|
|
data_source.write_item_set(champ, positions, &path, client);
|
2021-03-16 10:53:16 +01:00
|
|
|
}
|
2021-03-15 18:59:49 +01:00
|
|
|
} else {
|
2021-03-16 10:53:16 +01:00
|
|
|
error!("{} not found in LoL champs", &champ_id);
|
2021-03-15 18:59:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-28 18:34:31 +02:00
|
|
|
#[cfg(target_os = "windows")]
|
2018-06-10 15:02:08 +02:00
|
|
|
fn lol_champ_dir() -> Result<PathBuf, Error> {
|
2021-03-14 13:06:29 +01:00
|
|
|
let hklm = RegKey::predef(winreg::enums::HKEY_LOCAL_MACHINE);
|
2021-03-15 14:59:14 +01:00
|
|
|
let path = if let Ok(node) = hklm.open_subkey(REG_KEY_LOL_RADS) {
|
|
|
|
debug!(
|
|
|
|
"Use registry key {} for relative champ directory",
|
|
|
|
REG_KEY_LOL_RADS
|
|
|
|
);
|
|
|
|
let val: String = node.get_value("LocalRootFolder")?;
|
|
|
|
// TODO: remplacer ce .unwrap()
|
|
|
|
PathBuf::from(val).parent().unwrap().to_path_buf()
|
|
|
|
} else if let Ok(node) = hklm.open_subkey(REG_KEY_LOL_INC) {
|
|
|
|
debug!(
|
|
|
|
"Use registry key {} for relative champ directory",
|
|
|
|
REG_KEY_LOL_INC
|
|
|
|
);
|
|
|
|
let val: String = node.get_value("Location")?;
|
|
|
|
PathBuf::from(val)
|
|
|
|
} else if let Ok(node) =
|
|
|
|
find_subnode_from_path(hklm, REG_KEY_WIN_64_UNINSTALL, "League of Legends")
|
|
|
|
{
|
|
|
|
debug!(
|
|
|
|
"Use registry key {} for relative champ directory",
|
|
|
|
REG_KEY_WIN_64_UNINSTALL
|
|
|
|
);
|
|
|
|
let val: String = node.get_value("InstallLocation")?;
|
|
|
|
PathBuf::from(val)
|
|
|
|
} else if let Ok(node) = find_subnode_from_path(
|
|
|
|
RegKey::predef(winreg::enums::HKEY_CURRENT_USER),
|
|
|
|
REG_KEY_WIN_UNINSTALL,
|
|
|
|
"Riot Game league_of_legends.live",
|
|
|
|
) {
|
|
|
|
debug!(
|
|
|
|
"Use registry key {} for relative champ directory",
|
|
|
|
REG_KEY_WIN_UNINSTALL
|
|
|
|
);
|
|
|
|
let val: String = node.get_value("InstallLocation")?;
|
|
|
|
PathBuf::from(val)
|
2021-03-14 13:06:29 +01:00
|
|
|
} else {
|
2021-03-15 14:59:14 +01:00
|
|
|
return Err(Error::from(ErrorKind::NotFound));
|
|
|
|
};
|
2021-03-15 18:59:49 +01:00
|
|
|
Ok(path.join("Config").join("Champions"))
|
2021-03-15 14:59:14 +01:00
|
|
|
}
|
2018-06-10 15:02:08 +02:00
|
|
|
|
2021-03-15 14:59:14 +01:00
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
|
|
fn lol_champ_dir() -> Result<PathBuf, Error> {
|
|
|
|
Ok(PathBuf::from(DEFAULT_LOL_CHAMPS_DIR))
|
|
|
|
}
|
2021-03-14 13:06:29 +01:00
|
|
|
|
2021-03-15 15:34:09 +01:00
|
|
|
#[cfg(target_os = "windows")]
|
2021-03-15 14:59:14 +01:00
|
|
|
fn find_subnode_from_path(reg: RegKey, path: &str, key: &str) -> io::Result<RegKey> {
|
|
|
|
if let Ok(node) = reg.open_subkey(path) {
|
|
|
|
if let Some(k) = node
|
2021-03-14 13:06:29 +01:00
|
|
|
.enum_keys()
|
|
|
|
.map(|x| x.unwrap())
|
2021-03-15 14:59:14 +01:00
|
|
|
.find(|x| x.starts_with(key))
|
|
|
|
{
|
|
|
|
return node.open_subkey(k);
|
2021-03-14 13:06:29 +01:00
|
|
|
}
|
2021-03-14 13:10:04 +01:00
|
|
|
}
|
2021-03-14 13:06:29 +01:00
|
|
|
Err(Error::from(ErrorKind::NotFound))
|
2018-06-09 08:31:15 +02:00
|
|
|
}
|