Refactor ready for MUD-specific routing
This commit is contained in:
parent
466566a6c5
commit
243132eeec
@ -1,7 +1,7 @@
|
||||
use itertools::join;
|
||||
|
||||
use crate::{
|
||||
echo_to_term_frame, lua_state::LuaState, parsing::parse_commands, GlobalMemoCell, TermFrame,
|
||||
echo_to_term_frame, lua_engine::LuaState, parsing::parse_commands, GlobalMemoCell, TermFrame,
|
||||
};
|
||||
|
||||
pub fn debrace(inp: &str) -> &str {
|
||||
|
@ -1,11 +1,11 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::{collections::BTreeMap, fmt::Debug};
|
||||
|
||||
use gc_arena::{lock::GcRefLock, Collect, Gc, Rootable};
|
||||
use piccolo::{Context, IntoValue, Singleton, UserData, Value};
|
||||
|
||||
#[derive(Collect)]
|
||||
#[collect(no_drop)]
|
||||
struct InternMapSingleton<'gc, A>(GcRefLock<'gc, BTreeMap<A, Gc<'gc, A>>>);
|
||||
struct InternMapSingleton<'gc, A>(GcRefLock<'gc, BTreeMap<A, Value<'gc>>>);
|
||||
|
||||
impl<'gc, A> Singleton<'gc> for InternMapSingleton<'gc, A>
|
||||
where
|
||||
@ -18,17 +18,22 @@ where
|
||||
|
||||
pub fn intern_id<'gc, A>(ctx: Context<'gc>, input: A) -> Value<'gc>
|
||||
where
|
||||
A: Collect + Ord + Clone + 'static,
|
||||
A: Collect + Ord + Clone + Debug + 'static,
|
||||
{
|
||||
let intern_map: &'gc InternMapSingleton<'gc, A> =
|
||||
ctx.singleton::<Rootable!['gcb => InternMapSingleton<'gcb, A>]>();
|
||||
let gc_id = intern_map
|
||||
intern_map
|
||||
.0
|
||||
.borrow_mut(&ctx)
|
||||
.entry(input.clone())
|
||||
.or_insert_with(|| Gc::new(&ctx, input.clone()))
|
||||
.clone();
|
||||
UserData::<'gc>::new::<Rootable!['gcb => Gc<'gcb, A>]>(&ctx, gc_id).into_value(ctx)
|
||||
.or_insert_with(|| {
|
||||
UserData::<'gc>::new::<Rootable!['gcb => Gc<'gcb, A>]>(
|
||||
&ctx,
|
||||
Gc::new(&ctx, input.clone()),
|
||||
)
|
||||
.into_value(ctx)
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
pub fn unintern_id<'gc, A>(ctx: Context<'gc>, input: &A)
|
||||
|
164
src/lua_engine.rs
Normal file
164
src/lua_engine.rs
Normal file
@ -0,0 +1,164 @@
|
||||
use self::{frames::*, muds::*};
|
||||
use anyhow::Error;
|
||||
use piccolo::{
|
||||
Closure, Executor, FromValue, Function, IntoValue, Lua, StashedExecutor, StaticError, 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<(), StaticError> {
|
||||
self.interp.try_enter(|ctx| {
|
||||
let info_table = Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"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 =
|
||||
Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"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(ctx.intern_static(b"commands").into_value(ctx), cmd_table)
|
||||
.map(|_| ())
|
||||
.map_err(|_| Error::msg("Can't set commands key"))?;
|
||||
|
||||
let info_table = Table::new(&ctx);
|
||||
ctx.set_global(ctx.intern_static(b"info").into_value(ctx), info_table)
|
||||
.map(|_| ())
|
||||
.map_err(|_| Error::msg("Can't set info key"))?;
|
||||
let muds_table = Table::new(&ctx);
|
||||
ctx.set_global(ctx.intern_static(b"muds").into_value(ctx), muds_table)
|
||||
.map(|_| ())
|
||||
.map_err(|_| Error::msg("Can't set muds key"))?;
|
||||
|
||||
let handlers_table = Table::new(&ctx);
|
||||
ctx.set_global(
|
||||
ctx.intern_static(b"handlers").into_value(ctx),
|
||||
handlers_table,
|
||||
)
|
||||
.map(|_| ())
|
||||
.map_err(|_| Error::msg("Can't set handlers key"))?;
|
||||
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);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(())
|
||||
}
|
160
src/lua_engine/frames.rs
Normal file
160
src/lua_engine/frames.rs
Normal file
@ -0,0 +1,160 @@
|
||||
use crate::{
|
||||
command_handler::debrace, echo_to_term_frame, GlobalLayoutCell, GlobalLayoutState,
|
||||
GlobalMemoCell, TermFrame,
|
||||
};
|
||||
use gc_arena::{Gc, Rootable};
|
||||
use piccolo::{
|
||||
self, Callback, Context, FromValue, Function, IntoValue, Table, UserData, Value, Variadic,
|
||||
};
|
||||
use std::{rc::Rc, str};
|
||||
use yew::UseStateSetter;
|
||||
|
||||
pub fn echo_frame_raw<'gc, 'a>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &'a GlobalMemoCell,
|
||||
_global_layout: &'a UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let frame: TermFrame = try_unwrap_frame(ctx, &stack.pop_front())?;
|
||||
let message: piccolo::String = stack.from_front(ctx)?;
|
||||
let message_str = str::from_utf8(message.as_bytes())
|
||||
.map_err(|_| "Expected message to echo to be UTF-8.".into_value(ctx))?;
|
||||
echo_to_term_frame(&global_memo, &frame, message_str).map_err(|m| m.into_value(ctx))?;
|
||||
Ok(piccolo::CallbackReturn::Return)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn echo_frame<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
_global_memo: &GlobalMemoCell,
|
||||
_global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let commands: Table<'gc> =
|
||||
Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"commands")))?;
|
||||
let function = Function::from_value(ctx, commands.get(ctx, "echo_frame_raw"))?;
|
||||
let frame_no: Value = stack.pop_front();
|
||||
let all_parts: Vec<String> = stack
|
||||
.consume::<Variadic<Vec<Value>>>(ctx)?
|
||||
.into_iter()
|
||||
.map(|v| format!("{}", v))
|
||||
.collect();
|
||||
stack.push_front(frame_no);
|
||||
let message = ctx.intern((all_parts.join(" ") + "\r\n").as_bytes());
|
||||
stack.push_back(message.into());
|
||||
Ok(piccolo::CallbackReturn::Call {
|
||||
function,
|
||||
then: None,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn echo<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
_global_memo: &GlobalMemoCell,
|
||||
_global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let commands: Table<'gc> =
|
||||
Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"commands")))?;
|
||||
let function = Function::from_value(ctx, commands.get(ctx, "echo_frame"))?;
|
||||
let info: Table<'gc> = Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"info")))?;
|
||||
let cur_frame = info.get(ctx, ctx.intern_static(b"current_frame"));
|
||||
stack.push_front(cur_frame);
|
||||
Ok(piccolo::CallbackReturn::Call {
|
||||
function,
|
||||
then: None,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn vsplit<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &GlobalMemoCell,
|
||||
global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
let global_layout = global_layout.clone();
|
||||
let global_memo = global_memo.clone();
|
||||
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 new_splits = global_memo
|
||||
.layout
|
||||
.borrow()
|
||||
.term_splits
|
||||
.vsplit(path, TermFrame(frame))
|
||||
.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)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn hsplit<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &GlobalMemoCell,
|
||||
global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
let global_layout = global_layout.clone();
|
||||
let global_memo = global_memo.clone();
|
||||
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 new_splits = global_memo
|
||||
.layout
|
||||
.borrow()
|
||||
.term_splits
|
||||
.hsplit(path, TermFrame(frame))
|
||||
.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)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn panel_merge<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &GlobalMemoCell,
|
||||
global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
let global_layout = global_layout.clone();
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let path: String = stack.from_front(ctx)?;
|
||||
let path: &str = debrace(&path);
|
||||
let new_splits = global_memo
|
||||
.layout
|
||||
.borrow()
|
||||
.term_splits
|
||||
.join(path)
|
||||
.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)
|
||||
})
|
||||
}
|
||||
|
||||
fn try_unwrap_frame<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
value: &Value<'gc>,
|
||||
) -> Result<TermFrame, piccolo::Error<'gc>> {
|
||||
match u64::from_value(ctx, *value) {
|
||||
Ok(v) => Ok(TermFrame(v)),
|
||||
Err(_) => Ok(UserData::from_value(ctx, *value)?
|
||||
.downcast::<Rootable!['gcb => Gc<'gcb, TermFrame>]>()?
|
||||
.as_ref()
|
||||
.clone()),
|
||||
}
|
||||
}
|
258
src/lua_engine/muds.rs
Normal file
258
src/lua_engine/muds.rs
Normal file
@ -0,0 +1,258 @@
|
||||
use anyhow::Error;
|
||||
use gc_arena::{Gc, Rootable};
|
||||
use piccolo::{self, Callback, Context, FromValue, Function, Table, UserData, Value};
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::console;
|
||||
use yew::UseStateSetter;
|
||||
|
||||
use crate::{
|
||||
command_handler::debrace,
|
||||
id_intern::{intern_id, unintern_id},
|
||||
websocket::{connect_websocket, send_message_to_mud, WebSocketId},
|
||||
GlobalLayoutCell, GlobalMemoCell,
|
||||
};
|
||||
|
||||
use super::LuaState;
|
||||
|
||||
fn try_unwrap_socketid<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
value: &Value<'gc>,
|
||||
) -> Result<WebSocketId, piccolo::Error<'gc>> {
|
||||
let value = if let Ok(sockname) = String::from_value(ctx, *value) {
|
||||
let ret = Table::from_value(ctx, ctx.get_global("muds"))?.get(ctx, sockname);
|
||||
if ret.is_nil() {
|
||||
Err(Error::msg(
|
||||
"Could not find a MUD connection with that name.",
|
||||
))?;
|
||||
}
|
||||
ret
|
||||
} else {
|
||||
*value
|
||||
};
|
||||
Ok(UserData::from_value(ctx, value)?
|
||||
.downcast::<Rootable!['gcb => Gc<'gcb, WebSocketId>]>()?
|
||||
.as_ref()
|
||||
.clone())
|
||||
}
|
||||
|
||||
pub fn handle_websocket_output(
|
||||
socket: &WebSocketId,
|
||||
engine: &mut LuaState,
|
||||
input: &[u8],
|
||||
) -> Result<(), String> {
|
||||
engine
|
||||
.interp
|
||||
.try_enter(|ctx| {
|
||||
let handlers = Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"handlers")))?;
|
||||
let input_fn =
|
||||
Function::from_value(ctx, handlers.get(ctx, ctx.intern_static(b"mudoutput")))?;
|
||||
ctx.fetch(&engine.exec).restart(
|
||||
ctx,
|
||||
input_fn,
|
||||
(
|
||||
intern_id::<WebSocketId>(ctx, socket.clone()),
|
||||
ctx.intern(input),
|
||||
),
|
||||
);
|
||||
Ok(())
|
||||
})
|
||||
.map_err(|err| format!("{}", err))?;
|
||||
engine
|
||||
.interp
|
||||
.execute::<()>(&engine.exec)
|
||||
.map_err(|err| format!("{}", err))
|
||||
}
|
||||
|
||||
pub fn handle_websocket_output_log_err(socket: &WebSocketId, engine: &mut LuaState, input: &[u8]) {
|
||||
match handle_websocket_output(socket, engine, input) {
|
||||
Ok(()) => {}
|
||||
Err(e) => console::log_2(
|
||||
&JsValue::from_str("An error occurred calling the WebSocket input handler"),
|
||||
&JsValue::from_str(&e),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_websocket_close(socket: &WebSocketId, engine: &mut LuaState) -> Result<(), String> {
|
||||
engine
|
||||
.interp
|
||||
.try_enter(|ctx| {
|
||||
let handlers = Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"handlers")))?;
|
||||
let input_fn =
|
||||
Function::from_value(ctx, handlers.get(ctx, ctx.intern_static(b"mudclose")))?;
|
||||
ctx.fetch(&engine.exec).restart(
|
||||
ctx,
|
||||
input_fn,
|
||||
intern_id::<WebSocketId>(ctx, socket.clone()),
|
||||
);
|
||||
Ok(())
|
||||
})
|
||||
.map_err(|err| format!("{}", err))?;
|
||||
engine
|
||||
.interp
|
||||
.execute::<()>(&engine.exec)
|
||||
.map_err(|err| format!("{}", err))
|
||||
}
|
||||
|
||||
pub fn handle_websocket_close_log_err(socket: &WebSocketId, engine: &mut LuaState) {
|
||||
match handle_websocket_close(socket, engine) {
|
||||
Ok(()) => {}
|
||||
Err(e) => console::log_2(
|
||||
&JsValue::from_str("An error occurred calling the WebSocket input handler"),
|
||||
&JsValue::from_str(&e),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn sendmud_raw<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &GlobalMemoCell,
|
||||
_global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let mud: WebSocketId = try_unwrap_socketid(ctx, &stack.pop_front())?;
|
||||
let msg: piccolo::String = stack.from_front(ctx)?;
|
||||
send_message_to_mud(&mud, msg.as_bytes(), &global_memo)?;
|
||||
Ok(piccolo::CallbackReturn::Return)
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn connect_mud<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &GlobalMemoCell,
|
||||
_global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let mut trusted: bool = false;
|
||||
let name: String = loop {
|
||||
let v: String = stack.from_front(ctx)?;
|
||||
if v == "-trust" {
|
||||
trusted = true;
|
||||
continue;
|
||||
}
|
||||
break v;
|
||||
};
|
||||
let name: Value<'gc> = ctx.intern(debrace(&name).as_bytes()).into();
|
||||
|
||||
let muds = Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"muds")))?;
|
||||
if !muds.get_value(name).is_nil() {
|
||||
Err(Error::msg(
|
||||
"Attempt to create MUD connection using name that's already taken",
|
||||
))?
|
||||
}
|
||||
|
||||
let url: String = stack.from_front(ctx)?;
|
||||
let new_socket = intern_id(
|
||||
ctx,
|
||||
connect_websocket(
|
||||
trusted,
|
||||
&url,
|
||||
&mut global_memo.ws_registry.borrow_mut(),
|
||||
&global_memo,
|
||||
)?,
|
||||
);
|
||||
|
||||
muds.set(ctx, name, new_socket)?;
|
||||
|
||||
let conntab = Table::new(&ctx);
|
||||
muds.set(ctx, new_socket, conntab)?;
|
||||
|
||||
Ok(piccolo::CallbackReturn::Return)
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn close_mud<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &GlobalMemoCell,
|
||||
_global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let mud_value: Value<'gc> = stack.pop_front();
|
||||
let socket_id = try_unwrap_socketid(ctx, &mud_value)?;
|
||||
match global_memo.ws_registry.borrow_mut().get_mut(&socket_id) {
|
||||
None => Err(Error::msg("That MUD connection doesn't exist."))?,
|
||||
Some(v) => {
|
||||
if v.closed {
|
||||
Err(Error::msg("That MUD connection was already closed."))?
|
||||
}
|
||||
v.connection.close().unwrap_or(());
|
||||
v.retained_closures = None;
|
||||
v.closed = true;
|
||||
}
|
||||
}
|
||||
Ok(piccolo::CallbackReturn::Return)
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn delete_mud<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &GlobalMemoCell,
|
||||
_global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let name: String = stack.from_front(ctx)?;
|
||||
let name: Value<'gc> = ctx.intern(debrace(&name).as_bytes()).into();
|
||||
|
||||
let muds = Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"muds")))?;
|
||||
let mud_value = muds.get_value(name);
|
||||
if mud_value.is_nil() {
|
||||
Err(Error::msg(
|
||||
"Attempt to delete MUD connection that wasn't found",
|
||||
))?
|
||||
}
|
||||
|
||||
let socket_id = try_unwrap_socketid(ctx, &mud_value)?;
|
||||
|
||||
// Delete the MUD data if possible.
|
||||
let _ = muds.set_value(&ctx, mud_value, Value::Nil);
|
||||
for (k, v) in muds.iter() {
|
||||
match UserData::from_value(ctx, v) {
|
||||
Err(_) => continue,
|
||||
Ok(ud) => match ud.downcast::<Rootable!['gcb => Gc<'gcb, WebSocketId>]>() {
|
||||
Err(_) => continue,
|
||||
Ok(v) if v.as_ref() != &socket_id => continue,
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
let _ = muds.set_value(&ctx, k, Value::Nil);
|
||||
}
|
||||
unintern_id(ctx, &socket_id);
|
||||
match global_memo.ws_registry.borrow_mut().remove(&socket_id) {
|
||||
None => {}
|
||||
Some(sockdat) if sockdat.closed => {}
|
||||
Some(sockdat) => sockdat.connection.close().unwrap_or(()),
|
||||
}
|
||||
|
||||
Ok(piccolo::CallbackReturn::Return)
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn mudoutput<'gc>(ctx: Context<'gc>, global_memo: &GlobalMemoCell) -> Callback<'gc> {
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
// Temporary hack for testing... alias to echo
|
||||
let echo = Function::from_value(
|
||||
ctx,
|
||||
Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"commands")))?
|
||||
.get(ctx, ctx.intern_static(b"echo")),
|
||||
)?;
|
||||
|
||||
let mud: Value<'gc> = stack.pop_front();
|
||||
let output: piccolo::String<'gc> = stack.from_front(ctx)?;
|
||||
|
||||
let conntab =
|
||||
Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"muds")))?.get_value(mud);
|
||||
if conntab.is_nil() {
|
||||
Err(Error::msg("Received input from MUD not in muds table."))?
|
||||
}
|
||||
|
||||
Ok(piccolo::CallbackReturn::Call {
|
||||
function: echo,
|
||||
then: None,
|
||||
})
|
||||
})
|
||||
}
|
425
src/lua_state.rs
425
src/lua_state.rs
@ -1,425 +0,0 @@
|
||||
use anyhow::Error;
|
||||
use gc_arena::{Gc, Rootable};
|
||||
use piccolo::{
|
||||
self, Callback, Closure, Context, Executor, FromValue, Function, IntoValue, Lua,
|
||||
StashedExecutor, StaticError, Table, UserData, Value, Variadic,
|
||||
};
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::console;
|
||||
use yew::UseStateSetter;
|
||||
|
||||
use crate::{
|
||||
command_handler::debrace,
|
||||
echo_to_term_frame,
|
||||
id_intern::intern_id,
|
||||
websocket::{connect_websocket, send_message_to_mud, WebSocketId},
|
||||
GlobalLayoutCell, GlobalLayoutState, GlobalMemoCell, TermFrame,
|
||||
};
|
||||
use std::{collections::VecDeque, rc::Rc, str};
|
||||
|
||||
pub struct LuaState {
|
||||
pub interp: Lua,
|
||||
pub exec: StashedExecutor,
|
||||
}
|
||||
|
||||
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<(), StaticError> {
|
||||
self.interp.try_enter(|ctx| {
|
||||
let info_table = Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"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 =
|
||||
Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"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!(sendmud_raw);
|
||||
register_command!(hsplit);
|
||||
register_command!(panel_merge);
|
||||
register_command!(vsplit);
|
||||
ctx.set_global(ctx.intern_static(b"commands").into_value(ctx), cmd_table)
|
||||
.map(|_| ())
|
||||
.map_err(|_| Error::msg("Can't set commands key"))?;
|
||||
|
||||
let info_table = Table::new(&ctx);
|
||||
ctx.set_global(ctx.intern_static(b"info").into_value(ctx), info_table)
|
||||
.map(|_| ())
|
||||
.map_err(|_| Error::msg("Can't set info key"))?;
|
||||
let muds_table = Table::new(&ctx);
|
||||
ctx.set_global(ctx.intern_static(b"muds").into_value(ctx), muds_table)
|
||||
.map(|_| ())
|
||||
.map_err(|_| Error::msg("Can't set muds key"))?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn echo_frame_raw<'gc, 'a>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &'a GlobalMemoCell,
|
||||
_global_layout: &'a UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let frame: TermFrame = try_unwrap_frame(ctx, &stack.pop_front())?;
|
||||
let message: piccolo::String = stack.from_front(ctx)?;
|
||||
let message_str = str::from_utf8(message.as_bytes())
|
||||
.map_err(|_| "Expected message to echo to be UTF-8.".into_value(ctx))?;
|
||||
echo_to_term_frame(&global_memo, &frame, message_str).map_err(|m| m.into_value(ctx))?;
|
||||
Ok(piccolo::CallbackReturn::Return)
|
||||
})
|
||||
}
|
||||
|
||||
fn echo_frame<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
_global_memo: &GlobalMemoCell,
|
||||
_global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let commands: Table<'gc> =
|
||||
Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"commands")))?;
|
||||
let function = Function::from_value(ctx, commands.get(ctx, "echo_frame_raw"))?;
|
||||
let frame_no: Value = stack.pop_front();
|
||||
let all_parts: Vec<String> = stack
|
||||
.consume::<Variadic<Vec<Value>>>(ctx)?
|
||||
.into_iter()
|
||||
.map(|v| format!("{}", v))
|
||||
.collect();
|
||||
stack.push_front(frame_no);
|
||||
let message = ctx.intern((all_parts.join(" ") + "\r\n").as_bytes());
|
||||
stack.push_back(message.into());
|
||||
Ok(piccolo::CallbackReturn::Call {
|
||||
function,
|
||||
then: None,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn echo<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
_global_memo: &GlobalMemoCell,
|
||||
_global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let commands: Table<'gc> =
|
||||
Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"commands")))?;
|
||||
let function = Function::from_value(ctx, commands.get(ctx, "echo_frame"))?;
|
||||
let info: Table<'gc> = Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"info")))?;
|
||||
let cur_frame = info.get(ctx, ctx.intern_static(b"current_frame"));
|
||||
stack.push_front(cur_frame);
|
||||
Ok(piccolo::CallbackReturn::Call {
|
||||
function,
|
||||
then: None,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn vsplit<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &GlobalMemoCell,
|
||||
global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
let global_layout = global_layout.clone();
|
||||
let global_memo = global_memo.clone();
|
||||
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 new_splits = global_memo
|
||||
.layout
|
||||
.borrow()
|
||||
.term_splits
|
||||
.vsplit(path, TermFrame(frame))
|
||||
.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)
|
||||
})
|
||||
}
|
||||
|
||||
fn hsplit<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &GlobalMemoCell,
|
||||
global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
let global_layout = global_layout.clone();
|
||||
let global_memo = global_memo.clone();
|
||||
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 new_splits = global_memo
|
||||
.layout
|
||||
.borrow()
|
||||
.term_splits
|
||||
.hsplit(path, TermFrame(frame))
|
||||
.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)
|
||||
})
|
||||
}
|
||||
|
||||
fn panel_merge<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &GlobalMemoCell,
|
||||
global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
let global_layout = global_layout.clone();
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let path: String = stack.from_front(ctx)?;
|
||||
let path: &str = debrace(&path);
|
||||
let new_splits = global_memo
|
||||
.layout
|
||||
.borrow()
|
||||
.term_splits
|
||||
.join(path)
|
||||
.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)
|
||||
})
|
||||
}
|
||||
|
||||
fn try_unwrap_frame<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
value: &Value<'gc>,
|
||||
) -> Result<TermFrame, piccolo::Error<'gc>> {
|
||||
match u64::from_value(ctx, *value) {
|
||||
Ok(v) => Ok(TermFrame(v)),
|
||||
Err(_) => Ok(UserData::from_value(ctx, *value)?
|
||||
.downcast::<Rootable!['gcb => Gc<'gcb, TermFrame>]>()?
|
||||
.as_ref()
|
||||
.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
fn try_unwrap_socketid<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
value: &Value<'gc>,
|
||||
) -> Result<WebSocketId, piccolo::Error<'gc>> {
|
||||
Ok(UserData::from_value(ctx, *value)?
|
||||
.downcast::<Rootable!['gcb => Gc<'gcb, WebSocketId>]>()?
|
||||
.as_ref()
|
||||
.clone())
|
||||
}
|
||||
|
||||
pub fn handle_websocket_output(
|
||||
socket: &WebSocketId,
|
||||
engine: &mut LuaState,
|
||||
input: &[u8],
|
||||
) -> Result<(), String> {
|
||||
engine
|
||||
.interp
|
||||
.try_enter(|ctx| {
|
||||
let handlers = Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"handlers")))?;
|
||||
let input_fn =
|
||||
Function::from_value(ctx, handlers.get(ctx, ctx.intern_static(b"mudoutput")))?;
|
||||
ctx.fetch(&engine.exec).restart(
|
||||
ctx,
|
||||
input_fn,
|
||||
(intern_id(ctx, socket.clone()), ctx.intern(input)),
|
||||
);
|
||||
Ok(())
|
||||
})
|
||||
.map_err(|err| format!("{}", err))?;
|
||||
engine
|
||||
.interp
|
||||
.execute::<()>(&engine.exec)
|
||||
.map_err(|err| format!("{}", err))
|
||||
}
|
||||
|
||||
pub fn handle_websocket_output_log_err(socket: &WebSocketId, engine: &mut LuaState, input: &[u8]) {
|
||||
match handle_websocket_output(socket, engine, input) {
|
||||
Ok(()) => {}
|
||||
Err(e) => console::log_2(
|
||||
&JsValue::from_str("An error occurred calling the WebSocket input handler"),
|
||||
&JsValue::from_str(&e),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_websocket_close(socket: &WebSocketId, engine: &mut LuaState) -> Result<(), String> {
|
||||
engine
|
||||
.interp
|
||||
.try_enter(|ctx| {
|
||||
let handlers = Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"handlers")))?;
|
||||
let input_fn =
|
||||
Function::from_value(ctx, handlers.get(ctx, ctx.intern_static(b"mudclose")))?;
|
||||
ctx.fetch(&engine.exec)
|
||||
.restart(ctx, input_fn, intern_id(ctx, socket.clone()));
|
||||
Ok(())
|
||||
})
|
||||
.map_err(|err| format!("{}", err))?;
|
||||
engine
|
||||
.interp
|
||||
.execute::<()>(&engine.exec)
|
||||
.map_err(|err| format!("{}", err))
|
||||
}
|
||||
|
||||
pub fn handle_websocket_close_log_err(socket: &WebSocketId, engine: &mut LuaState) {
|
||||
match handle_websocket_close(socket, engine) {
|
||||
Ok(()) => {}
|
||||
Err(e) => console::log_2(
|
||||
&JsValue::from_str("An error occurred calling the WebSocket input handler"),
|
||||
&JsValue::from_str(&e),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn sendmud_raw<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &GlobalMemoCell,
|
||||
_global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let mud: WebSocketId = try_unwrap_socketid(ctx, &stack.pop_front())?;
|
||||
let msg: piccolo::String = stack.from_front(ctx)?;
|
||||
send_message_to_mud(&mud, msg.as_bytes(), &global_memo)?;
|
||||
Ok(piccolo::CallbackReturn::Return)
|
||||
})
|
||||
}
|
||||
|
||||
fn connect_mud<'gc>(
|
||||
ctx: Context<'gc>,
|
||||
global_memo: &GlobalMemoCell,
|
||||
_global_layout: &UseStateSetter<GlobalLayoutCell>,
|
||||
) -> Callback<'gc> {
|
||||
let global_memo = global_memo.clone();
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let mut trusted: bool = false;
|
||||
let name: String = loop {
|
||||
let v: String = stack.from_front(ctx)?;
|
||||
if v == "-trust" {
|
||||
trusted = true;
|
||||
continue;
|
||||
}
|
||||
break v;
|
||||
};
|
||||
let name: Value<'gc> = ctx.intern(debrace(&name).as_bytes()).into();
|
||||
|
||||
let muds = Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"muds")))?;
|
||||
if !muds.get_value(name).is_nil() {
|
||||
Err(Error::msg(
|
||||
"Attempt to create MUD connection using name that's already taken",
|
||||
))?
|
||||
}
|
||||
|
||||
let url: String = stack.from_front(ctx)?;
|
||||
let new_socket = intern_id(
|
||||
ctx,
|
||||
connect_websocket(
|
||||
trusted,
|
||||
&url,
|
||||
&mut global_memo.ws_registry.borrow_mut(),
|
||||
&global_memo,
|
||||
)?,
|
||||
);
|
||||
|
||||
muds.set(ctx, name, new_socket)?;
|
||||
Ok(piccolo::CallbackReturn::Return)
|
||||
})
|
||||
}
|
@ -7,13 +7,13 @@ use yew::prelude::*;
|
||||
pub mod command_handler;
|
||||
pub mod id_intern;
|
||||
pub mod lineengine;
|
||||
pub mod lua_state;
|
||||
pub mod lua_engine;
|
||||
pub mod parsing;
|
||||
pub mod split_panel;
|
||||
pub mod term_split;
|
||||
pub mod term_view;
|
||||
pub mod websocket;
|
||||
use crate::lua_state::{install_lua_globals, LuaState};
|
||||
use crate::lua_engine::{install_lua_globals, LuaState};
|
||||
use crate::split_panel::*;
|
||||
use crate::term_view::*;
|
||||
use crate::websocket::RegisteredWebSockets;
|
||||
|
@ -10,7 +10,7 @@ use web_sys::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
lua_state::{handle_websocket_close_log_err, handle_websocket_output_log_err},
|
||||
lua_engine::muds::{handle_websocket_close_log_err, handle_websocket_output_log_err},
|
||||
GlobalMemoCell,
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user