Implement mudinput (sending to MUD from terminal)
This commit is contained in:
parent
ff0411b040
commit
8816d76c53
@ -1,8 +1,7 @@
|
||||
use itertools::join;
|
||||
|
||||
use crate::{
|
||||
echo_to_term_frame, lua_engine::LuaState, parsing::parse_commands, GlobalMemoCell, TermFrame,
|
||||
};
|
||||
use itertools::{join, Itertools};
|
||||
|
||||
pub fn debrace(inp: &str) -> &str {
|
||||
let v = inp.trim();
|
||||
@ -52,6 +51,17 @@ fn reentrant_command_handler(
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// A normal command. Try dispatching it to the frame object in Lua...
|
||||
match lua_state
|
||||
.dispatch_normal_command(term_frame, &command.arguments.iter().join(" "))
|
||||
{
|
||||
Ok(()) => (),
|
||||
Err(msg) => {
|
||||
echo_to_term_frame(globals, term_frame, &format!("{}\r\n", msg))
|
||||
.unwrap_or(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,6 +90,30 @@ impl LuaState {
|
||||
.execute::<()>(&self.exec)
|
||||
.map_err(|err| format!("{}", err))
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_normal_command(
|
||||
&mut self,
|
||||
frame: &TermFrame,
|
||||
command: &str,
|
||||
) -> anyhow::Result<()> {
|
||||
self.interp.try_enter(|ctx| {
|
||||
let frames: Table = ctx.get_global("frames")?;
|
||||
let frame_tab: Value = frames.get(ctx, frame.0 as i64)?;
|
||||
if frame_tab.is_nil() {
|
||||
Err(anyhow::Error::msg(
|
||||
"Dispatching command to frame missing in Lua frames.",
|
||||
))?;
|
||||
}
|
||||
ctx.fetch(&self.exec).restart(
|
||||
ctx,
|
||||
Function::Callback(send_command_to_frame(ctx)),
|
||||
(frame_tab, ctx.intern(command.as_bytes())),
|
||||
);
|
||||
Ok(())
|
||||
})?;
|
||||
self.interp.execute(&self.exec)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn install_lua_globals(
|
||||
@ -129,6 +153,8 @@ pub fn install_lua_globals(
|
||||
ctx.set_global("info", info_table);
|
||||
let muds_table = Table::new(&ctx);
|
||||
ctx.set_global("muds", muds_table);
|
||||
let frames_table = Table::new(&ctx);
|
||||
ctx.set_global("frames", frames_table);
|
||||
|
||||
let handlers_table = Table::new(&ctx);
|
||||
ctx.set_global("handlers", handlers_table);
|
||||
@ -180,6 +206,7 @@ pub fn install_lua_globals(
|
||||
register_class_function!(mud_class_table, mudoutput_do);
|
||||
register_class_function!(mud_class_table, mudoutput_dont);
|
||||
register_class_function!(mud_class_table, mudoutput_subnegotiation);
|
||||
register_class_function!(mud_class_table, mudinput_line);
|
||||
register_class_function!(mud_class_table, "new", new_mud);
|
||||
|
||||
macro_rules! register_class_nop {
|
||||
@ -200,6 +227,12 @@ pub fn install_lua_globals(
|
||||
register_class_nop!(mud_class_table, mudoutput_abort_output);
|
||||
register_class_nop!(mud_class_table, mudoutput_areyouthere);
|
||||
|
||||
let frame_class_table = Table::new(&ctx);
|
||||
classes_table.set(ctx, "frame", frame_class_table)?;
|
||||
frame_class_table.set(ctx, MetaMethod::Index, frame_class_table)?;
|
||||
register_class_function!(frame_class_table, "new", new_frame);
|
||||
register_class_function!(frame_class_table, "input", frame_input);
|
||||
|
||||
let frameroute_class_table = Table::new(&ctx);
|
||||
classes_table.set(ctx, "frameroute", frameroute_class_table)?;
|
||||
frameroute_class_table.set(ctx, MetaMethod::Index, frameroute_class_table)?;
|
||||
@ -210,9 +243,26 @@ pub fn install_lua_globals(
|
||||
})
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
lua_global_initialisation(global_memo)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn lua_global_initialisation(global_memo: &GlobalMemoCell) -> Result<(), String> {
|
||||
let mut lua_engine = global_memo.lua_engine.borrow_mut();
|
||||
let lua_engine_ref: &mut LuaState = &mut lua_engine;
|
||||
lua_engine_ref.interp.enter(|ctx| {
|
||||
ctx.fetch(&lua_engine_ref.exec).restart(
|
||||
ctx,
|
||||
Function::Callback(ensure_frame_instance(ctx, &TermFrame(1))),
|
||||
(),
|
||||
);
|
||||
});
|
||||
lua_engine_ref
|
||||
.interp
|
||||
.execute(&lua_engine_ref.exec)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
pub fn lua_nop(ctx: Context<'_>) -> Callback<'_> {
|
||||
Callback::from_fn(&ctx, |_ctx, _ex, _stack| {
|
||||
Ok(piccolo::CallbackReturn::Return)
|
||||
|
@ -1,14 +1,17 @@
|
||||
use crate::{
|
||||
command_handler::debrace, echo_to_term_frame, GlobalLayoutCell, GlobalLayoutState,
|
||||
GlobalMemoCell, TermFrame,
|
||||
command_handler::debrace, echo_to_term_frame, id_intern::intern_id, GlobalLayoutCell,
|
||||
GlobalLayoutState, GlobalMemoCell, TermFrame,
|
||||
};
|
||||
use gc_arena::{Gc, Rootable};
|
||||
use piccolo::{
|
||||
self, Callback, Context, FromValue, Function, IntoValue, Table, UserData, Value, Variadic,
|
||||
self, async_sequence, meta_ops, Callback, CallbackReturn, Context, FromValue, Function,
|
||||
IntoValue, SequenceReturn, Table, UserData, Value, Variadic,
|
||||
};
|
||||
use std::{rc::Rc, str};
|
||||
use yew::UseStateSetter;
|
||||
|
||||
use super::prep_metaop_call;
|
||||
|
||||
pub fn echo_frame_raw<'gc, 'a>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &'a GlobalMemoCell,
|
||||
@ -84,19 +87,22 @@ pub fn vsplit<'gc>(
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let path: String = stack.from_front(ctx)?;
|
||||
let path: &str = debrace(&path);
|
||||
let frame: u64 = stack.from_front(ctx)?;
|
||||
let frame: TermFrame = TermFrame(stack.from_front(ctx)?);
|
||||
let new_splits = global_memo
|
||||
.layout
|
||||
.borrow()
|
||||
.term_splits
|
||||
.vsplit(path, TermFrame(frame))
|
||||
.vsplit(path, frame.clone())
|
||||
.map_err(|e| e.into_value(ctx))?;
|
||||
let new_layout = Rc::new(GlobalLayoutState {
|
||||
term_splits: new_splits.clone(),
|
||||
});
|
||||
global_layout.set(new_layout.clone());
|
||||
*(global_memo.layout.borrow_mut()) = new_layout;
|
||||
Ok(piccolo::CallbackReturn::Return)
|
||||
Ok(CallbackReturn::Call {
|
||||
function: Function::Callback(ensure_frame_instance(ctx, &frame)),
|
||||
then: None,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -110,19 +116,22 @@ pub fn hsplit<'gc>(
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let path: String = stack.from_front(ctx)?;
|
||||
let path: &str = debrace(&path);
|
||||
let frame: u64 = stack.from_front(ctx)?;
|
||||
let frame: TermFrame = TermFrame(stack.from_front(ctx)?);
|
||||
let new_splits = global_memo
|
||||
.layout
|
||||
.borrow()
|
||||
.term_splits
|
||||
.hsplit(path, TermFrame(frame))
|
||||
.hsplit(path, frame.clone())
|
||||
.map_err(|e| e.into_value(ctx))?;
|
||||
let new_layout = Rc::new(GlobalLayoutState {
|
||||
term_splits: new_splits.clone(),
|
||||
});
|
||||
global_layout.set(new_layout.clone());
|
||||
*(global_memo.layout.borrow_mut()) = new_layout;
|
||||
Ok(piccolo::CallbackReturn::Return)
|
||||
Ok(CallbackReturn::Call {
|
||||
function: Function::Callback(ensure_frame_instance(ctx, &frame)),
|
||||
then: None,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -163,3 +172,178 @@ pub fn try_unwrap_frame<'gc>(
|
||||
.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_frame_instance<'gc>(ctx: Context<'gc>, frame: &TermFrame) -> Callback<'gc> {
|
||||
let frame = frame.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, _stack| {
|
||||
let frames: Table = ctx.get_global("frames")?;
|
||||
let frame_value: Value = frames.get(ctx, frame.0 as i64)?;
|
||||
if !frame_value.is_nil() {
|
||||
return Ok(CallbackReturn::Return);
|
||||
}
|
||||
|
||||
let frame_tab = Table::new(&ctx);
|
||||
let classes: Table = ctx.get_global("classes")?;
|
||||
frame_tab.set_metatable(&ctx, Some(classes.get(ctx, "frame")?));
|
||||
frames.set(ctx, frame.0 as i64, frame_tab)?;
|
||||
|
||||
// Call frame_tab:new(frame) to setup.
|
||||
let frame = frame.clone();
|
||||
let seq = async_sequence(&ctx, move |locals, mut seq| {
|
||||
let frame_tab = locals.stash(&ctx, frame_tab);
|
||||
async move {
|
||||
let call = seq.try_enter(|ctx, locals, _execution, mut stack| {
|
||||
let frame_tab = locals.fetch(&frame_tab);
|
||||
stack.consume(ctx)?;
|
||||
Ok(prep_metaop_call(
|
||||
ctx,
|
||||
stack,
|
||||
locals,
|
||||
meta_ops::index(
|
||||
ctx,
|
||||
frame_tab.into_value(ctx),
|
||||
ctx.intern_static(b"new").into_value(ctx),
|
||||
)?,
|
||||
))
|
||||
})?;
|
||||
if let Some(call) = call {
|
||||
seq.call(&call, 0).await?;
|
||||
}
|
||||
let call = seq.try_enter(|ctx, locals, _, mut stack| {
|
||||
let value = stack
|
||||
.pop_back()
|
||||
.ok_or_else(|| anyhow::Error::msg("Index didn't return value"))?;
|
||||
stack.consume(ctx)?;
|
||||
stack.push_back(locals.fetch(&frame_tab).into_value(ctx));
|
||||
stack.push_back(intern_id::<TermFrame>(ctx, frame.clone()));
|
||||
Ok(locals.stash(&ctx, Function::from_value(ctx, value)?))
|
||||
})?;
|
||||
seq.call(&call, 0).await?;
|
||||
|
||||
Ok(SequenceReturn::Return)
|
||||
}
|
||||
});
|
||||
Ok(CallbackReturn::Sequence(seq))
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn new_frame<'gc>(ctx: Context<'gc>, _global_memo: &GlobalMemoCell) -> Callback<'gc> {
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let frame_tab: Table = Table::from_value(
|
||||
ctx,
|
||||
stack
|
||||
.pop_front()
|
||||
.ok_or_else(|| anyhow::Error::msg("classes.frame:new missing object!"))?,
|
||||
)?;
|
||||
let frame: Value = stack
|
||||
.pop_front()
|
||||
.ok_or_else(|| anyhow::Error::msg("classes.frame:new missing frame!"))?;
|
||||
|
||||
frame_tab.set(ctx, ctx.intern_static(b"frame"), frame)?;
|
||||
|
||||
Ok(piccolo::CallbackReturn::Return)
|
||||
})
|
||||
}
|
||||
|
||||
// Note: send_command_to_frame is called from the command parser, and can't be overriden (or even
|
||||
// called) by the user.
|
||||
// It will normally result in a call into frame_input, but that can be overriden by scripts.
|
||||
pub(super) fn send_command_to_frame<'gc>(ctx: Context<'gc>) -> Callback<'gc> {
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let (frame, line): (Table, piccolo::String) = stack.consume(ctx)?;
|
||||
let seq = async_sequence(&ctx, |locals, mut seq| {
|
||||
let frame = locals.stash(&ctx, frame);
|
||||
let line = locals.stash(&ctx, line);
|
||||
async move {
|
||||
let call = seq.try_enter(|ctx, locals, _execution, stack| {
|
||||
let frame = locals.fetch(&frame);
|
||||
Ok(prep_metaop_call(
|
||||
ctx,
|
||||
stack,
|
||||
locals,
|
||||
meta_ops::index(
|
||||
ctx,
|
||||
frame.into_value(ctx),
|
||||
ctx.intern_static(b"input").into_value(ctx),
|
||||
)?,
|
||||
))
|
||||
})?;
|
||||
if let Some(call) = call {
|
||||
seq.call(&call, 0).await?;
|
||||
}
|
||||
let call = seq.try_enter(|ctx, locals, _, mut stack| {
|
||||
let value = stack
|
||||
.pop_back()
|
||||
.ok_or_else(|| anyhow::Error::msg("Index didn't return value"))?;
|
||||
stack.consume(ctx)?;
|
||||
stack.push_back(locals.fetch(&frame).into_value(ctx));
|
||||
stack.push_back(locals.fetch(&line).into_value(ctx));
|
||||
Ok(locals.stash(&ctx, Function::from_value(ctx, value)?))
|
||||
})?;
|
||||
seq.call(&call, 0).await?;
|
||||
|
||||
Ok(SequenceReturn::Return)
|
||||
}
|
||||
});
|
||||
Ok(CallbackReturn::Sequence(seq))
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn frame_input<'gc>(ctx: Context<'gc>, _global_memo: &GlobalMemoCell) -> Callback<'gc> {
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let frame_tab: Table = Table::from_value(
|
||||
ctx,
|
||||
stack
|
||||
.pop_front()
|
||||
.ok_or_else(|| anyhow::Error::msg("classes.frame:new missing object!"))?,
|
||||
)?;
|
||||
let line: Value = stack
|
||||
.pop_front()
|
||||
.ok_or_else(|| anyhow::Error::msg("classes.frame:new missing line!"))?;
|
||||
|
||||
let linked_mud = frame_tab.get_value(ctx, ctx.intern_static(b"linked_mud"));
|
||||
if linked_mud.is_nil() {
|
||||
return Ok(piccolo::CallbackReturn::Return);
|
||||
}
|
||||
let linked_mud: Table = Table::from_value(ctx, linked_mud)?;
|
||||
|
||||
// linked_mud:mudinput_line(line)
|
||||
let seq = async_sequence(&ctx, |locals, mut seq| {
|
||||
let linked_mud = locals.stash(&ctx, linked_mud);
|
||||
let line = locals.stash(&ctx, line);
|
||||
async move {
|
||||
let call = seq.try_enter(|ctx, locals, _execution, mut stack| {
|
||||
let linked_mud = locals.fetch(&linked_mud);
|
||||
stack.consume(ctx)?;
|
||||
Ok(prep_metaop_call(
|
||||
ctx,
|
||||
stack,
|
||||
locals,
|
||||
meta_ops::index(
|
||||
ctx,
|
||||
linked_mud.into_value(ctx),
|
||||
ctx.intern_static(b"mudinput_line").into_value(ctx),
|
||||
)?,
|
||||
))
|
||||
})?;
|
||||
if let Some(call) = call {
|
||||
seq.call(&call, 0).await?;
|
||||
}
|
||||
let call = seq.try_enter(|ctx, locals, _, mut stack| {
|
||||
let value = stack
|
||||
.pop_back()
|
||||
.ok_or_else(|| anyhow::Error::msg("Index didn't return value"))?;
|
||||
stack.consume(ctx)?;
|
||||
stack.push_back(locals.fetch(&linked_mud).into_value(ctx));
|
||||
stack.push_back(locals.fetch(&line));
|
||||
Ok(locals.stash(&ctx, Function::from_value(ctx, value)?))
|
||||
})?;
|
||||
seq.call(&call, 0).await?;
|
||||
|
||||
Ok(SequenceReturn::Return)
|
||||
}
|
||||
});
|
||||
|
||||
Ok(piccolo::CallbackReturn::Sequence(seq))
|
||||
})
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use anyhow::Error;
|
||||
use gc_arena::{Gc, Rootable};
|
||||
use piccolo::{
|
||||
self, async_sequence, meta_ops, Callback, CallbackReturn, Context, FromValue, Function,
|
||||
IntoValue, MetaMethod, SequenceReturn, StashedValue, Table, UserData, Value,
|
||||
IntoValue, SequenceReturn, StashedValue, Table, UserData, Value,
|
||||
};
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::console;
|
||||
@ -16,7 +16,7 @@ use crate::{
|
||||
GlobalLayoutCell, GlobalMemoCell,
|
||||
};
|
||||
|
||||
use super::{prep_metaop_call, LuaState};
|
||||
use super::{prep_metaop_call, try_unwrap_frame, LuaState};
|
||||
|
||||
fn try_unwrap_socketid<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
@ -171,10 +171,25 @@ pub(super) fn connect_mud<'gc>(
|
||||
muds.set(ctx, new_socket, conntab)?;
|
||||
conntab.set(ctx, ctx.intern_static(b"socket"), new_socket)?;
|
||||
conntab.set(ctx, ctx.intern_static(b"buffer"), ctx.intern_static(b""))?;
|
||||
let conntab_meta = Table::new(&ctx);
|
||||
conntab_meta.set(ctx, MetaMethod::Index, mud_class)?;
|
||||
conntab.set_metatable(&ctx, Some(conntab_meta));
|
||||
|
||||
conntab.set_metatable(&ctx, Some(mud_class));
|
||||
let curframe: Value = ctx
|
||||
.get_global::<Table>("info")?
|
||||
.get(ctx, ctx.intern_static(b"current_frame"))?;
|
||||
if let Ok(curframe) = try_unwrap_frame(ctx, &curframe) {
|
||||
if let Ok(frame) = ctx
|
||||
.get_global::<Table>("frames")?
|
||||
.get::<i64, Value>(ctx, curframe.0 as i64)
|
||||
{
|
||||
if !frame.is_nil() {
|
||||
Table::from_value(ctx, frame)?.set(
|
||||
ctx,
|
||||
ctx.intern_static(b"linked_mud"),
|
||||
conntab,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Call conntab:new...
|
||||
let seq = async_sequence(&ctx, |locals, mut seq| {
|
||||
let conntab = locals.stash(&ctx, conntab);
|
||||
async move {
|
||||
@ -483,6 +498,27 @@ pub(super) fn mudoutput_subnegotiation<'gc>(
|
||||
Callback::from_fn(&ctx, move |_ctx, _ex, _stack| Ok(CallbackReturn::Return))
|
||||
}
|
||||
|
||||
pub(super) fn mudinput_line<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
_global_memo: &GlobalMemoCell,
|
||||
) -> Callback<'gc> {
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let (mud, line): (Table, piccolo::String) = stack.consume(ctx)?;
|
||||
stack.push_back(mud.get::<piccolo::String, Value>(ctx, ctx.intern_static(b"socket"))?);
|
||||
let line = [line.as_bytes(), b"\r\n"].concat();
|
||||
stack.push_back(ctx.intern(&line).into_value(ctx));
|
||||
|
||||
let func = ctx
|
||||
.get_global::<Table>("commands")?
|
||||
.get::<piccolo::String, Function>(ctx, ctx.intern_static(b"sendmud_raw"))?;
|
||||
|
||||
Ok(CallbackReturn::Call {
|
||||
function: func,
|
||||
then: None,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn new_mud<'gc>(ctx: Context<'gc>, _global_memo: &GlobalMemoCell) -> Callback<'gc> {
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let mud: Table = Table::from_value(
|
||||
|
@ -70,7 +70,7 @@ fn app() -> Html {
|
||||
});
|
||||
use_memo((), |_| {
|
||||
install_lua_globals(&global_memo, global_layout.setter())
|
||||
.expect("Couldn't install Lua globals")
|
||||
.expect("Couldn't install Lua globals");
|
||||
});
|
||||
|
||||
html! {
|
||||
|
Loading…
Reference in New Issue
Block a user