added file-watcher for memory dumps

master
hheik 2023-04-01 11:23:25 +03:00
parent 2b0ba06bf1
commit 0395cbc942
7 changed files with 130 additions and 66 deletions

12
Cargo.lock generated
View File

@ -2557,6 +2557,16 @@ dependencies = [
"windows-sys 0.42.0",
]
[[package]]
name = "notify-debouncer-mini"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e23e9fa24f094b143c1eb61f90ac6457de87be6987bc70746e0179f7dbc9007b"
dependencies = [
"crossbeam-channel",
"notify",
]
[[package]]
name = "ntapi"
version = "0.4.0"
@ -3293,6 +3303,8 @@ dependencies = [
"bevy_egui",
"json",
"lazy_static",
"notify",
"notify-debouncer-mini",
"reqwest",
]

View File

@ -10,6 +10,8 @@ bevy = "0.10.0"
bevy_egui = "0.20.1"
json = "0.12.4"
lazy_static = "1.4.0"
notify = "5.1.0"
notify-debouncer-mini = "0.2.1"
reqwest = { version = "0.11.14", features = ["blocking", "json"] }
# Enable a small amount of optimization in debug mode

View File

@ -1,6 +1,8 @@
use bevy::{prelude::*, window::WindowResolution};
mod battle;
mod inspector;
mod memory_reader;
mod ui;
pub fn run() {
@ -21,5 +23,6 @@ pub fn run() {
.add_plugin(crate::database::DatabasePlugin)
.add_plugin(inspector::InspectorPlugin)
.add_plugin(ui::UiPlugin)
.add_plugin(memory_reader::MemoryReaderPlugin)
.run()
}

6
src/ivpeek/battle.rs Normal file
View File

@ -0,0 +1,6 @@
use super::inspector::Battle;
pub fn parse_battle_party(data: &Vec<u8>) -> Result<Battle, String> {
let mut result = Battle::default();
Ok(result)
}

View File

@ -3,10 +3,7 @@ use std::collections::HashMap;
use bevy::prelude::*;
use bevy_egui::EguiContexts;
use crate::{
database::{load_url_asset, Database},
pokemon::*,
};
use crate::{database::load_url_asset, pokemon::*};
use super::ui::{EguiAsset, UiAssets};
@ -15,25 +12,22 @@ pub struct InspectorPlugin;
impl Plugin for InspectorPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(Inspector::default())
.add_system(state_changed)
.add_startup_system(test_init);
.add_system(state_changed);
}
}
#[derive(Resource, Default)]
pub struct Inspector {
pub allies: Vec<PokemonInstance>,
pub enemies: Vec<PokemonInstance>,
pub battle: Battle,
}
impl Inspector {
pub fn clear(&mut self) {
self.allies.clear();
self.enemies.clear();
}
#[derive(Default, Clone)]
pub struct Battle {
pub player_party: Vec<PokemonInstance>,
pub enemy_party: Vec<PokemonInstance>,
}
#[derive(Default)]
#[derive(Default, Clone)]
pub struct PokemonInstance {
pub pokemon: Pokemon,
pub nature: Option<Nature>,
@ -41,55 +35,6 @@ pub struct PokemonInstance {
pub evs: HashMap<BaseStat, u8>,
}
fn test_init(mut inspector: ResMut<Inspector>, pokemon_database: Res<Database<Pokemon>>) {
for name in vec!["baltoy"].iter() {
inspector.enemies.push(PokemonInstance {
pokemon: pokemon_database.map.get(*name).unwrap().clone(),
ivs: {
let mut result = HashMap::new();
for stat in BaseStat::all().iter() {
result.insert(
*stat,
match stat {
BaseStat::Hp => 31,
BaseStat::Attack => 27,
BaseStat::Defense => 26,
BaseStat::SpecialAttack => 0,
BaseStat::SpecialDefense => 21,
BaseStat::Speed => 15,
},
);
}
result
},
..default()
});
}
for name in vec!["seadra", "sceptile", "ampharos", "shedinja"].iter() {
inspector.allies.push(PokemonInstance {
pokemon: pokemon_database.map.get(*name).unwrap().clone(),
ivs: {
let mut result = HashMap::new();
for stat in BaseStat::all().iter() {
result.insert(
*stat,
match stat {
BaseStat::Hp => 31,
BaseStat::Attack => 27,
BaseStat::Defense => 26,
BaseStat::SpecialAttack => 0,
BaseStat::SpecialDefense => 21,
BaseStat::Speed => 15,
},
);
}
result
},
..default()
});
}
}
fn state_changed(
inspector: Res<Inspector>,
assets: Res<AssetServer>,
@ -97,7 +42,12 @@ fn state_changed(
mut contexts: EguiContexts,
) {
if inspector.is_changed() {
for instance in inspector.enemies.iter().chain(inspector.allies.iter()) {
for instance in inspector
.battle
.enemy_party
.iter()
.chain(inspector.battle.player_party.iter())
{
let pokemon = instance.pokemon.clone();
if !ui_assets.sprite_map.contains_key(&pokemon.key()) {

View File

@ -0,0 +1,91 @@
use bevy::prelude::*;
use notify_debouncer_mini::new_debouncer;
use std::{
fs,
sync::{Arc, Mutex},
thread,
time::Duration,
};
use super::{
battle::parse_battle_party,
inspector::{Battle, Inspector},
};
pub struct MemoryReaderPlugin;
impl Plugin for MemoryReaderPlugin {
fn build(&self, app: &mut App) {
app.add_event::<ParsedBattleEvent>()
.insert_resource(EventQueue::default())
.add_startup_system(init_memory_reader)
.add_system(flush_updates)
.add_system(update_inspector);
}
}
#[derive(Default)]
pub struct ParsedBattleEvent(Battle);
#[derive(Resource, Default)]
struct EventQueue {
updates: Arc<Mutex<Vec<ParsedBattleEvent>>>,
}
fn flush_updates(queue: Res<EventQueue>, mut events: EventWriter<ParsedBattleEvent>) {
match queue.updates.lock() {
Ok(mut updates) => {
for update in updates.drain(..) {
events.send(update)
}
}
Err(err) => {
error!("{err}");
}
}
}
fn update_inspector(mut inspector: ResMut<Inspector>, mut events: EventReader<ParsedBattleEvent>) {
for event in events.iter() {
inspector.battle = event.0.clone();
}
}
fn init_memory_reader(queue: Res<EventQueue>) {
let updates = queue.updates.clone();
thread::spawn(move || {
let watch_path = std::path::Path::new("/tmp/ivpeek/");
fs::create_dir_all(watch_path).expect("Failed to create path for {watch_path:?}");
let (tx, rx) = std::sync::mpsc::channel();
let mut debouncer = new_debouncer(Duration::from_millis(1_000), None, tx)
.expect("Could not create notify debouncer");
debouncer
.watcher()
.watch(watch_path, notify::RecursiveMode::NonRecursive)
.expect("Could not watch for {watch_path:?}");
for result in rx {
match result {
Ok(events) => {
for event in events.iter() {
match fs::read(&event.path) {
Ok(data) => match parse_battle_party(&data) {
Ok(battle) => match updates.lock() {
Ok(mut updates) => updates.push(ParsedBattleEvent(battle)),
Err(err) => error!("{err}"),
},
Err(err) => error!("Failed to parse party data: ${err}"),
},
_ => (),
}
}
}
Err(err) => error!("Watcher error: {err:?}"),
}
}
});
}

View File

@ -74,7 +74,7 @@ fn ui_system(mut contexts: EguiContexts, inspector: Res<Inspector>, ui_assets: R
egui::CentralPanel::default().show(contexts.ctx_mut(), |ui| {
egui::ScrollArea::vertical().show(ui, |ui| {
ui.heading("Enemy team");
for instance in inspector.enemies.iter() {
for instance in inspector.battle.enemy_party.iter() {
ui.add_space(8.);
render_pokemon_instance(ui, &ui_assets, instance);
ui.add_space(8.);
@ -83,7 +83,7 @@ fn ui_system(mut contexts: EguiContexts, inspector: Res<Inspector>, ui_assets: R
ui.separator();
ui.heading("Player team");
for instance in inspector.allies.iter() {
for instance in inspector.battle.player_party.iter() {
ui.add_space(8.);
render_pokemon_instance(ui, &ui_assets, instance);
ui.add_space(8.);