/* Hurry Curry! - a game about cooking Copyright (C) 2025 Hurry Curry! Contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License only. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ use hurrycurry_protocol::{Gamedata, ItemIndex, Recipe, TileIndex}; use log::debug; use std::{ collections::{BTreeSet, HashMap}, sync::RwLock, }; use crate::Serverdata; #[derive(Default)] pub(crate) struct ItemTileRegistry { tiles: RwLock>, items: RwLock>, } impl ItemTileRegistry { pub fn register_tile(&self, name: String) -> TileIndex { TileIndex(Self::register(&self.tiles, name)) } pub fn register_item(&self, name: String) -> ItemIndex { ItemIndex(Self::register(&self.items, name)) } fn register(db: &RwLock>, name: String) -> usize { let mut db = db.write().unwrap(); // TODO maybe btreemap for better time complexity if let Some(index) = db.iter().position(|e| e == &name) { index } else { let index = db.len(); db.push(name); index } } pub fn finish(self) -> (Vec, Vec) { ( self.items.into_inner().unwrap(), self.tiles.into_inner().unwrap(), ) } } pub(crate) fn filter_unused_tiles_and_items(data: &mut Gamedata, serverdata: &mut Serverdata) { debug!("running unused item/tile filter"); let mut used_items = BTreeSet::new(); let mut used_tiles = BTreeSet::new(); for recipe in &data.recipes { used_items.extend(recipe.inputs()); used_items.extend(recipe.outputs()); used_tiles.extend(recipe.tile()); } for demand in &data.demands { used_items.insert(demand.input); used_items.extend(demand.output); } for rg in serverdata.recipe_groups.values() { used_items.extend(rg); } for &(tile, item) in serverdata.initial_map.values() { used_tiles.insert(tile); used_items.extend(item); } let mut item_names = Vec::new(); let mut item_map = HashMap::new(); for item in used_items { item_map.insert(item, ItemIndex(item_names.len())); item_names.push(data.item_name(item).to_string()); } let mut tile_names = Vec::new(); let mut tile_map = HashMap::new(); for tile in used_tiles { tile_map.insert(tile, TileIndex(tile_names.len())); tile_names.push(data.tile_name(tile).to_string()); } debug!( "removing {} items and {} tiles from registry", data.item_names.len() - item_names.len(), data.tile_names.len() - tile_names.len() ); data.item_names = item_names; data.tile_names = tile_names; for recipe in &mut data.recipes { match recipe { Recipe::Passive { tile, input, output, .. } => { if let Some(tile) = tile { *tile = tile_map[tile] } *input = item_map[input]; if let Some(output) = output { *output = item_map[output]; } } Recipe::Active { tile, input, outputs, .. } => { if let Some(tile) = tile { *tile = tile_map[tile] } *input = item_map[input]; for output in outputs { if let Some(output) = output { *output = item_map[output]; } } } Recipe::Instant { tile, inputs, outputs, .. } => { if let Some(tile) = tile { *tile = tile_map[tile] } for input in inputs { if let Some(input) = input { *input = item_map[input]; } } for output in outputs { if let Some(output) = output { *output = item_map[output]; } } } } } for demand in &mut data.demands { demand.input = item_map[&demand.input]; if let Some(output) = &mut demand.output { *output = item_map[output]; } } for rg in serverdata.recipe_groups.values_mut() { *rg = rg.clone().into_iter().map(|e| item_map[&e]).collect(); } for (tile, item) in serverdata.initial_map.values_mut() { *tile = tile_map[tile]; if let Some(item) = item { *item = item_map[item] } } data.tile_walkable = data .tile_walkable .clone() .into_iter() .map(|e| tile_map[&e]) .collect(); data.tile_interactable_empty = data .tile_interactable_empty .clone() .into_iter() .map(|e| tile_map[&e]) .collect(); data.tile_placeable_items = data .tile_placeable_items .clone() .into_iter() .map(|(tile, items)| { ( tile_map[&tile], items.into_iter().map(|i| item_map[&i]).collect(), ) }) .collect(); debug!("done") }