Skip to content

Commit 4617ce1

Browse files
authored
perf: add indexing on custom asset id (#16)
1 parent a58d957 commit 4617ce1

9 files changed

Lines changed: 148 additions & 60 deletions

File tree

src/assets/indexing.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use std::{collections::HashMap, marker::PhantomData};
2+
3+
use bevy::prelude::*;
4+
5+
pub trait Indexable {
6+
fn index(&self) -> &String;
7+
}
8+
9+
pub struct AssetIndexPlugin<T>(PhantomData<T>);
10+
11+
impl<T> Default for AssetIndexPlugin<T> {
12+
fn default() -> Self {
13+
Self(PhantomData)
14+
}
15+
}
16+
17+
impl<T> Plugin for AssetIndexPlugin<T>
18+
where
19+
T: Asset + Indexable,
20+
{
21+
fn build(&self, app: &mut App) {
22+
app.register_type::<IndexMap<T>>();
23+
app.init_resource::<IndexMap<T>>();
24+
25+
app.add_systems(Update, populate_index::<T>);
26+
}
27+
}
28+
29+
#[derive(Resource, Reflect, Deref, DerefMut)]
30+
#[reflect(Resource)]
31+
pub struct IndexMap<T: Asset>(HashMap<String, AssetId<T>>);
32+
33+
impl<T> Default for IndexMap<T>
34+
where
35+
T: Asset,
36+
{
37+
fn default() -> Self {
38+
Self(HashMap::default())
39+
}
40+
}
41+
42+
fn populate_index<T: Asset + Indexable>(
43+
mut events: EventReader<AssetEvent<T>>,
44+
assets: Res<Assets<T>>,
45+
mut index_map: ResMut<IndexMap<T>>,
46+
) {
47+
for event in events.read() {
48+
if let AssetEvent::Added { id } = event {
49+
let Some(asset) = assets.get(*id) else {
50+
continue;
51+
};
52+
53+
index_map.insert(asset.index().to_owned(), *id);
54+
}
55+
}
56+
}

src/assets/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use bevy::prelude::*;
22

3+
pub mod indexing;
34
pub mod loaders;
45
pub mod manifest;
56
mod tracking;

src/simulation/item/assets.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,18 @@ use bevy::{asset::LoadedFolder, prelude::*};
22
use bevy_aseprite_ultra::prelude::*;
33
use serde::Deserialize;
44

5-
use crate::assets::{LoadResource, loaders::toml::TomlAssetPlugin};
5+
use crate::assets::{
6+
LoadResource,
7+
indexing::{AssetIndexPlugin, Indexable},
8+
loaders::toml::TomlAssetPlugin,
9+
};
610

711
pub fn plugin(app: &mut App) {
812
app.register_type::<ItemDef>();
9-
app.add_plugins(TomlAssetPlugin::<ItemDef>::extensions(&["item.toml"]));
13+
app.add_plugins((
14+
TomlAssetPlugin::<ItemDef>::extensions(&["item.toml"]),
15+
AssetIndexPlugin::<ItemDef>::default(),
16+
));
1017

1118
app.register_type::<ItemAssets>();
1219
app.load_resource::<ItemAssets>();
@@ -19,6 +26,12 @@ pub struct ItemDef {
1926
pub stack_size: u32,
2027
}
2128

29+
impl Indexable for ItemDef {
30+
fn index(&self) -> &String {
31+
&self.id
32+
}
33+
}
34+
2235
#[derive(Asset, Clone, Resource, Reflect)]
2336
#[reflect(Resource)]
2437
pub struct ItemAssets {

src/simulation/logistics/conveyor_belt.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use bevy::{
77
use bevy_aseprite_ultra::prelude::*;
88

99
use crate::{
10+
assets::indexing::IndexMap,
1011
simulation::{
1112
FactorySystems,
1213
dismantle::QueueDismantle,
@@ -255,6 +256,7 @@ fn receive_items_from_belt(
255256
mut inputs: Query<&mut InputInventory>,
256257
mut commands: Commands,
257258
items: Res<Assets<ItemDef>>,
259+
item_index: Res<IndexMap<ItemDef>>,
258260
) {
259261
for (entity, item, progress, item_of) in conveyored_items {
260262
if progress.0 < 1.0 {
@@ -276,10 +278,9 @@ fn receive_items_from_belt(
276278
continue;
277279
};
278280

279-
let Some(item_def) = items
280-
.iter()
281-
.map(|(_, item)| item)
282-
.find(|item_def| item_def.id == item.0)
281+
let Some(item_def) = item_index
282+
.get(&item.0)
283+
.and_then(|asset_id| items.get(*asset_id))
283284
else {
284285
continue;
285286
};

src/simulation/recipe/assets.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,18 @@ use std::{collections::HashMap, time::Duration};
33
use bevy::{asset::LoadedFolder, prelude::*};
44
use serde::Deserialize;
55

6-
use crate::assets::{LoadResource, loaders::toml::TomlAssetPlugin};
6+
use crate::assets::{
7+
LoadResource,
8+
indexing::{AssetIndexPlugin, Indexable},
9+
loaders::toml::TomlAssetPlugin,
10+
};
711

812
pub fn plugin(app: &mut App) {
913
app.register_type::<RecipeDef>();
10-
app.add_plugins(TomlAssetPlugin::<RecipeDef>::extensions(&["recipe.toml"]));
14+
app.add_plugins((
15+
TomlAssetPlugin::<RecipeDef>::extensions(&["recipe.toml"]),
16+
AssetIndexPlugin::<RecipeDef>::default(),
17+
));
1118

1219
app.register_type::<RecipeAssets>();
1320
app.load_resource::<RecipeAssets>();
@@ -27,6 +34,12 @@ pub struct RecipeDef {
2734
pub tags: Vec<RecipeTags>,
2835
}
2936

37+
impl Indexable for RecipeDef {
38+
fn index(&self) -> &String {
39+
&self.id
40+
}
41+
}
42+
3043
#[derive(Debug, Reflect, Deserialize, PartialEq, Eq)]
3144
#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
3245
pub enum RecipeTags {

src/simulation/recipe/process.rs

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
use bevy::prelude::*;
22

3-
use crate::simulation::{
4-
FactorySystems,
5-
item::{ItemDef, Stack},
6-
logistics::{InputInventory, OutputInventory},
7-
machine::power::Powered,
8-
recipe::RecipeDef,
3+
use crate::{
4+
assets::indexing::IndexMap,
5+
simulation::{
6+
FactorySystems,
7+
item::{ItemDef, Stack},
8+
logistics::{InputInventory, OutputInventory},
9+
machine::power::Powered,
10+
recipe::RecipeDef,
11+
},
912
};
1013

1114
use super::{SelectedRecipe, progress::on_progress_state_add};
@@ -44,6 +47,7 @@ impl ProcessState {
4447
fn consume_input(
4548
query: Query<(&mut ProcessState, &mut InputInventory, &SelectedRecipe), With<Powered>>,
4649
recipes: Res<Assets<RecipeDef>>,
50+
recipe_index: Res<IndexMap<RecipeDef>>,
4751
) {
4852
for (mut state, mut inventory, selected_recipe) in query {
4953
if !matches!(*state, ProcessState::InsufficientInput) {
@@ -54,10 +58,9 @@ fn consume_input(
5458
continue;
5559
};
5660

57-
let recipe = recipes
58-
.iter()
59-
.map(|(_, recipe_def)| recipe_def)
60-
.find(|recipe_def| recipe_def.id == *recipe_id)
61+
let recipe = recipe_index
62+
.get(recipe_id)
63+
.and_then(|asset_id| recipes.get(*asset_id))
6164
.expect("Selected recipe refers to non-existent recipe");
6265

6366
if inventory.consume_input(recipe).is_err() {
@@ -73,7 +76,9 @@ fn consume_input(
7376
fn progress_work(
7477
query: Query<(&mut ProcessState, &SelectedRecipe), With<Powered>>,
7578
recipes: Res<Assets<RecipeDef>>,
79+
recipe_index: Res<IndexMap<RecipeDef>>,
7680
items: Res<Assets<ItemDef>>,
81+
item_index: Res<IndexMap<ItemDef>>,
7782
time: Res<Time>,
7883
) {
7984
for (mut state, selected_recipe) in query {
@@ -89,20 +94,18 @@ fn progress_work(
8994
continue;
9095
};
9196

92-
let recipe = recipes
93-
.iter()
94-
.map(|(_, recipe_def)| recipe_def)
95-
.find(|recipe_def| recipe_def.id == *recipe_id)
97+
let recipe = recipe_index
98+
.get(recipe_id)
99+
.and_then(|asset_id| recipes.get(*asset_id))
96100
.expect("Selected recipe refers to non-existent id");
97101

98102
let output: Vec<Stack> = recipe
99103
.output
100104
.iter()
101105
.map(|(item_id, quantity)| {
102-
let item_def = items
103-
.iter()
104-
.map(|(_, item_def)| item_def)
105-
.find(|item_def| item_def.id == *item_id)
106+
let item_def = item_index
107+
.get(item_id)
108+
.and_then(|asset_id| items.get(*asset_id))
106109
.expect("Recipe refers to non-existent output item");
107110

108111
Stack {

src/simulation/recipe/select.rs

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
use bevy::prelude::*;
22

3-
use crate::simulation::{
4-
item::{Inventory, ItemDef, Stack},
5-
logistics::{InputInventory, OutputInventory},
6-
recipe::{ProcessState, assets::RecipeDef},
3+
use crate::{
4+
assets::indexing::IndexMap,
5+
simulation::{
6+
item::{Inventory, ItemDef, Stack},
7+
logistics::{InputInventory, OutputInventory},
8+
recipe::{ProcessState, assets::RecipeDef},
9+
},
710
};
811

912
pub fn plugin(app: &mut App) {
@@ -24,26 +27,23 @@ pub struct SelectRecipe(pub String);
2427
fn on_select_recipe(
2528
trigger: Trigger<SelectRecipe>,
2629
recipes: Res<Assets<RecipeDef>>,
30+
recipe_index: Res<IndexMap<RecipeDef>>,
2731
items: Res<Assets<ItemDef>>,
32+
item_index: Res<IndexMap<ItemDef>>,
2833
mut commands: Commands,
2934
) {
3035
let event = trigger.event();
3136

32-
let Some(recipe_def) = recipes
33-
.iter()
34-
.map(|(_, recipe)| recipe)
35-
.find(|recipe| recipe.id == event.0)
36-
else {
37-
warn!("Attempted to select invalid recipe");
38-
return;
39-
};
37+
let recipe_def = recipe_index
38+
.get(&event.0)
39+
.and_then(|asset_id| recipes.get(*asset_id))
40+
.expect("Attempted to select invalid recipe");
4041

4142
let mut input_inventory = Inventory::default();
42-
for id in recipe_def.input.keys() {
43-
let item_def = items
44-
.iter()
45-
.map(|(_, item)| item)
46-
.find(|item| item.id == *id)
43+
for item_id in recipe_def.input.keys() {
44+
let item_def = item_index
45+
.get(item_id)
46+
.and_then(|asset_id| items.get(*asset_id))
4747
.expect("Recipe refers to non-existent item");
4848

4949
let slot = Stack {
@@ -56,11 +56,10 @@ fn on_select_recipe(
5656
}
5757

5858
let mut output_inventory = Inventory::default();
59-
for id in recipe_def.output.keys() {
60-
let item_def = items
61-
.iter()
62-
.map(|(_, item)| item)
63-
.find(|item| item.id == *id)
59+
for item_id in recipe_def.output.keys() {
60+
let item_def = item_index
61+
.get(item_id)
62+
.and_then(|asset_id| items.get(*asset_id))
6463
.expect("Recipe refers to non-existent item");
6564

6665
let slot = Stack {

src/simulation/world/deposit.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use serde::Deserialize;
66
use crate::{
77
assets::{
88
LoadResource,
9+
indexing::IndexMap,
910
manifest::{Id, Manifest, ManifestPlugin},
1011
},
1112
screens::Screen,
@@ -108,23 +109,23 @@ fn on_mine_deposit(
108109
deposits: Query<&DepositRecipe>,
109110
mut inventory: Single<&mut PlayerInventory>,
110111
recipes: Res<Assets<RecipeDef>>,
112+
recipe_index: Res<IndexMap<RecipeDef>>,
111113
items: Res<Assets<ItemDef>>,
114+
item_index: Res<IndexMap<ItemDef>>,
112115
) {
113116
let Ok(deposit_recipe) = deposits.get(trigger.target()) else {
114117
return;
115118
};
116119

117-
let recipe_def = recipes
118-
.iter()
119-
.map(|(_, recipe_def)| recipe_def)
120-
.find(|recipe_def| recipe_def.id == deposit_recipe.0)
120+
let recipe_def = recipe_index
121+
.get(&deposit_recipe.0)
122+
.and_then(|asset_id| recipes.get(*asset_id))
121123
.expect("Deposit refers to non-existent recipe");
122124

123125
for (item_id, quantity) in recipe_def.output.iter() {
124-
let item_def = items
125-
.iter()
126-
.map(|(_, item_def)| item_def)
127-
.find(|item_def| item_def.id == *item_id)
126+
let item_def = item_index
127+
.get(item_id)
128+
.and_then(|asset_id| items.get(*asset_id))
128129
.expect("Recipe refers to invalid item id");
129130

130131
let mut stack = Stack {

src/ui/inspect/info.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use bevy::{
44
};
55

66
use crate::{
7+
assets::indexing::IndexMap,
78
simulation::recipe::{RecipeDef, SelectedRecipe},
89
ui::{
910
inspect::{InspectedEntity, InspectionMenuState},
@@ -24,6 +25,7 @@ pub fn open_recipe_menu(
2425
selected_recipes: Query<&SelectedRecipe>,
2526
// icons: Res<ItemAssets>,
2627
recipes: Res<Assets<RecipeDef>>,
28+
recipe_index: Res<IndexMap<RecipeDef>>,
2729
) {
2830
let Ok(selected_recipe) = selected_recipes.get(inspected_entity.0) else {
2931
return;
@@ -33,10 +35,9 @@ pub fn open_recipe_menu(
3335
return;
3436
};
3537

36-
let Some(recipe) = recipes
37-
.iter()
38-
.map(|(_, recipe_def)| recipe_def)
39-
.find(|recipe_def| recipe_def.id == *recipe_id)
38+
let Some(recipe) = recipe_index
39+
.get(recipe_id)
40+
.and_then(|asset_id| recipes.get(*asset_id))
4041
else {
4142
return;
4243
};

0 commit comments

Comments
 (0)