blastmud/blastmud_game/src/static_content.rs
2023-01-02 15:39:55 +11:00

116 lines
4.0 KiB
Rust

use crate::DResult;
use crate::db::DBPool;
use crate::models::item::Item;
use std::collections::{BTreeSet, BTreeMap};
use log::info;
pub mod room;
pub mod npc;
mod fixed_item;
pub struct StaticItem {
pub item_code: &'static str,
pub initial_item: Box<dyn Fn() -> Item>
}
struct StaticItemTypeGroup {
item_type: &'static str,
items: fn () -> Box<dyn Iterator<Item = StaticItem>>
}
fn static_item_registry() -> Vec<StaticItemTypeGroup> {
vec!(
// Must have no duplicates.
StaticItemTypeGroup {
item_type: "npc",
items: || npc::npc_static_items()
},
StaticItemTypeGroup {
item_type: "room",
items: || room::room_static_items()
},
StaticItemTypeGroup {
item_type: "fixed_item",
items: || fixed_item::static_items()
},
)
}
async fn refresh_static_items(pool: &DBPool) -> DResult<()> {
let registry = static_item_registry();
let expected_type: BTreeSet<String> =
registry.iter().map(|x| x.item_type.to_owned()).collect();
let cur_types: Box<BTreeSet<String>> = pool.find_static_item_types().await?;
for item_type in cur_types.difference(&expected_type) {
pool.delete_static_items_by_type(item_type).await?;
}
for type_group in registry.iter() {
info!("Checking static_content of item_type {}", type_group.item_type);
let tx = pool.start_transaction().await?;
let existing_items = tx.find_static_items_by_type(type_group.item_type).await?;
let expected_items: BTreeMap<String, StaticItem> =
(type_group.items)().map(|x| (x.item_code.to_owned(), x)).collect();
let expected_set: BTreeSet<String> = expected_items.keys().map(|x|x.to_owned()).collect();
for unwanted_item in existing_items.difference(&expected_set) {
info!("Deleting item {:?}", unwanted_item);
tx.delete_static_items_by_code(type_group.item_type, unwanted_item).await?;
}
for new_item_code in expected_set.difference(&existing_items) {
info!("Creating item {:?}", new_item_code);
tx.create_item(&(expected_items.get(new_item_code)
.unwrap().initial_item)()).await?;
}
for existing_item_code in expected_set.intersection(&existing_items) {
tx.limited_update_static_item(
&(expected_items.get(existing_item_code)
.unwrap().initial_item)()).await?;
}
tx.commit().await?;
info!("Committed any changes for static_content of item_type {}", type_group.item_type);
}
Ok(())
}
pub async fn refresh_static_content(pool: &DBPool) -> DResult<()> {
refresh_static_items(pool).await?;
Ok(())
}
#[cfg(test)]
mod test {
use itertools::Itertools;
use super::*;
#[test]
fn no_duplicate_static_content() {
let mut registry = static_item_registry();
registry.sort_unstable_by(|x, y| x.item_type.cmp(y.item_type));
let duplicates: Vec<&'static str> =
registry.iter()
.group_by(|x| x.item_type).into_iter()
.filter_map(|(k, v)| if v.count() <= 1 { None } else { Some(k) })
.collect();
if duplicates.len() > 0 {
panic!("static_item_registry has duplicate item_types: {:}", duplicates.join(", "));
}
for type_group in registry.iter() {
let iterator : Box<dyn Iterator<Item = StaticItem>> = (type_group.items)();
let duplicates: Vec<&'static str> = iterator
.group_by(|x| x.item_code)
.into_iter()
.filter_map(|(k, v)| if v.count() <= 1 { None } else { Some(k) })
.collect();
if duplicates.len() > 0 {
panic!("static_item_registry has duplicate item_codes for {}: {:}",
type_group.item_type,
duplicates.join(", "));
}
}
}
}