178 lines
5.9 KiB
Rust
178 lines
5.9 KiB
Rust
use self::{frames::*, muds::*};
|
|
use anyhow::Error;
|
|
use piccolo::{
|
|
Callback, Closure, Context, Executor, ExternError, FromValue, Function, Lua, StashedExecutor,
|
|
Table, Value, Variadic,
|
|
};
|
|
use yew::UseStateSetter;
|
|
|
|
use crate::{id_intern::intern_id, GlobalLayoutCell, GlobalMemoCell, TermFrame};
|
|
use std::collections::VecDeque;
|
|
|
|
pub struct LuaState {
|
|
pub interp: Lua,
|
|
pub exec: StashedExecutor,
|
|
}
|
|
|
|
mod frames;
|
|
pub mod muds;
|
|
|
|
impl LuaState {
|
|
pub fn setup() -> Result<LuaState, String> {
|
|
let mut interp = Lua::core();
|
|
let exec: StashedExecutor =
|
|
interp.enter(|ctx| Ok::<StashedExecutor, String>(ctx.stash(Executor::new(ctx))))?;
|
|
|
|
Ok(LuaState { interp, exec })
|
|
}
|
|
|
|
fn try_set_current_frame(&mut self, frame: &TermFrame) -> Result<(), ExternError> {
|
|
self.interp.try_enter(|ctx| {
|
|
let info_table = Table::from_value(ctx, ctx.get_global("info")?)?;
|
|
info_table.set(
|
|
ctx,
|
|
ctx.intern_static(b"current_frame"),
|
|
intern_id::<TermFrame>(ctx, frame.clone()),
|
|
)?;
|
|
Ok(())
|
|
})
|
|
}
|
|
|
|
pub fn set_current_frame(&mut self, frame: &TermFrame) {
|
|
// We silently ignore errors here. Failure can happen if the Lua code does weird things
|
|
// like messes with the info global, so better to just ignore.
|
|
self.try_set_current_frame(frame).unwrap_or(())
|
|
}
|
|
|
|
pub fn execute(&mut self, command: &str) -> Result<(), String> {
|
|
self.interp
|
|
.try_enter(|ctx| {
|
|
let closure = Closure::load(ctx, None, format!("return ({})", command).as_bytes())
|
|
.or_else(|_| Closure::load(ctx, None, command.as_bytes()))?;
|
|
ctx.fetch(&self.exec).restart(ctx, closure.into(), ());
|
|
Ok(())
|
|
})
|
|
.map_err(|err| format!("{}", err))?;
|
|
self.interp
|
|
.execute::<()>(&self.exec)
|
|
.map_err(|err| format!("{}", err))
|
|
}
|
|
|
|
pub fn execute_command(
|
|
&mut self,
|
|
command: &str,
|
|
arguments: &VecDeque<&str>,
|
|
) -> Result<(), String> {
|
|
self.interp
|
|
.try_enter(|ctx| {
|
|
let commands = ctx.get_global::<Table>("commands")?;
|
|
let command_value: Value = commands.get(ctx, ctx.intern(command.as_bytes()))?;
|
|
if command_value.is_nil() {
|
|
Err(anyhow::Error::msg("Unknown command"))?;
|
|
}
|
|
let command_fn = Function::from_value(ctx, command_value)?;
|
|
ctx.fetch(&self.exec).restart(
|
|
ctx,
|
|
command_fn,
|
|
Variadic(
|
|
arguments
|
|
.iter()
|
|
.map(|s| ctx.intern(s.as_bytes()).into())
|
|
.collect::<Vec<Value>>(),
|
|
),
|
|
);
|
|
Ok(())
|
|
})
|
|
.map_err(|err| format!("{}", err))?;
|
|
self.interp
|
|
.execute::<()>(&self.exec)
|
|
.map_err(|err| format!("{}", err))
|
|
}
|
|
}
|
|
|
|
pub fn install_lua_globals(
|
|
global_memo: &GlobalMemoCell,
|
|
global_layout: UseStateSetter<GlobalLayoutCell>,
|
|
) -> Result<(), String> {
|
|
global_memo.lua_engine.borrow_mut().interp.try_enter(|ctx| {
|
|
let cmd_table = Table::new(&ctx);
|
|
macro_rules! register_command {
|
|
($sym: ident) => {
|
|
cmd_table
|
|
.set(
|
|
ctx,
|
|
ctx.intern_static(stringify!($sym).as_bytes()),
|
|
$sym(ctx, &global_memo, &global_layout),
|
|
)
|
|
.map_err(|_| Error::msg("Can't add command"))?;
|
|
};
|
|
}
|
|
|
|
register_command!(echo);
|
|
register_command!(echo_frame);
|
|
register_command!(echo_frame_raw);
|
|
register_command!(connect_mud);
|
|
register_command!(delete_mud);
|
|
register_command!(close_mud);
|
|
register_command!(sendmud_raw);
|
|
register_command!(hsplit);
|
|
register_command!(panel_merge);
|
|
register_command!(vsplit);
|
|
ctx.set_global("commands", cmd_table);
|
|
let info_table = Table::new(&ctx);
|
|
ctx.set_global("info", info_table);
|
|
let muds_table = Table::new(&ctx);
|
|
ctx.set_global("muds", muds_table);
|
|
|
|
let handlers_table = Table::new(&ctx);
|
|
ctx.set_global("handlers", handlers_table);
|
|
macro_rules! register_handler {
|
|
($sym: ident) => {
|
|
handlers_table
|
|
.set(
|
|
ctx,
|
|
ctx.intern_static(stringify!($sym).as_bytes()),
|
|
$sym(ctx, &global_memo),
|
|
)
|
|
.map_err(|_| Error::msg("Can't add handler"))?;
|
|
};
|
|
}
|
|
register_handler!(mudoutput);
|
|
register_handler!(mudoutput_line);
|
|
register_handler!(mudoutput_prompt);
|
|
register_handler!(mudoutput_will);
|
|
register_handler!(mudoutput_wont);
|
|
register_handler!(mudoutput_do);
|
|
register_handler!(mudoutput_dont);
|
|
register_handler!(mudoutput_subnegotiation);
|
|
|
|
macro_rules! register_nop_handler {
|
|
($sym: ident) => {
|
|
handlers_table
|
|
.set(
|
|
ctx,
|
|
ctx.intern_static(stringify!($sym).as_bytes()),
|
|
lua_nop(ctx),
|
|
)
|
|
.map_err(|_| Error::msg("Can't add handler"))?;
|
|
};
|
|
}
|
|
|
|
register_nop_handler!(mudoutput_break);
|
|
register_nop_handler!(mudoutput_sync);
|
|
register_nop_handler!(mudoutput_interrupt);
|
|
register_nop_handler!(mudoutput_abort_output);
|
|
register_nop_handler!(mudoutput_areyouthere);
|
|
|
|
Ok(())
|
|
});
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn lua_nop(ctx: Context<'_>) -> Callback<'_> {
|
|
Callback::from_fn(&ctx, |_ctx, _ex, _stack| {
|
|
Ok(piccolo::CallbackReturn::Return)
|
|
})
|
|
}
|