From a8c279e13eb1a2fe5733a76470b3a0fca8f79232 Mon Sep 17 00:00:00 2001 From: Condorra Date: Sat, 28 Sep 2024 21:39:16 +1000 Subject: [PATCH] Add triggers + removing triggers / aliases. --- Cargo.lock | 36 ++++++++++ Cargo.toml | 1 + src/lua_engine.rs | 12 ++++ src/lua_engine/frames.rs | 43 ++++++++++-- src/lua_engine/muds.rs | 148 ++++++++++++++++++++++++++++++++++++--- src/match_table.rs | 5 +- src/websocket.rs | 7 +- 7 files changed, 229 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 466e860..b85d377 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1139,6 +1139,15 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" +[[package]] +name = "strip-ansi-escapes" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa" +dependencies = [ + "vte", +] + [[package]] name = "syn" version = "1.0.109" @@ -1284,12 +1293,38 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vte" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" +dependencies = [ + "utf8parse", + "vte_generate_state_changes", +] + +[[package]] +name = "vte_generate_state_changes" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1396,6 +1431,7 @@ dependencies = [ "regex", "serde", "serde_json", + "strip-ansi-escapes", "thiserror", "unicode-segmentation", "unicode-width", diff --git a/Cargo.toml b/Cargo.toml index 2b6cde8..6a1899c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,4 @@ serde = "1.0.209" serde_json = "1.0.127" gc-arena = { git = "https://github.com/kyren/gc-arena.git", rev = "5a7534b883b703f23cfb8c3cfdf033460aa77ea9" } regex = "1.10.6" +strip-ansi-escapes = "0.2.0" diff --git a/src/lua_engine.rs b/src/lua_engine.rs index ec9f730..d28dcf7 100644 --- a/src/lua_engine.rs +++ b/src/lua_engine.rs @@ -158,8 +158,16 @@ pub fn install_lua_globals( ) .map_err(|_| Error::msg("Can't add command"))?; }; + ($sym: ident, $name: literal) => { + cmd_table + .set(ctx, ctx.intern_static($name.as_bytes()), $sym(ctx)) + .map_err(|_| Error::msg("Can't add command"))?; + }; } + register_stateless_command!(mud_trigger, "act"); + register_stateless_command!(mud_trigger, "action"); register_stateless_command!(alias); + register_stateless_command!(unalias); register_command!(close_mud); register_command!(connect_mud); register_stateless_command!(create_match_table); @@ -170,6 +178,10 @@ pub fn install_lua_globals( register_command!(hsplit); register_command!(panel_merge); register_command!(sendmud_raw); + register_stateless_command!(mud_trigger, "untrigger"); + register_stateless_command!(mud_untrigger, "unact"); + register_stateless_command!(mud_untrigger, "unaction"); + register_stateless_command!(mud_untrigger, "untrigger"); register_command!(vsplit); ctx.set_global("commands", cmd_table); let info_table = Table::new(&ctx); diff --git a/src/lua_engine/frames.rs b/src/lua_engine/frames.rs index 9e87496..ebd698c 100644 --- a/src/lua_engine/frames.rs +++ b/src/lua_engine/frames.rs @@ -1,7 +1,7 @@ use crate::{ echo_to_term_frame, id_intern::intern_id, - match_table::{create_match_table, match_table_add}, + match_table::{create_match_table, match_table_add, match_table_remove}, GlobalLayoutCell, GlobalLayoutState, GlobalMemoCell, TermFrame, }; use gc_arena::{Gc, Rootable}; @@ -15,7 +15,7 @@ use yew::UseStateSetter; use super::call_checking_metatable; -pub fn alias<'gc, 'a>(ctx: Context<'gc>) -> Callback<'gc> { +pub fn alias(ctx: Context<'_>) -> Callback<'_> { Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { let info: Table = ctx.get_global("info")?; let cur_frame_id: TermFrame = @@ -25,7 +25,7 @@ pub fn alias<'gc, 'a>(ctx: Context<'gc>) -> Callback<'gc> { let aliases: UserData = cur_frame.get(ctx, "aliases")?; if stack.is_empty() { - return list_aliases(cur_frame_id, aliases, ctx); + return list_match_tab(cur_frame_id, aliases, ctx); } let alias_match: piccolo::String = piccolo::String::from_value( @@ -61,7 +61,42 @@ pub fn alias<'gc, 'a>(ctx: Context<'gc>) -> Callback<'gc> { }) } -fn list_aliases<'gc>( +pub fn unalias(ctx: Context<'_>) -> Callback<'_> { + Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { + let info: Table = ctx.get_global("info")?; + let cur_frame_id: TermFrame = + try_unwrap_frame(ctx, &info.get(ctx, ctx.intern_static(b"current_frame"))?)?; + let frames: Table = ctx.get_global("frames")?; + let cur_frame: Table = frames.get(ctx, cur_frame_id.0 as i64)?; + let aliases: UserData = cur_frame.get(ctx, "aliases")?; + + let alias_match: piccolo::String = piccolo::String::from_value( + ctx, + stack + .pop_front() + .ok_or_else(|| anyhow::Error::msg("Missing alias match"))?, + )?; + if !stack.is_empty() { + Err(anyhow::Error::msg( + "Extra arguments to unalias command. Repeating the substitution is not necessary.", + ))?; + } + + stack.push_back(aliases.into_value(ctx)); + stack.push_back(alias_match.into_value(ctx)); + let seq = async_sequence(&ctx, |locals, mut seq| { + let remove_func = locals.stash(&ctx, Function::Callback(match_table_remove(ctx))); + async move { + seq.call(&remove_func, 0).await?; + Ok(SequenceReturn::Return) + } + }); + + Ok(piccolo::CallbackReturn::Sequence(seq)) + }) +} + +pub fn list_match_tab<'gc>( frame: TermFrame, aliases: UserData<'gc>, ctx: Context<'gc>, diff --git a/src/lua_engine/muds.rs b/src/lua_engine/muds.rs index 14b74fe..62601f2 100644 --- a/src/lua_engine/muds.rs +++ b/src/lua_engine/muds.rs @@ -2,7 +2,7 @@ use anyhow::Error; use gc_arena::{Gc, Rootable}; use piccolo::{ self, async_sequence, Callback, CallbackReturn, Context, FromValue, Function, IntoValue, - SequenceReturn, StashedTable, StashedValue, Table, UserData, Value, + SequenceReturn, StashedTable, StashedUserData, StashedValue, Table, UserData, Value, }; use wasm_bindgen::JsValue; use web_sys::console; @@ -11,12 +11,13 @@ use yew::UseStateSetter; use crate::{ command_handler::execute_queue, id_intern::{intern_id, unintern_id}, + match_table::{create_match_table, match_table_add, match_table_remove}, telnet::{parse_telnet_buf, TelnetOutput}, websocket::{connect_websocket, send_message_to_mud, WebSocketId}, - GlobalLayoutCell, GlobalMemoCell, + GlobalLayoutCell, GlobalMemoCell, TermFrame, }; -use super::{call_checking_metatable, try_unwrap_frame, LuaState}; +use super::{call_checking_metatable, list_match_tab, try_unwrap_frame, LuaState}; fn try_unwrap_socketid<'gc>( ctx: Context<'gc>, @@ -69,10 +70,9 @@ pub fn handle_websocket_output( pub fn handle_websocket_output_log_err( socket: &WebSocketId, globals: &GlobalMemoCell, - engine: &mut LuaState, input: &[u8], ) { - match handle_websocket_output(socket, engine, input) { + match handle_websocket_output(socket, &mut globals.lua_engine.borrow_mut(), input) { Ok(()) => {} Err(e) => console::log_2( &JsValue::from_str("An error occurred calling the WebSocket input handler"), @@ -102,12 +102,8 @@ pub fn handle_websocket_close(socket: &WebSocketId, engine: &mut LuaState) -> Re .map_err(|err| format!("{}", err)) } -pub fn handle_websocket_close_log_err( - socket: &WebSocketId, - globals: &GlobalMemoCell, - engine: &mut LuaState, -) { - match handle_websocket_close(socket, engine) { +pub fn handle_websocket_close_log_err(socket: &WebSocketId, globals: &GlobalMemoCell) { + match handle_websocket_close(socket, &mut globals.lua_engine.borrow_mut()) { Ok(()) => {} Err(e) => console::log_2( &JsValue::from_str("An error occurred calling the WebSocket input handler"), @@ -388,7 +384,18 @@ pub(super) fn mudoutput_line<'gc>( )?; let frameroutes: Table = mud.get(ctx, "frameroutes")?; + let triggers: UserData = mud.get(ctx, "triggers")?; + let default_frame: Value = frameroutes + .iter() + .map(|(_k, v)| Ok::(Table::from_value(ctx, v)?)) + .next() + .unwrap_or_else(|| Ok(ctx.get_global::("frames")?.get(ctx, 1_i64)?))? + .get(ctx, "frame")?; + console::log_1(&JsValue::from_str(&format!( + "Line to match: {:?}", + line.as_bytes() + ))); let seq = async_sequence(&ctx, |locals, mut seq| { let frameroutes: Vec = frameroutes .iter() @@ -399,7 +406,16 @@ pub(super) fn mudoutput_line<'gc>( }) .collect(); let line = locals.stash(&ctx, line.into_value(ctx)); + let triggers = locals.stash(&ctx, triggers); + let default_frame = locals.stash(&ctx, default_frame); async move { + call_checking_metatable::( + &mut seq, + triggers, + "try_run_sub", + &[line.clone(), default_frame], + ) + .await?; for frameroute in frameroutes { call_checking_metatable::( &mut seq, @@ -494,14 +510,124 @@ pub(super) fn new_mud<'gc>(ctx: Context<'gc>) -> Callback<'gc> { let curr_frame: Value = ctx .get_global::
("info")? .get(ctx, ctx.intern_static(b"current_frame"))?; + // And call new on the frameroute, for the current frame. let seq = async_sequence(&ctx, |locals, mut seq| { let frameroute = locals.stash(&ctx, frameroute); let curr_frame = locals.stash(&ctx, curr_frame); + let create_match_tab = locals.stash(&ctx, Function::Callback(create_match_table(ctx))); + let mud = locals.stash(&ctx, mud); async move { call_checking_metatable::(&mut seq, frameroute, "new", &[curr_frame]) .await?; + seq.call(&create_match_tab, 0).await?; + + seq.try_enter(|ctx, locals, _ex, mut stack| { + let matchtab: Value = stack.consume(ctx)?; + locals.fetch(&mud).set(ctx, "triggers", matchtab)?; + Ok(()) + })?; + + Ok(SequenceReturn::Return) + } + }); + + Ok(piccolo::CallbackReturn::Sequence(seq)) + }) +} + +pub(super) fn mud_trigger<'gc>(ctx: Context<'gc>) -> Callback<'gc> { + Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { + let info: Table = ctx.get_global("info")?; + let cur_frame_id: TermFrame = + try_unwrap_frame(ctx, &info.get(ctx, ctx.intern_static(b"current_frame"))?)?; + let frames: Table = ctx.get_global("frames")?; + let cur_frame: Table = frames.get(ctx, cur_frame_id.0 as i64)?; + let linked_mud: Value = cur_frame.get(ctx, "linked_mud")?; + + if linked_mud.is_nil() { + Err(anyhow::Error::msg( + "No MUD connection is linked to the current terminal. Triggers are per MUD and so can only be set after connecting.", + ))?; + } + let linked_mud: Table = Table::from_value(ctx, linked_mud)?; + + let triggers: UserData = linked_mud.get(ctx, "triggers")?; + + if stack.is_empty() { + return list_match_tab(cur_frame_id, triggers, ctx); + } + + let trigger_match: piccolo::String = piccolo::String::from_value( + ctx, + stack + .pop_front() + .ok_or_else(|| anyhow::Error::msg("Missing alias match"))?, + )?; + let sub_to: piccolo::String = piccolo::String::from_value( + ctx, + stack + .pop_front() + .ok_or_else(|| anyhow::Error::msg("Missing substitution match"))?, + )?; + if !stack.is_empty() { + Err(anyhow::Error::msg( + "Extra arguments to trigger command. Try wrapping the action in {}", + ))?; + } + + stack.push_back(triggers.into_value(ctx)); + stack.push_back(trigger_match.into_value(ctx)); + stack.push_back(sub_to.into_value(ctx)); + let seq = async_sequence(&ctx, |locals, mut seq| { + let add_func = locals.stash(&ctx, Function::Callback(match_table_add(ctx))); + async move { + seq.call(&add_func, 0).await?; + Ok(SequenceReturn::Return) + } + }); + + Ok(piccolo::CallbackReturn::Sequence(seq)) + }) +} + +pub(super) fn mud_untrigger(ctx: Context<'_>) -> Callback<'_> { + Callback::from_fn(&ctx, move |ctx, _ex, mut stack| { + let info: Table = ctx.get_global("info")?; + let cur_frame_id: TermFrame = + try_unwrap_frame(ctx, &info.get(ctx, ctx.intern_static(b"current_frame"))?)?; + let frames: Table = ctx.get_global("frames")?; + let cur_frame: Table = frames.get(ctx, cur_frame_id.0 as i64)?; + let linked_mud: Value = cur_frame.get(ctx, "linked_mud")?; + + if linked_mud.is_nil() { + Err(anyhow::Error::msg( + "No MUD connection is linked to the current terminal. Triggers are per MUD and so can only be set after connecting.", + ))?; + } + let linked_mud: Table = Table::from_value(ctx, linked_mud)?; + + let triggers: UserData = linked_mud.get(ctx, "triggers")?; + + let trigger_match: piccolo::String = piccolo::String::from_value( + ctx, + stack + .pop_front() + .ok_or_else(|| anyhow::Error::msg("Missing trigger match"))?, + )?; + if !stack.is_empty() { + Err(anyhow::Error::msg( + "Extra arguments to unact command. Giving the commands to trigger is not necessary.", + ))?; + } + + stack.push_back(triggers.into_value(ctx)); + stack.push_back(trigger_match.into_value(ctx)); + let seq = async_sequence(&ctx, |locals, mut seq| { + let remove_func = locals.stash(&ctx, Function::Callback(match_table_remove(ctx))); + async move { + seq.call(&remove_func, 0).await?; Ok(SequenceReturn::Return) } }); diff --git a/src/match_table.rs b/src/match_table.rs index 5f28dbe..d7a01c8 100644 --- a/src/match_table.rs +++ b/src/match_table.rs @@ -35,8 +35,9 @@ impl MatchSubTable { } pub fn try_sub(&self, input: &str) -> Option> { - for record in self.contents.iter() { - if let Some(matched) = record.match_regex.captures(input) { + let cleaned_input = strip_ansi_escapes::strip_str(input); + for record in self.contents.iter().rev() { + if let Some(matched) = record.match_regex.captures(&cleaned_input) { let vec = Some( record .sub_commands diff --git a/src/websocket.rs b/src/websocket.rs index 0672878..4ec44d5 100644 --- a/src/websocket.rs +++ b/src/websocket.rs @@ -66,7 +66,6 @@ pub fn connect_websocket( handle_websocket_output_log_err( &data_new_id, &data_globals, - &mut data_globals.lua_engine.borrow_mut(), &Uint8Array::new(&data).to_vec(), ); } else if data.has_type::() { @@ -99,11 +98,7 @@ pub fn connect_websocket( Some(closed_socket) => { closed_socket.closed = true; closed_socket.retained_closures = None; - handle_websocket_close_log_err( - &close_id, - &close_globals, - &mut close_globals.lua_engine.borrow_mut(), - ); + handle_websocket_close_log_err(&close_id, &close_globals); } }, );