From d362c2f3717861b6c37dd54bf5bd164ed7ba0d2c Mon Sep 17 00:00:00 2001 From: Shagnor Date: Tue, 27 Dec 2022 23:35:27 +1100 Subject: [PATCH] Start modelling and supporting rooms / zones. --- Cargo.lock | 1 + blastmud_game/Cargo.toml | 1 + .../message_handler/user_commands/register.rs | 2 +- blastmud_game/src/static_content.rs | 67 +++++---- blastmud_game/src/static_content/room.rs | 133 +++++++++++++++++- 5 files changed, 175 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8695d3c7..dc754133 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -116,6 +116,7 @@ dependencies = [ "log", "nix", "nom", + "once_cell", "ouroboros", "phf", "ring", diff --git a/blastmud_game/Cargo.toml b/blastmud_game/Cargo.toml index 431dd21a..afcdbe73 100644 --- a/blastmud_game/Cargo.toml +++ b/blastmud_game/Cargo.toml @@ -33,3 +33,4 @@ chrono = { version = "0.4.23", features = ["serde"] } bcrypt = "0.13.0" validator = "0.16.0" itertools = "0.10.5" +once_cell = "1.16.0" diff --git a/blastmud_game/src/message_handler/user_commands/register.rs b/blastmud_game/src/message_handler/user_commands/register.rs index 6f9ee04f..ff22ce21 100644 --- a/blastmud_game/src/message_handler/user_commands/register.rs +++ b/blastmud_game/src/message_handler/user_commands/register.rs @@ -34,7 +34,7 @@ impl UserVerb for Verb { item_type: "player".to_owned(), item_code: username.to_lowercase(), display: username.to_owned(), - location: "room/chargen_room".to_owned(), + location: "room/repro_xv_chargen".to_owned(), ..Item::default() }).await?; diff --git a/blastmud_game/src/static_content.rs b/blastmud_game/src/static_content.rs index 256d538d..085ac493 100644 --- a/blastmud_game/src/static_content.rs +++ b/blastmud_game/src/static_content.rs @@ -1,15 +1,15 @@ use crate::DResult; use crate::db::DBPool; use crate::models::item::Item; -use log::error; use itertools::Itertools; use std::collections::{BTreeSet, BTreeMap}; +use log::info; mod room; pub struct StaticItem { pub item_code: &'static str, - pub initial_item: fn () -> Item + pub initial_item: Box Item> } struct StaticItemTypeGroup { @@ -26,25 +26,15 @@ fn static_item_registry() -> Vec { }, StaticItemTypeGroup { item_type: "room", - items: || room::static_items() + items: || room::room_static_items() }, ) } async fn refresh_static_items(pool: &DBPool) -> DResult<()> { - let mut registry = static_item_registry(); - registry.sort_unstable_by(|x, y| x.item_type.cmp(y.item_type)); + let registry = static_item_registry(); - 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 { - error!("static_item_registry has duplicate item_types: {:}", duplicates.join(", ")); - Err("Duplicate item_types in static_item_registry")?; - } let expected_type: BTreeSet = registry.iter().map(|x| x.item_type.to_owned()).collect(); let cur_types: Box> = pool.find_static_item_types().await?; @@ -53,18 +43,7 @@ async fn refresh_static_items(pool: &DBPool) -> DResult<()> { } for type_group in registry.iter() { - let iterator : Box> = (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 { - error!("static_item_registry has duplicate item_codes for {}: {:}", - type_group.item_type, - duplicates.join(", ")); - Err("Duplicate item_types in static_item_registry")?; - } + 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 = @@ -77,6 +56,8 @@ async fn refresh_static_items(pool: &DBPool) -> DResult<()> { tx.create_item(&(expected_items.get(new_item_code) .unwrap().initial_item)()).await?; } + tx.commit().await?; + info!("Committed any changes for static_content of item_type {}", type_group.item_type); } Ok(()) } @@ -85,3 +66,37 @@ pub async fn refresh_static_content(pool: &DBPool) -> DResult<()> { refresh_static_items(pool).await?; Ok(()) } + +#[cfg(test)] +mod test { + 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> = (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(", ")); + } + } + } +} diff --git a/blastmud_game/src/static_content/room.rs b/blastmud_game/src/static_content/room.rs index fde23f83..0fda9676 100644 --- a/blastmud_game/src/static_content/room.rs +++ b/blastmud_game/src/static_content/room.rs @@ -1,5 +1,134 @@ use super::StaticItem; +use once_cell::sync::OnceCell; +use std::collections::BTreeMap; +use ansi_macro::ansi; +use crate::models::item::Item; -pub fn static_items() -> Box> { - Box::new(vec!().into_iter()) +pub struct Zone { + pub code: &'static str, + pub display: &'static str, + pub outdoors: bool, +} + +static STATIC_ZONE_DETAILS: OnceCell> = OnceCell::new(); +pub fn zone_details() -> &'static BTreeMap<&'static str, Zone> { + STATIC_ZONE_DETAILS.get_or_init( + || vec!( + Zone { code: "melbs", + display: "Melbs", + outdoors: true }, + Zone { code: "repro_xv", + display: "Reprolabs XV", + outdoors: true }, + ).into_iter().map(|x|(x.code, x)).collect()) +} + +pub struct GridCoords { + x: i64, + y: i64, + z: i64 +} + +pub enum ExitType { + Free, // Anyone can just walk it. + // Future ideas: Doors with locks, etc... +} + +pub enum Direction { + NORTH, + SOUTH, + EAST, + WEST, + NORTHEAST, + SOUTEAST, + NORTHWEST, + SOUTHWEST, + UP, + DOWN, + IN(&'static str) +} + +pub enum ExitTarget { + UseGPS, + Custom(&'static str) +} + +pub struct Exit { + direction: Direction, + target: ExitTarget, + exit_type: ExitType +} + +pub struct Room { + pub zone: &'static str, + pub code: &'static str, + pub name: &'static str, + pub short: &'static str, + pub grid_coords: GridCoords, + pub description: &'static str, + pub exits: Vec +} + +static STATIC_ROOM_LIST: OnceCell> = OnceCell::new(); +pub fn room_list() -> &'static Vec { + STATIC_ROOM_LIST.get_or_init( + || vec!( + Room { + zone: "repro_xv", + code: "repro_xv_chargen", + name: "Choice Room", + short: ansi!("CR"), + description: "A room brightly lit in unnaturally white light, covered in sparkling \ + white tiles from floor to \ + ceiling. A loudspeaker plays a message on loop:\r\n\ + \t\"Citizen, you are here because your memory has been wiped and you \ + are ready to start a fresh life. As a being enhanced by Gazos-Murlison \ + Co technology, the emperor has granted you the power to choose 14 points \ + of upgrades to yourself. Choose wisely, as it will impact who you end up \ + being, and you would need to completely wipe your brain again to change \ + them. Talk to Statbot to spend your 14 points and create your body.\"\r\n\ + [Try \"statbot hi, to send hi to statbot - the \" means to \ + whisper to a particular person in the room]", + grid_coords: GridCoords { x: 0, y: 0, z: 2 }, + exits: vec!() + }, + ).into_iter().collect()) +} + +static STATIC_ROOM_MAP_BY_CODE: OnceCell> = OnceCell::new(); +pub fn room_map_by_code() -> &'static BTreeMap<&'static str, &'static Room> { + STATIC_ROOM_MAP_BY_CODE.get_or_init( + || room_list().iter().map(|r| (r.code, r)).collect()) +} + +pub fn room_static_items() -> Box> { + Box::new(room_list().iter().map(|r| StaticItem { + item_code: r.code, + initial_item: Box::new(|| Item { + item_code: r.code.to_owned(), + item_type: "room".to_owned(), + display: r.description.to_owned(), + location: r.code.to_owned(), + is_static: true, + ..Item::default() + }) + })) +} + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn room_zones_should_exist() { + for room in room_list() { + zone_details().get(room.zone).expect( + &format!("zone {} for room {} should exist", room.zone, room.code)); + } + } + + #[test] + fn room_map_by_code_should_have_repro_xv_chargen() { + assert_eq!(room_map_by_code().get("repro_xv_chargen").expect("repro_xv_chargen to exist").code, + "repro_xv_chargen"); + } }