You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
263 lines
7.7 KiB
263 lines
7.7 KiB
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
|
|
|
use std::collections::HashMap;
|
|
use std::sync::mpsc::Receiver;
|
|
use std::{path::PathBuf, sync::Mutex};
|
|
|
|
use config::Config;
|
|
use crossbeam::channel::Sender;
|
|
use notify::PollWatcher;
|
|
use overlay::{Event, Overlay};
|
|
|
|
use plan::{Plan, PlanMetadata};
|
|
use poe_data::world_area::WorldAreasMap;
|
|
use poe_reader::filter_func;
|
|
use poe_reader::{blocking_area_filtered_rx, poe_client_log_receiver};
|
|
use simple_logger::SimpleLogger;
|
|
use storage::Storage;
|
|
use tauri::CustomMenuItem;
|
|
use tauri::Manager;
|
|
|
|
use tauri::SystemTray;
|
|
use tauri::SystemTrayEvent;
|
|
use tauri::SystemTrayMenu;
|
|
use tauri::SystemTrayMenuItem;
|
|
use tauri::Window;
|
|
|
|
use lazy_static::lazy_static;
|
|
use time::{RunHistory, RunHistoryMetadata};
|
|
|
|
mod config;
|
|
mod overlay;
|
|
mod plan;
|
|
mod poe_reader;
|
|
mod storage;
|
|
mod time;
|
|
|
|
lazy_static! {
|
|
static ref WORLD_AREAS_MAP: WorldAreasMap = poe_data::world_area::load_world_areas_map(
|
|
include_str!("../../data/processed_world_areas.json")
|
|
);
|
|
}
|
|
|
|
lazy_static! {
|
|
static ref FULL_WORLD_AREAS_MAP: WorldAreasMap = poe_data::world_area::load_world_areas_map(
|
|
include_str!("../../data/processed_world_areas_full.json")
|
|
);
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn set_interactable(interactable: bool, state: tauri::State<Sender<Event>>) {
|
|
if interactable {
|
|
state.send(overlay::State::Interactable {}.into()).ok();
|
|
} else {
|
|
state.send(overlay::Event::Return).ok();
|
|
}
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn load_world_areas() -> WorldAreasMap {
|
|
log::info!("Loading world areas");
|
|
WORLD_AREAS_MAP.clone()
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn load_full_world_areas() -> WorldAreasMap {
|
|
log::info!("Loading world areas");
|
|
FULL_WORLD_AREAS_MAP.clone()
|
|
}
|
|
|
|
|
|
#[tauri::command]
|
|
fn load_config(state: tauri::State<Mutex<Storage>>) -> Option<Config> {
|
|
Some(state.lock().ok()?.config.clone())
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn update_config(config: Config, state: tauri::State<Mutex<Storage>>) {
|
|
log::info!("Saved config: {:?}", config);
|
|
if let Ok(mut storage) = state.lock() {
|
|
storage.config = config.clone();
|
|
storage.save_config();
|
|
}
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn enumerate_stored_plans() -> Vec<PlanMetadata> {
|
|
Storage::enumerate_plans()
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn save_history(current_run_history: RunHistory) {
|
|
Storage::save_history(current_run_history);
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn load_history_at_uuid(uuid: String) -> Option<RunHistory> {
|
|
Storage::load_history_at_uuid(uuid)
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn load_cache() -> Option<HashMap<String, RunHistoryMetadata>> {
|
|
Storage::load_cache()
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn load_plan_at_path(
|
|
path: PathBuf,
|
|
save_local: Option<bool>,
|
|
state: tauri::State<Mutex<Storage>>,
|
|
) -> Option<Plan> {
|
|
if !path.exists() {
|
|
return None;
|
|
}
|
|
|
|
let mut storage = match state.lock() {
|
|
Ok(storage) => storage,
|
|
Err(_) => return None,
|
|
};
|
|
|
|
let save_local = save_local.unwrap_or(true);
|
|
|
|
storage.last_plan = Some(path.clone());
|
|
Storage::load_plan_at_path(path, save_local)
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn save_plan_at_path(path: PathBuf, plan: Plan) -> bool {
|
|
if let Some(path_string) = path.with_extension("json").to_str() {
|
|
return Storage::save_plan_at_path(path_string, plan).is_ok();
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn save_plan_at_store(name: String, plan: Plan, allow_overwrite: bool) -> Option<PathBuf> {
|
|
Storage::save_plan_at_store_path(&name, plan, allow_overwrite).ok()
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn base_plan() -> Plan {
|
|
const BASE_PLAN_STRING: &str = include_str!("../../data/base_plan.json");
|
|
serde_json::from_str(BASE_PLAN_STRING).expect("could not load base_plan")
|
|
}
|
|
|
|
fn main() {
|
|
SimpleLogger::new()
|
|
.with_module_level("underlayer", log::LevelFilter::Info)
|
|
.with_level(log::LevelFilter::Trace)
|
|
.init()
|
|
.expect("Could not init logger");
|
|
|
|
let settings = CustomMenuItem::new("settings".to_string(), "Settings");
|
|
let editor = CustomMenuItem::new("editor".to_string(), "Plan Editor");
|
|
let runstats = CustomMenuItem::new("runstats".to_string(), "Run stats");
|
|
let force_show = CustomMenuItem::new("force_show".to_string(), "Force show");
|
|
let exit = CustomMenuItem::new("exit".to_string(), "Exit");
|
|
let tray_menu = SystemTrayMenu::new()
|
|
.add_item(settings)
|
|
.add_item(editor)
|
|
.add_item(runstats)
|
|
.add_item(force_show)
|
|
.add_native_item(SystemTrayMenuItem::Separator)
|
|
.add_item(exit);
|
|
let system_tray = SystemTray::new().with_menu(tray_menu);
|
|
|
|
tauri::Builder::default()
|
|
.setup(|app| {
|
|
let tx = Overlay::initialize(
|
|
app.get_window("Overlay")
|
|
.expect("Could not get main overlay window"),
|
|
"Path of Exile 2",
|
|
);
|
|
app.manage(tx);
|
|
app.manage(Mutex::new(Storage::default()));
|
|
|
|
if let Ok(storage) = app.state::<Mutex<Storage>>().lock() {
|
|
listen_for_zone_changes(
|
|
storage.config.poe_client_log_path.clone(),
|
|
app.get_window("Overlay")
|
|
.expect("Could not get main overlay window"),
|
|
);
|
|
}
|
|
|
|
// app.get_window("Overlay")
|
|
// .expect("Could not get main overlay window")
|
|
// .open_devtools();
|
|
|
|
Ok(())
|
|
})
|
|
.invoke_handler(tauri::generate_handler![
|
|
set_interactable,
|
|
load_world_areas,
|
|
load_full_world_areas,
|
|
load_config,
|
|
update_config,
|
|
enumerate_stored_plans,
|
|
load_plan_at_path,
|
|
save_plan_at_path,
|
|
save_plan_at_store,
|
|
base_plan,
|
|
save_history,
|
|
load_history_at_uuid,
|
|
load_cache,
|
|
])
|
|
.system_tray(system_tray)
|
|
.on_system_tray_event(|app, event| match event {
|
|
SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
|
|
"exit" => {
|
|
std::process::exit(0);
|
|
}
|
|
"editor" | "settings" | "runstats" => {
|
|
if let Some(window) = app.get_window("Normal") {
|
|
window.show().ok();
|
|
window.emit_to("Normal", "loadTab", id).ok();
|
|
}
|
|
}
|
|
"force_show" => {
|
|
app.state::<Sender<Event>>()
|
|
.send(overlay::State::Interactable {}.into())
|
|
.ok();
|
|
}
|
|
_ => {}
|
|
},
|
|
_ => {}
|
|
})
|
|
.on_window_event(|event| match event.event() {
|
|
tauri::WindowEvent::CloseRequested { api, .. } => {
|
|
event.window().hide().ok();
|
|
api.prevent_close();
|
|
}
|
|
_ => {}
|
|
})
|
|
.run(tauri::generate_context!())
|
|
.expect("error while running tauri application");
|
|
}
|
|
|
|
fn listen_for_zone_changes(poe_client_log_path: Option<PathBuf>, window: Window) {
|
|
std::thread::spawn(move || {
|
|
// need _watcher
|
|
if let Some((enter_area_receiver, _watcher)) = receiver_from_path(&poe_client_log_path) {
|
|
for area in blocking_area_filtered_rx(&enter_area_receiver) {
|
|
if let Some(area) = filter_func(area) {
|
|
// if let Some(entered) = world_areas.get(&area) {
|
|
window.emit_to("Overlay", "entered", &area).ok();
|
|
// }
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
fn receiver_from_path(path: &Option<PathBuf>) -> Option<(Receiver<String>, PollWatcher)> {
|
|
if let Some(poe_path) = path {
|
|
if poe_path.exists() {
|
|
return Some(poe_client_log_receiver(poe_path.clone()));
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|