From d4fd71d8391e39e6c5689939010d8070c59cf7e9 Mon Sep 17 00:00:00 2001 From: Condorra Date: Mon, 20 Feb 2023 22:27:43 +1100 Subject: [PATCH] Improve get targeting with 'all' option. --- blastmud_game/src/db.rs | 62 +++++++++++++++---- .../src/message_handler/user_commands.rs | 8 +++ .../message_handler/user_commands/attack.rs | 1 + .../src/message_handler/user_commands/get.rs | 25 ++++++-- .../src/message_handler/user_commands/look.rs | 1 + .../message_handler/user_commands/parsing.rs | 10 +++ .../message_handler/user_commands/whisper.rs | 1 + .../message_handler/user_commands/wield.rs | 1 + 8 files changed, 92 insertions(+), 17 deletions(-) diff --git a/blastmud_game/src/db.rs b/blastmud_game/src/db.rs index 3e01bdfc..16da240e 100644 --- a/blastmud_game/src/db.rs +++ b/blastmud_game/src/db.rs @@ -235,6 +235,8 @@ pub struct ItemSearchParams<'l> { pub include_loc_contents: bool, pub include_active_players: bool, pub include_all_players: bool, + pub item_type_only: Option<&'l str>, + pub limit: u8, pub dead_first: bool, } @@ -247,6 +249,8 @@ impl ItemSearchParams<'_> { include_active_players: false, include_all_players: false, dead_first: false, + limit: 100, + item_type_only: None, } } } @@ -521,8 +525,30 @@ impl DBTrans { let player_desig = format!("{}/{}", search.from_item.item_type, search.from_item.item_code); - let (offset, query) = parse_offset(search.query); - let mut param_no: usize = 5; + let (offset, mut query) = parse_offset(search.query); + let mut param_no: usize = 6; + let mut extra_order: String = String::new(); + let mut extra_where: String = String::new(); + + let mut dead_only = false; + if query.starts_with("dead") { + query = query[4..].trim(); + dead_only = true; + } else if query.starts_with("corpse of") { + query = query[9..].trim(); + dead_only = true; + } else if query.starts_with("corpse") { + query = query[6..].trim(); + dead_only = true; + } + if dead_only { + extra_where.push_str(" AND COALESCE(CAST(details->>'is_dead' AS boolean), false) = true"); + } else if search.dead_first { + extra_order.push_str(" COALESCE(CAST(details->>'is_dead' AS boolean), false) DESC,"); + } else { + extra_order.push_str(" COALESCE(CAST(details->>'is_dead' AS boolean), false) ASC,"); + } + let query_wildcard = query.replace("\\", "\\\\") .replace("_", "\\_") .replace("%", "") @@ -530,11 +556,24 @@ impl DBTrans { let offset_sql = offset.map(|x| (if x >= 1 { x - 1 } else { x}) as i64).unwrap_or(0); let query_json = serde_json::to_value(query.to_lowercase())?; let query_len = query.len() as i32; + let limit = search.limit as i64; let mut params: Vec<&(dyn ToSql + Sync)> = vec!( &query_wildcard, - &offset_sql, &query_len, &query_json); - - + &offset_sql, + &query_len, + &query_json, + &limit + ); + + match search.item_type_only { + None => {} + Some(ref item_type) => { + extra_where.push_str(&format!(" AND details->>'item_type' = ${}", param_no)); + param_no += 1; + params.push(item_type); + } + } + if search.include_contents { ctes.push(format!("contents AS (\ SELECT details, details->'aliases' AS aliases FROM items WHERE details->>'location' = ${}\ @@ -567,14 +606,15 @@ impl DBTrans { ctes.push(format!("relevant_items AS ({})", include_tables.join(" UNION "))); let cte_str: String = ctes.join(", "); - + Ok(Arc::new(self.pg_trans()?.query( &format!( - "WITH {} SELECT details, aliases FROM relevant_items WHERE (lower(details->>'display') LIKE $1) \ - OR (lower(details ->>'display_less_explicit') LIKE $1) \ - OR aliases @> $4 \ - ORDER BY ABS(length(details->>'display')-$3) ASC \ - LIMIT 1 OFFSET $2", &cte_str), + "WITH {} SELECT details, aliases FROM relevant_items WHERE \ + ((lower(details->>'display') LIKE $1) \ + OR (lower(details ->>'display_less_explicit') LIKE $1) \ + OR aliases @> $4) {} \ + ORDER BY {} ABS(length(details->>'display')-$3) ASC \ + LIMIT $5 OFFSET $2", &cte_str, &extra_where, &extra_order), ¶ms ).await?.into_iter() .filter_map(|i| serde_json::from_value(i.get("details")).ok()) diff --git a/blastmud_game/src/message_handler/user_commands.rs b/blastmud_game/src/message_handler/user_commands.rs index af7cc6d1..85ebfcaa 100644 --- a/blastmud_game/src/message_handler/user_commands.rs +++ b/blastmud_game/src/message_handler/user_commands.rs @@ -242,6 +242,14 @@ pub async fn search_item_for_user<'l>(ctx: &'l VerbContext<'l>, search: &'l Item }) } +pub async fn search_items_for_user<'l>(ctx: &'l VerbContext<'l>, search: &'l ItemSearchParams<'l>) -> + UResult>> { + Ok(match &ctx.trans.resolve_items_by_display_name_for_player(search).await?[..] { + [] => user_error("Sorry, I couldn't find anything matching.".to_owned())?, + v => v.into_iter().map(|it| it.clone()).collect(), + }) +} + #[cfg(test)] mod test { use crate::db::MockDBTrans; diff --git a/blastmud_game/src/message_handler/user_commands/attack.rs b/blastmud_game/src/message_handler/user_commands/attack.rs index 36b34f9e..f98eb3ec 100644 --- a/blastmud_game/src/message_handler/user_commands/attack.rs +++ b/blastmud_game/src/message_handler/user_commands/attack.rs @@ -22,6 +22,7 @@ impl UserVerb for Verb { let attack_whom = search_item_for_user(ctx, &ItemSearchParams { include_loc_contents: true, + limit: 1, ..ItemSearchParams::base(&player_item, remaining) }).await?; diff --git a/blastmud_game/src/message_handler/user_commands/get.rs b/blastmud_game/src/message_handler/user_commands/get.rs index 7a21ef62..d32eecaf 100644 --- a/blastmud_game/src/message_handler/user_commands/get.rs +++ b/blastmud_game/src/message_handler/user_commands/get.rs @@ -6,7 +6,8 @@ use super::{ ItemSearchParams, user_error, get_player_item_or_fail, - search_item_for_user, + search_items_for_user, + parsing::parse_count }; use crate::{ static_content::possession_type::possession_data, @@ -115,20 +116,32 @@ impl QueueCommandHandler for QueueHandler { pub struct Verb; #[async_trait] impl UserVerb for Verb { - async fn handle(self: &Self, ctx: &mut VerbContext, _verb: &str, remaining: &str) -> UResult<()> { + async fn handle(self: &Self, ctx: &mut VerbContext, _verb: &str, mut remaining: &str) -> UResult<()> { let player_item = get_player_item_or_fail(ctx).await?; // TODO: Parse "get target from container" variant - let target = search_item_for_user(ctx, &ItemSearchParams { + let mut get_limit = Some(1); + if remaining == "all" || remaining.starts_with("all ") { + remaining = remaining[3..].trim(); + get_limit = None; + } else if let (Some(n), remaining2) = parse_count(remaining) { + get_limit = Some(n); + remaining = remaining2; + } + let targets = search_items_for_user(ctx, &ItemSearchParams { include_loc_contents: true, + item_type_only: Some("possession"), + limit: get_limit.unwrap_or(100), ..ItemSearchParams::base(&player_item, &remaining) }).await?; if player_item.is_dead { user_error("You try to get it, but your ghostly hands slip through it uselessly".to_owned())?; } - if target.item_type != "possession" { - user_error("You can't get that!".to_owned())?; + for target in targets { + if target.item_type != "possession" { + user_error("You can't get that!".to_owned())?; + } + queue_command(ctx, &QueueCommand::Get { possession_id: target.item_code.clone() }).await?; } - queue_command(ctx, &QueueCommand::Get { possession_id: target.item_code.clone() }).await?; Ok(()) } } diff --git a/blastmud_game/src/message_handler/user_commands/look.rs b/blastmud_game/src/message_handler/user_commands/look.rs index 0e5fe671..628613f7 100644 --- a/blastmud_game/src/message_handler/user_commands/look.rs +++ b/blastmud_game/src/message_handler/user_commands/look.rs @@ -219,6 +219,7 @@ impl UserVerb for Verb { &ItemSearchParams { include_contents: true, include_loc_contents: true, + limit: 1, ..ItemSearchParams::base(&player_item, &rem_trim) } ).await? diff --git a/blastmud_game/src/message_handler/user_commands/parsing.rs b/blastmud_game/src/message_handler/user_commands/parsing.rs index 6296d0ba..fc57de04 100644 --- a/blastmud_game/src/message_handler/user_commands/parsing.rs +++ b/blastmud_game/src/message_handler/user_commands/parsing.rs @@ -45,6 +45,16 @@ pub fn parse_offset(input: &str) -> (Option, &str) { } } +pub fn parse_count(input: &str) -> (Option, &str) { + fn parser(input: &str) -> IResult<&str, u8> { + terminated(u8, char(' '))(input) + } + match parser(input) { + Err(_) => (None, input), + Ok((rest, result)) => (Some(result), rest) + } +} + pub fn parse_username(input: &str) -> Result<(&str, &str), &'static str> { const CATCHALL_ERROR: &'static str = "Must only contain alphanumeric characters or _"; fn parse_valid(input: &str) -> IResult<&str, (), VerboseError<&str>> { diff --git a/blastmud_game/src/message_handler/user_commands/whisper.rs b/blastmud_game/src/message_handler/user_commands/whisper.rs index 4a65a496..e0a71e0a 100644 --- a/blastmud_game/src/message_handler/user_commands/whisper.rs +++ b/blastmud_game/src/message_handler/user_commands/whisper.rs @@ -22,6 +22,7 @@ impl UserVerb for Verb { } let to_whom = search_item_for_user(ctx, &ItemSearchParams { include_loc_contents: true, + limit: 1, ..ItemSearchParams::base(&player_item, &to_whom_name) }).await?; diff --git a/blastmud_game/src/message_handler/user_commands/wield.rs b/blastmud_game/src/message_handler/user_commands/wield.rs index 7e3f5552..f6fad53f 100644 --- a/blastmud_game/src/message_handler/user_commands/wield.rs +++ b/blastmud_game/src/message_handler/user_commands/wield.rs @@ -117,6 +117,7 @@ impl UserVerb for Verb { let player_item = get_player_item_or_fail(ctx).await?; let weapon = search_item_for_user(ctx, &ItemSearchParams { include_contents: true, + limit: 1, ..ItemSearchParams::base(&player_item, &remaining) }).await?; if player_item.is_dead {