Refactor ready for MUD-specific routing
This commit is contained in:
parent
466566a6c5
commit
243132eeec
@ -1,7 +1,7 @@
|
|||||||
use itertools::join;
|
use itertools::join;
|
||||||
|
|
||||||
use crate::{
|
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 {
|
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 gc_arena::{lock::GcRefLock, Collect, Gc, Rootable};
|
||||||
use piccolo::{Context, IntoValue, Singleton, UserData, Value};
|
use piccolo::{Context, IntoValue, Singleton, UserData, Value};
|
||||||
|
|
||||||
#[derive(Collect)]
|
#[derive(Collect)]
|
||||||
#[collect(no_drop)]
|
#[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>
|
impl<'gc, A> Singleton<'gc> for InternMapSingleton<'gc, A>
|
||||||
where
|
where
|
||||||
@ -18,17 +18,22 @@ where
|
|||||||
|
|
||||||
pub fn intern_id<'gc, A>(ctx: Context<'gc>, input: A) -> Value<'gc>
|
pub fn intern_id<'gc, A>(ctx: Context<'gc>, input: A) -> Value<'gc>
|
||||||
where
|
where
|
||||||
A: Collect + Ord + Clone + 'static,
|
A: Collect + Ord + Clone + Debug + 'static,
|
||||||
{
|
{
|
||||||
let intern_map: &'gc InternMapSingleton<'gc, A> =
|
let intern_map: &'gc InternMapSingleton<'gc, A> =
|
||||||
ctx.singleton::<Rootable!['gcb => InternMapSingleton<'gcb, A>]>();
|
ctx.singleton::<Rootable!['gcb => InternMapSingleton<'gcb, A>]>();
|
||||||
let gc_id = intern_map
|
intern_map
|
||||||
.0
|
.0
|
||||||
.borrow_mut(&ctx)
|
.borrow_mut(&ctx)
|
||||||
.entry(input.clone())
|
.entry(input.clone())
|
||||||
.or_insert_with(|| Gc::new(&ctx, input.clone()))
|
.or_insert_with(|| {
|
||||||
.clone();
|
UserData::<'gc>::new::<Rootable!['gcb => Gc<'gcb, A>]>(
|
||||||
UserData::<'gc>::new::<Rootable!['gcb => Gc<'gcb, A>]>(&ctx, gc_id).into_value(ctx)
|
&ctx,
|
||||||
|
Gc::new(&ctx, input.clone()),
|
||||||
|
)
|
||||||
|
.into_value(ctx)
|
||||||
|
})
|
||||||
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unintern_id<'gc, A>(ctx: Context<'gc>, input: &A)
|
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 command_handler;
|
||||||
pub mod id_intern;
|
pub mod id_intern;
|
||||||
pub mod lineengine;
|
pub mod lineengine;
|
||||||
pub mod lua_state;
|
pub mod lua_engine;
|
||||||
pub mod parsing;
|
pub mod parsing;
|
||||||
pub mod split_panel;
|
pub mod split_panel;
|
||||||
pub mod term_split;
|
pub mod term_split;
|
||||||
pub mod term_view;
|
pub mod term_view;
|
||||||
pub mod websocket;
|
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::split_panel::*;
|
||||||
use crate::term_view::*;
|
use crate::term_view::*;
|
||||||
use crate::websocket::RegisteredWebSockets;
|
use crate::websocket::RegisteredWebSockets;
|
||||||
|
@ -10,7 +10,7 @@ use web_sys::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
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,
|
GlobalMemoCell,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user