added file-watcher for memory dumps
parent
2b0ba06bf1
commit
0395cbc942
|
|
@ -2557,6 +2557,16 @@ dependencies = [
|
||||||
"windows-sys 0.42.0",
|
"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]]
|
[[package]]
|
||||||
name = "ntapi"
|
name = "ntapi"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
|
@ -3293,6 +3303,8 @@ dependencies = [
|
||||||
"bevy_egui",
|
"bevy_egui",
|
||||||
"json",
|
"json",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"notify",
|
||||||
|
"notify-debouncer-mini",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ bevy = "0.10.0"
|
||||||
bevy_egui = "0.20.1"
|
bevy_egui = "0.20.1"
|
||||||
json = "0.12.4"
|
json = "0.12.4"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
notify = "5.1.0"
|
||||||
|
notify-debouncer-mini = "0.2.1"
|
||||||
reqwest = { version = "0.11.14", features = ["blocking", "json"] }
|
reqwest = { version = "0.11.14", features = ["blocking", "json"] }
|
||||||
|
|
||||||
# Enable a small amount of optimization in debug mode
|
# Enable a small amount of optimization in debug mode
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
use bevy::{prelude::*, window::WindowResolution};
|
use bevy::{prelude::*, window::WindowResolution};
|
||||||
|
|
||||||
|
mod battle;
|
||||||
mod inspector;
|
mod inspector;
|
||||||
|
mod memory_reader;
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
|
|
@ -21,5 +23,6 @@ pub fn run() {
|
||||||
.add_plugin(crate::database::DatabasePlugin)
|
.add_plugin(crate::database::DatabasePlugin)
|
||||||
.add_plugin(inspector::InspectorPlugin)
|
.add_plugin(inspector::InspectorPlugin)
|
||||||
.add_plugin(ui::UiPlugin)
|
.add_plugin(ui::UiPlugin)
|
||||||
|
.add_plugin(memory_reader::MemoryReaderPlugin)
|
||||||
.run()
|
.run()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
@ -3,10 +3,7 @@ use std::collections::HashMap;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_egui::EguiContexts;
|
use bevy_egui::EguiContexts;
|
||||||
|
|
||||||
use crate::{
|
use crate::{database::load_url_asset, pokemon::*};
|
||||||
database::{load_url_asset, Database},
|
|
||||||
pokemon::*,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::ui::{EguiAsset, UiAssets};
|
use super::ui::{EguiAsset, UiAssets};
|
||||||
|
|
||||||
|
|
@ -15,25 +12,22 @@ pub struct InspectorPlugin;
|
||||||
impl Plugin for InspectorPlugin {
|
impl Plugin for InspectorPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.insert_resource(Inspector::default())
|
app.insert_resource(Inspector::default())
|
||||||
.add_system(state_changed)
|
.add_system(state_changed);
|
||||||
.add_startup_system(test_init);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource, Default)]
|
#[derive(Resource, Default)]
|
||||||
pub struct Inspector {
|
pub struct Inspector {
|
||||||
pub allies: Vec<PokemonInstance>,
|
pub battle: Battle,
|
||||||
pub enemies: Vec<PokemonInstance>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Inspector {
|
#[derive(Default, Clone)]
|
||||||
pub fn clear(&mut self) {
|
pub struct Battle {
|
||||||
self.allies.clear();
|
pub player_party: Vec<PokemonInstance>,
|
||||||
self.enemies.clear();
|
pub enemy_party: Vec<PokemonInstance>,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, Clone)]
|
||||||
pub struct PokemonInstance {
|
pub struct PokemonInstance {
|
||||||
pub pokemon: Pokemon,
|
pub pokemon: Pokemon,
|
||||||
pub nature: Option<Nature>,
|
pub nature: Option<Nature>,
|
||||||
|
|
@ -41,55 +35,6 @@ pub struct PokemonInstance {
|
||||||
pub evs: HashMap<BaseStat, u8>,
|
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(
|
fn state_changed(
|
||||||
inspector: Res<Inspector>,
|
inspector: Res<Inspector>,
|
||||||
assets: Res<AssetServer>,
|
assets: Res<AssetServer>,
|
||||||
|
|
@ -97,7 +42,12 @@ fn state_changed(
|
||||||
mut contexts: EguiContexts,
|
mut contexts: EguiContexts,
|
||||||
) {
|
) {
|
||||||
if inspector.is_changed() {
|
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();
|
let pokemon = instance.pokemon.clone();
|
||||||
|
|
||||||
if !ui_assets.sprite_map.contains_key(&pokemon.key()) {
|
if !ui_assets.sprite_map.contains_key(&pokemon.key()) {
|
||||||
|
|
|
||||||
|
|
@ -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:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -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::CentralPanel::default().show(contexts.ctx_mut(), |ui| {
|
||||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||||
ui.heading("Enemy team");
|
ui.heading("Enemy team");
|
||||||
for instance in inspector.enemies.iter() {
|
for instance in inspector.battle.enemy_party.iter() {
|
||||||
ui.add_space(8.);
|
ui.add_space(8.);
|
||||||
render_pokemon_instance(ui, &ui_assets, instance);
|
render_pokemon_instance(ui, &ui_assets, instance);
|
||||||
ui.add_space(8.);
|
ui.add_space(8.);
|
||||||
|
|
@ -83,7 +83,7 @@ fn ui_system(mut contexts: EguiContexts, inspector: Res<Inspector>, ui_assets: R
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
ui.heading("Player team");
|
ui.heading("Player team");
|
||||||
for instance in inspector.allies.iter() {
|
for instance in inspector.battle.player_party.iter() {
|
||||||
ui.add_space(8.);
|
ui.add_space(8.);
|
||||||
render_pokemon_instance(ui, &ui_assets, instance);
|
render_pokemon_instance(ui, &ui_assets, instance);
|
||||||
ui.add_space(8.);
|
ui.add_space(8.);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue