Compare commits
No commits in common. "466566a6c5a78ea4b019e03435cd03dff2baaa62" and "1fa196da1f0b738cef47135bac3d83f9cf40035a" have entirely different histories.
466566a6c5
...
1fa196da1f
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -1015,9 +1015,9 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.209"
|
version = "1.0.206"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
|
checksum = "5b3e4cd94123dd520a128bcd11e34d9e9e423e7e3e50425cb1b4b1e3549d0284"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
@ -1046,9 +1046,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.209"
|
version = "1.0.206"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
|
checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1057,9 +1057,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.127"
|
version = "1.0.122"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad"
|
checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -1352,14 +1352,11 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
"gc-arena",
|
|
||||||
"im",
|
"im",
|
||||||
"itertools",
|
"itertools",
|
||||||
"minicrossterm",
|
"minicrossterm",
|
||||||
"nom",
|
"nom",
|
||||||
"piccolo",
|
"piccolo",
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
|
@ -19,6 +19,3 @@ minicrossterm = { git = "https://git.blastmud.org/blasthavers/minicrossterm.git"
|
|||||||
thiserror = "1.0.63"
|
thiserror = "1.0.63"
|
||||||
console_error_panic_hook = "0.1.7"
|
console_error_panic_hook = "0.1.7"
|
||||||
anyhow = "1.0.86"
|
anyhow = "1.0.86"
|
||||||
serde = "1.0.209"
|
|
||||||
serde_json = "1.0.127"
|
|
||||||
gc-arena = "0.5.3"
|
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
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>>>);
|
|
||||||
|
|
||||||
impl<'gc, A> Singleton<'gc> for InternMapSingleton<'gc, A>
|
|
||||||
where
|
|
||||||
A: Collect,
|
|
||||||
{
|
|
||||||
fn create(ctx: Context<'gc>) -> Self {
|
|
||||||
InternMapSingleton(Gc::new(&ctx, Default::default()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn intern_id<'gc, A>(ctx: Context<'gc>, input: A) -> Value<'gc>
|
|
||||||
where
|
|
||||||
A: Collect + Ord + Clone + 'static,
|
|
||||||
{
|
|
||||||
let intern_map: &'gc InternMapSingleton<'gc, A> =
|
|
||||||
ctx.singleton::<Rootable!['gcb => InternMapSingleton<'gcb, A>]>();
|
|
||||||
let gc_id = 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unintern_id<'gc, A>(ctx: Context<'gc>, input: &A)
|
|
||||||
where
|
|
||||||
A: Collect + Ord + Clone + 'static,
|
|
||||||
{
|
|
||||||
let intern_map: &InternMapSingleton<'gc, A> =
|
|
||||||
ctx.singleton::<Rootable!['gcb => InternMapSingleton<'gcb, A>]>();
|
|
||||||
intern_map.0.borrow_mut(&ctx).remove(input);
|
|
||||||
}
|
|
172
src/lua_state.rs
172
src/lua_state.rs
@ -1,19 +1,13 @@
|
|||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use gc_arena::{Gc, Rootable};
|
|
||||||
use piccolo::{
|
use piccolo::{
|
||||||
self, Callback, Closure, Context, Executor, FromValue, Function, IntoValue, Lua,
|
Callback, Closure, Context, Executor, FromValue, Function, IntoValue, Lua, StashedExecutor,
|
||||||
StashedExecutor, StaticError, Table, UserData, Value, Variadic,
|
StaticError, Table, Value, Variadic,
|
||||||
};
|
};
|
||||||
use wasm_bindgen::JsValue;
|
|
||||||
use web_sys::console;
|
|
||||||
use yew::UseStateSetter;
|
use yew::UseStateSetter;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
command_handler::debrace,
|
command_handler::debrace, echo_to_term_frame, GlobalLayoutCell, GlobalLayoutState,
|
||||||
echo_to_term_frame,
|
GlobalMemoCell, TermFrame,
|
||||||
id_intern::intern_id,
|
|
||||||
websocket::{connect_websocket, send_message_to_mud, WebSocketId},
|
|
||||||
GlobalLayoutCell, GlobalLayoutState, GlobalMemoCell, TermFrame,
|
|
||||||
};
|
};
|
||||||
use std::{collections::VecDeque, rc::Rc, str};
|
use std::{collections::VecDeque, rc::Rc, str};
|
||||||
|
|
||||||
@ -34,11 +28,7 @@ impl LuaState {
|
|||||||
fn try_set_current_frame(&mut self, frame: &TermFrame) -> Result<(), StaticError> {
|
fn try_set_current_frame(&mut self, frame: &TermFrame) -> Result<(), StaticError> {
|
||||||
self.interp.try_enter(|ctx| {
|
self.interp.try_enter(|ctx| {
|
||||||
let info_table = Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"info")))?;
|
let info_table = Table::from_value(ctx, ctx.get_global(ctx.intern_static(b"info")))?;
|
||||||
info_table.set(
|
info_table.set(ctx, ctx.intern_static(b"current_frame"), frame.0 as i64)?;
|
||||||
ctx,
|
|
||||||
ctx.intern_static(b"current_frame"),
|
|
||||||
intern_id::<TermFrame>(ctx, frame.clone()),
|
|
||||||
)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -121,8 +111,6 @@ pub fn install_lua_globals(
|
|||||||
register_command!(echo);
|
register_command!(echo);
|
||||||
register_command!(echo_frame);
|
register_command!(echo_frame);
|
||||||
register_command!(echo_frame_raw);
|
register_command!(echo_frame_raw);
|
||||||
register_command!(connect_mud);
|
|
||||||
register_command!(sendmud_raw);
|
|
||||||
register_command!(hsplit);
|
register_command!(hsplit);
|
||||||
register_command!(panel_merge);
|
register_command!(panel_merge);
|
||||||
register_command!(vsplit);
|
register_command!(vsplit);
|
||||||
@ -134,10 +122,6 @@ pub fn install_lua_globals(
|
|||||||
ctx.set_global(ctx.intern_static(b"info").into_value(ctx), info_table)
|
ctx.set_global(ctx.intern_static(b"info").into_value(ctx), info_table)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|_| Error::msg("Can't set info key"))?;
|
.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(())
|
Ok(())
|
||||||
})
|
})
|
||||||
@ -153,11 +137,12 @@ fn echo_frame_raw<'gc, 'a>(
|
|||||||
) -> Callback<'gc> {
|
) -> Callback<'gc> {
|
||||||
let global_memo = global_memo.clone();
|
let global_memo = global_memo.clone();
|
||||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||||
let frame: TermFrame = try_unwrap_frame(ctx, &stack.pop_front())?;
|
let frame_no: u64 = stack.from_front(ctx)?;
|
||||||
let message: piccolo::String = stack.from_front(ctx)?;
|
let message: piccolo::String = stack.from_front(ctx)?;
|
||||||
let message_str = str::from_utf8(message.as_bytes())
|
let message_str = str::from_utf8(message.as_bytes())
|
||||||
.map_err(|_| "Expected message to echo to be UTF-8.".into_value(ctx))?;
|
.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))?;
|
echo_to_term_frame(&global_memo, &TermFrame(frame_no), message_str)
|
||||||
|
.map_err(|m| m.into_value(ctx))?;
|
||||||
Ok(piccolo::CallbackReturn::Return)
|
Ok(piccolo::CallbackReturn::Return)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -282,144 +267,3 @@ fn panel_merge<'gc>(
|
|||||||
Ok(piccolo::CallbackReturn::Return)
|
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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@ -5,25 +5,21 @@ use term_split::TermSplit;
|
|||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
pub mod command_handler;
|
pub mod command_handler;
|
||||||
pub mod id_intern;
|
|
||||||
pub mod lineengine;
|
pub mod lineengine;
|
||||||
pub mod lua_state;
|
pub mod lua_state;
|
||||||
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;
|
|
||||||
use crate::lua_state::{install_lua_globals, LuaState};
|
use crate::lua_state::{install_lua_globals, LuaState};
|
||||||
use crate::split_panel::*;
|
use crate::split_panel::*;
|
||||||
use crate::term_view::*;
|
use crate::term_view::*;
|
||||||
use crate::websocket::RegisteredWebSockets;
|
|
||||||
|
|
||||||
#[derive(Properties)]
|
#[derive(Properties)]
|
||||||
pub struct GlobalMemoState {
|
pub struct GlobalMemoState {
|
||||||
// No strong references allowed between each of these groups of state.
|
// No strong references allowed between each of these groups of state.
|
||||||
frame_registry: RefCell<RegisteredTermFrames>,
|
frame_registry: RefCell<RegisteredTermFrames>,
|
||||||
lua_engine: RefCell<LuaState>,
|
lua_engine: RefCell<LuaState>,
|
||||||
ws_registry: RefCell<RegisteredWebSockets>,
|
|
||||||
|
|
||||||
// A cache of the latest layout info (separate from the state).
|
// A cache of the latest layout info (separate from the state).
|
||||||
// Updating this doesn't force a relayout, so only update the cache when
|
// Updating this doesn't force a relayout, so only update the cache when
|
||||||
@ -63,7 +59,6 @@ fn app() -> Html {
|
|||||||
});
|
});
|
||||||
let global_memo = use_memo((), |_| GlobalMemoState {
|
let global_memo = use_memo((), |_| GlobalMemoState {
|
||||||
frame_registry: RegisteredTermFrames::new().into(),
|
frame_registry: RegisteredTermFrames::new().into(),
|
||||||
ws_registry: RegisteredWebSockets::new().into(),
|
|
||||||
lua_engine: LuaState::setup().expect("Can create interpreter").into(),
|
lua_engine: LuaState::setup().expect("Can create interpreter").into(),
|
||||||
layout: RefCell::new((*global_layout).clone()),
|
layout: RefCell::new((*global_layout).clone()),
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use gc_arena::Collect;
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use web_sys::{Element, Node};
|
use web_sys::{Element, Node};
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
@ -66,8 +65,7 @@ extern "C" {
|
|||||||
fn fit(this: &FitAddon);
|
fn fit(this: &FitAddon);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Eq, Ord, Hash, PartialEq, PartialOrd, Clone, Debug, Collect)]
|
#[derive(Eq, Ord, Hash, PartialEq, PartialOrd, Clone, Debug)]
|
||||||
#[collect(require_static)]
|
|
||||||
pub struct TermFrame(pub u64);
|
pub struct TermFrame(pub u64);
|
||||||
|
|
||||||
#[derive(Properties)]
|
#[derive(Properties)]
|
||||||
|
140
src/websocket.rs
140
src/websocket.rs
@ -1,140 +0,0 @@
|
|||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use gc_arena::Collect;
|
|
||||||
use serde::Deserialize;
|
|
||||||
use wasm_bindgen::{closure::Closure, JsCast, JsValue};
|
|
||||||
use web_sys::{
|
|
||||||
console,
|
|
||||||
js_sys::{ArrayBuffer, JsString, Uint8Array},
|
|
||||||
BinaryType, DomException, MessageEvent, WebSocket,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
lua_state::{handle_websocket_close_log_err, handle_websocket_output_log_err},
|
|
||||||
GlobalMemoCell,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(PartialEq, PartialOrd, Eq, Ord, Debug, Clone, Collect)]
|
|
||||||
#[collect(require_static)]
|
|
||||||
pub struct WebSocketId(pub u64);
|
|
||||||
pub struct WebSocketData {
|
|
||||||
pub connection: WebSocket,
|
|
||||||
pub trusted: bool, // Is it allowed to submit Lua to be run?
|
|
||||||
pub closed: bool,
|
|
||||||
pub url: String,
|
|
||||||
pub retained_closures: Option<(Closure<dyn FnMut(MessageEvent)>, Closure<dyn FnMut()>)>,
|
|
||||||
}
|
|
||||||
pub type RegisteredWebSockets = BTreeMap<WebSocketId, WebSocketData>;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub enum MessageFromServer {
|
|
||||||
RunLua(String),
|
|
||||||
#[serde(other)]
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn connect_websocket(
|
|
||||||
trusted: bool,
|
|
||||||
url: &str,
|
|
||||||
sockets: &mut RegisteredWebSockets,
|
|
||||||
// Only used for callbacks, expected sockets already borrowed mut!
|
|
||||||
globals: &GlobalMemoCell,
|
|
||||||
) -> anyhow::Result<WebSocketId> {
|
|
||||||
let mut data = WebSocketData {
|
|
||||||
connection: WebSocket::new(url).map_err(|e| {
|
|
||||||
anyhow::Error::msg(
|
|
||||||
e.dyn_into::<DomException>()
|
|
||||||
.map(|e| e.message().to_string())
|
|
||||||
.unwrap_or("Unknown error connecting".to_owned()),
|
|
||||||
)
|
|
||||||
})?,
|
|
||||||
trusted,
|
|
||||||
closed: false,
|
|
||||||
url: url.to_owned(),
|
|
||||||
retained_closures: None,
|
|
||||||
};
|
|
||||||
data.connection.set_binary_type(BinaryType::Arraybuffer);
|
|
||||||
|
|
||||||
let new_id = sockets
|
|
||||||
.last_key_value()
|
|
||||||
.map_or(WebSocketId(0), |v| WebSocketId(v.0 .0 + 1));
|
|
||||||
let data_globals = globals.clone();
|
|
||||||
let data_new_id = new_id.clone();
|
|
||||||
let data_closure: Closure<dyn FnMut(MessageEvent)> = Closure::new(move |ev: MessageEvent| {
|
|
||||||
let data = ev.data();
|
|
||||||
if data.has_type::<ArrayBuffer>() {
|
|
||||||
handle_websocket_output_log_err(
|
|
||||||
&data_new_id,
|
|
||||||
&mut data_globals.lua_engine.borrow_mut(),
|
|
||||||
&Uint8Array::new(&data).to_vec(),
|
|
||||||
);
|
|
||||||
} else if data.has_type::<JsString>() {
|
|
||||||
let msg: String = data.unchecked_into::<JsString>().into();
|
|
||||||
if let Some(latest_data) = data_globals.ws_registry.borrow().get(&data_new_id) {
|
|
||||||
if latest_data.trusted {
|
|
||||||
match data_globals.lua_engine.borrow_mut().execute(&msg) {
|
|
||||||
Ok(()) => {}
|
|
||||||
Err(e) => console::log_3(
|
|
||||||
&JsValue::from_str("Error executing Lua from websocket"),
|
|
||||||
&JsValue::from_str(&latest_data.url),
|
|
||||||
&JsValue::from_str(&e),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console::log_2(
|
|
||||||
&JsValue::from_str("Ignoring Lua from untrusted websocket."),
|
|
||||||
&JsValue::from_str(&latest_data.url),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let close_globals = globals.clone();
|
|
||||||
let close_id = new_id.clone();
|
|
||||||
let close_closure: Closure<dyn FnMut()> =
|
|
||||||
Closure::new(
|
|
||||||
move || match close_globals.ws_registry.borrow_mut().get_mut(&close_id) {
|
|
||||||
None => {}
|
|
||||||
Some(closed_socket) => {
|
|
||||||
closed_socket.closed = true;
|
|
||||||
closed_socket.retained_closures = None;
|
|
||||||
handle_websocket_close_log_err(
|
|
||||||
&close_id,
|
|
||||||
&mut close_globals.lua_engine.borrow_mut(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
data.connection
|
|
||||||
.add_event_listener_with_callback("message", data_closure.as_ref().unchecked_ref())
|
|
||||||
.expect("Couldn't set message handler on WebSocket");
|
|
||||||
data.connection
|
|
||||||
.add_event_listener_with_callback("close", close_closure.as_ref().unchecked_ref())
|
|
||||||
.expect("Couldn't set close handler on WebSocket");
|
|
||||||
data.connection
|
|
||||||
.add_event_listener_with_callback("error", close_closure.as_ref().unchecked_ref())
|
|
||||||
.expect("Couldn't set error handler on WebSocket");
|
|
||||||
data.retained_closures = Some((data_closure, close_closure));
|
|
||||||
sockets.insert(new_id.clone(), data);
|
|
||||||
Ok(new_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_message_to_mud(
|
|
||||||
socket: &WebSocketId,
|
|
||||||
msg: &[u8],
|
|
||||||
global: &GlobalMemoCell,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
match global.ws_registry.borrow().get(socket) {
|
|
||||||
None => Err(anyhow::Error::msg("MUD connection not found")),
|
|
||||||
Some(sock_data) if sock_data.closed => Err(anyhow::Error::msg("MUD connection is closed")),
|
|
||||||
Some(sock_data) => {
|
|
||||||
sock_data.connection.send_with_u8_array(msg).map_err(|e| {
|
|
||||||
e.dyn_into::<DomException>()
|
|
||||||
.map(|e| anyhow::Error::msg(e.message()))
|
|
||||||
.unwrap_or_else(|_| anyhow::Error::msg("Unexpected exception while sending"))
|
|
||||||
})?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user