diff --git a/blastmud_game/src/db.rs b/blastmud_game/src/db.rs index c8200300..febf2961 100644 --- a/blastmud_game/src/db.rs +++ b/blastmud_game/src/db.rs @@ -333,6 +333,66 @@ impl DBTrans { .collect()) } + + pub async fn resolve_items_by_display_name_for_player( + self: &Self, + from_item: &Item, + query: &str, + include_contents: bool, + include_loc_contents: bool, + include_active_players: bool, + include_all_players: bool + ) -> DResult> { + let mut ctes: Vec = Vec::new(); + let mut include_tables: Vec<&'static str> = Vec::new(); + + let player_loc = &from_item.location; + let player_desig = format!("{}/{}", from_item.item_type, + from_item.item_code); + if include_contents { + ctes.push("contents AS (\ + SELECT details FROM items WHERE details->>'location' = $1 + )".to_owned()); + include_tables.push("SELECT details FROM contents"); + } + if include_loc_contents { + ctes.push("loc_contents AS (\ + SELECT details FROM items WHERE details->>'location' = $2 + )".to_owned()); + include_tables.push("SELECT details FROM loc_contents"); + } + if include_active_players { + ctes.push("active_players AS (\ + SELECT details FROM items WHERE details->>'item_type' = 'player' \ + AND current_session IS NOT NULL \ + )".to_owned()); + include_tables.push("SELECT details FROM active_players"); + } + if include_all_players { + ctes.push("all_players AS (\ + SELECT details FROM items WHERE details->>'item_type' = 'player' + )".to_owned()); + include_tables.push("SELECT details FROM all_players"); + } + ctes.push(format!("relevant_items AS ({})", include_tables.join(" UNION "))); + + let cte_str: String = ctes.join(", "); + + Ok(self.pg_trans()?.query( + &format!( + "WITH {} SELECT details FROM relevant_items WHERE (lower(details->>'display') LIKE $3) \ + OR (lower(details ->>'display_less_explicit') LIKE $3) \ + ORDER BY length(details->>'display') DESC \ + LIMIT 2", &cte_str), + &[&player_desig, &player_loc, + &(query.replace("\\", "\\\\") + .replace("_", "\\_") + .replace("%", "") + .to_lowercase() + "%")] + ).await?.into_iter() + .filter_map(|i| serde_json::from_value(i.get("details")).ok()) + .collect()) + } pub async fn commit(mut self: Self) -> DResult<()> { let trans_opt = self.with_trans_mut(|t| std::mem::replace(t, None)); diff --git a/blastmud_game/src/message_handler/user_commands/look.rs b/blastmud_game/src/message_handler/user_commands/look.rs index 169c5a59..0504466b 100644 --- a/blastmud_game/src/message_handler/user_commands/look.rs +++ b/blastmud_game/src/message_handler/user_commands/look.rs @@ -42,7 +42,7 @@ pub fn render_map(room: &room::Room, width: usize, height: usize) -> String { pub async fn describe_normal_item(ctx: &VerbContext<'_>, item: &Item) -> UResult<()> { ctx.trans.queue_for_session( ctx.session, - Some(&format!("{}\n{}", + Some(&format!("{}\n{}\n", explicit_if_allowed( ctx, &item.display, @@ -147,10 +147,11 @@ impl UserVerb for Verb { async fn handle(self: &Self, ctx: &mut VerbContext, _verb: &str, remaining: &str) -> UResult<()> { let player_item = get_player_item_or_fail(ctx).await?; + let rem_trim = remaining.trim().to_lowercase(); let (heretype, herecode) = player_item.location.split_once("/").unwrap_or(("room", "repro_xv_chargen")); - let (itype, icode) = if remaining == "" { - Ok((heretype, herecode)) - } else if let Some(dir) = Direction::parse(remaining) { + let (itype, icode): (String, String) = if rem_trim == "" { + Ok((heretype.to_owned(), herecode.to_owned())) + } else if let Some(dir) = Direction::parse(&rem_trim) { if heretype != "room" { // Fix this when we have planes / boats / roomkits. user_error("Navigating outside rooms not yet supported.".to_owned()) @@ -161,7 +162,7 @@ impl UserVerb for Verb { Some(exit) => { match room::resolve_exit(room, exit) { None => user_error("There is nothing in that direction".to_owned()), - Some(room2) => Ok(("room", room2.code)) + Some(room2) => Ok(("room".to_owned(), room2.code.to_owned())) } } } @@ -169,16 +170,30 @@ impl UserVerb for Verb { user_error("Can't find your current location".to_owned()) } } + } else if rem_trim == "me" || rem_trim == "self" { + Ok((player_item.item_type.clone(), player_item.item_code.clone())) } else { - user_error("Sorry, I don't understand what you want to look at.".to_owned()) + match &ctx.trans.resolve_items_by_display_name_for_player( + &player_item, + &rem_trim, + true, true, false, false + ).await?[..] { + [] => user_error("Sorry, I couldn't find anything matching.".to_owned()), + [match_it] => Ok((match_it.item_type.clone(), match_it.item_code.clone())), + [item1, ..] if item1.display.to_lowercase() == rem_trim || + item1.display_less_explicit.as_ref().map(|x|x.to_lowercase()) == Some(rem_trim) => + Ok((item1.item_type.clone(), item1.item_code.clone())), + _ => user_error("Sorry, that name is ambiguous, please be more specific.".to_owned()) + } }?; - let item = ctx.trans.find_item_by_type_code(itype, icode).await? + let item = ctx.trans.find_item_by_type_code(&itype, &icode).await? .ok_or_else(|| UserError("Sorry, that no longer exists".to_owned()))?; if itype != "room" { describe_normal_item(ctx, &item).await?; } else { let room = - room::room_map_by_code().get(icode).ok_or_else(|| UserError("Sorry, that room no longer exists".to_owned()))?; + room::room_map_by_code().get(icode.as_str()) + .ok_or_else(|| UserError("Sorry, that room no longer exists".to_owned()))?; describe_room(ctx, &room, &list_item_contents(ctx, &item).await?).await?; } Ok(()) diff --git a/blastmud_game/src/message_handler/user_commands/register.rs b/blastmud_game/src/message_handler/user_commands/register.rs index 2e9d2be2..bbfa67cd 100644 --- a/blastmud_game/src/message_handler/user_commands/register.rs +++ b/blastmud_game/src/message_handler/user_commands/register.rs @@ -34,6 +34,7 @@ impl UserVerb for Verb { item_type: "player".to_owned(), item_code: username.to_lowercase(), display: username.to_owned(), + details: Some("A non-descript individual".to_owned()), location: "room/repro_xv_chargen".to_owned(), ..Item::default() }).await?; diff --git a/schema/schema.sql b/schema/schema.sql index 59842d18..33011e5d 100644 --- a/schema/schema.sql +++ b/schema/schema.sql @@ -24,7 +24,9 @@ CREATE TABLE items ( CREATE UNIQUE INDEX item_index ON items ((details->>'item_type'), (details->>'item_code')); CREATE INDEX item_by_loc ON items ((details->>'location')); CREATE INDEX item_by_static ON items ((cast(details->>'is_static' as boolean))); - +CREATE INDEX item_by_display ON items (lower(details->>'display')); +CREATE INDEX item_by_display_less_explicit ON items (lower(details->>'display_less_explicit')); + CREATE TABLE users ( -- Username here is all lower case, but details has correct case version. username TEXT NOT NULL PRIMARY KEY,