1199 lines
43 KiB
Rust
1199 lines
43 KiB
Rust
use anyhow::Error;
|
|
use gc_arena::{Gc, Rootable};
|
|
use piccolo::{
|
|
self, async_sequence, Callback, CallbackReturn, Context, FromValue, Function, IntoValue,
|
|
SequenceReturn, StashedTable, StashedUserData, StashedValue, Table, UserData, Value,
|
|
};
|
|
use telopt::{EOR_TELOPT, TERMTYPE_TELOPT};
|
|
use wasm_bindgen::JsValue;
|
|
use web_sys::{console, window};
|
|
use yew::UseStateSetter;
|
|
|
|
use crate::{
|
|
command_handler::execute_queue,
|
|
echo_to_term_frame,
|
|
id_intern::{intern_id, unintern_id},
|
|
logging::{start_deleting_logs, start_downloading_logs, start_listing_logs},
|
|
match_table::{create_match_table, match_table_add, match_table_remove},
|
|
telnet::{parse_telnet_buf, TelnetOutput},
|
|
websocket::{connect_websocket, send_message_to_mud, WebSocketId},
|
|
FrameId, GlobalLayoutCell, GlobalMemoCell,
|
|
};
|
|
|
|
use self::telopt::{
|
|
handle_incoming_do, handle_incoming_dont, handle_incoming_will, handle_incoming_wont,
|
|
send_subnegotiation_if_allowed, set_option_supported, MudWithMemo, Side, Telopt, NAWS_TELOPT,
|
|
};
|
|
|
|
use super::{call_checking_metatable, list_match_tab, try_unwrap_frame, LuaState};
|
|
pub mod telopt;
|
|
|
|
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: Value<'gc> = ctx.get_global::<Table<'gc>>("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![Gc<'_, 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 = ctx.get_global("handlers")?;
|
|
let input_fn: Function = 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(super) fn mudclose<'gc>(ctx: Context<'gc>, _global_memo: &GlobalMemoCell) -> Callback<'gc> {
|
|
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
|
let mud: Value<'gc> = stack
|
|
.pop_front()
|
|
.ok_or_else(|| anyhow::Error::msg("Missing argument to mudclose"))?;
|
|
let conntab: Table<'gc> = Table::from_value(
|
|
ctx,
|
|
Table::from_value(ctx, ctx.get_global("muds")?)?.get_value(ctx, mud),
|
|
)?;
|
|
let seq = piccolo::async_sequence(&ctx, |locals, mut seq| {
|
|
let conntab = locals.stash(&ctx, conntab);
|
|
async move {
|
|
call_checking_metatable::<StashedTable, _>(
|
|
&mut seq,
|
|
conntab.clone(),
|
|
"closed",
|
|
&[],
|
|
)
|
|
.await?;
|
|
Ok(piccolo::SequenceReturn::Return)
|
|
}
|
|
});
|
|
Ok(piccolo::CallbackReturn::Sequence(seq))
|
|
})
|
|
}
|
|
|
|
pub fn handle_websocket_output_log_err(
|
|
socket: &WebSocketId,
|
|
globals: &GlobalMemoCell,
|
|
input: &[u8],
|
|
) {
|
|
match handle_websocket_output(socket, &mut globals.lua_engine.borrow_mut(), input) {
|
|
Ok(()) => {}
|
|
Err(e) => console::log_2(
|
|
&JsValue::from_str("An error occurred calling the WebSocket input handler"),
|
|
&JsValue::from_str(&e),
|
|
),
|
|
}
|
|
execute_queue(globals);
|
|
}
|
|
|
|
pub fn handle_websocket_close(socket: &WebSocketId, engine: &mut LuaState) -> Result<(), String> {
|
|
engine
|
|
.interp
|
|
.try_enter(|ctx| {
|
|
let handlers: Table = ctx.get_global("handlers")?;
|
|
let input_fn: Function = 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, globals: &GlobalMemoCell) {
|
|
match handle_websocket_close(socket, &mut globals.lua_engine.borrow_mut()) {
|
|
Ok(()) => {}
|
|
Err(e) => console::log_2(
|
|
&JsValue::from_str("An error occurred calling the WebSocket close handler"),
|
|
&JsValue::from_str(&e),
|
|
),
|
|
}
|
|
execute_queue(globals);
|
|
}
|
|
|
|
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()
|
|
.ok_or_else(|| anyhow::Error::msg("Missing MUD argument"))?,
|
|
)?;
|
|
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 mut naws: bool = true;
|
|
let name: String = loop {
|
|
let v: String = stack.from_front(ctx)?;
|
|
if v == "-trust" {
|
|
trusted = true;
|
|
continue;
|
|
}
|
|
if v == "-no_naws" {
|
|
naws = false;
|
|
continue;
|
|
}
|
|
break v;
|
|
};
|
|
let name: Value<'gc> = ctx.intern(name.as_bytes()).into();
|
|
|
|
let muds: Table = ctx.get_global("muds")?;
|
|
if !muds.get_value(ctx, 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 mud_class: Table = ctx
|
|
.get_global::<Table>("classes")?
|
|
.get(ctx, ctx.intern_static(b"mud"))?;
|
|
|
|
let conntab = Table::new(&ctx);
|
|
muds.set(ctx, new_socket, conntab)?;
|
|
conntab.set(ctx, ctx.intern_static(b"socket"), new_socket)?;
|
|
conntab.set(ctx, ctx.intern_static(b"buffer"), ctx.intern_static(b""))?;
|
|
conntab.set_metatable(&ctx, Some(mud_class));
|
|
let curframe: Value = ctx
|
|
.get_global::<Table>("info")?
|
|
.get(ctx, ctx.intern_static(b"current_frame"))?;
|
|
if let Ok(curframe) = try_unwrap_frame(ctx, &curframe) {
|
|
if let Ok(frame) = ctx
|
|
.get_global::<Table>("frames")?
|
|
.get::<i64, Value>(ctx, curframe.0 as i64)
|
|
{
|
|
if !frame.is_nil() {
|
|
Table::from_value(ctx, frame)?.set(
|
|
ctx,
|
|
ctx.intern_static(b"linked_mud"),
|
|
conntab,
|
|
)?;
|
|
}
|
|
}
|
|
}
|
|
|
|
if naws {
|
|
set_option_supported(ctx, &conntab, &NAWS_TELOPT, &Side::Us);
|
|
}
|
|
set_option_supported(ctx, &conntab, &TERMTYPE_TELOPT, &Side::Us);
|
|
set_option_supported(ctx, &conntab, &EOR_TELOPT, &Side::Him);
|
|
|
|
// Call conntab:new...
|
|
let seq = async_sequence(&ctx, |locals, mut seq| {
|
|
let conntab = locals.stash(&ctx, conntab);
|
|
async move {
|
|
call_checking_metatable::<StashedTable, _>(&mut seq, conntab, "new", &[]).await?;
|
|
Ok(SequenceReturn::Return)
|
|
}
|
|
});
|
|
|
|
Ok(piccolo::CallbackReturn::Sequence(seq))
|
|
})
|
|
}
|
|
|
|
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()
|
|
.ok_or_else(|| anyhow::Error::msg("Missing MUD value argument"))?;
|
|
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(name.as_bytes()).into();
|
|
|
|
let muds: Table = ctx.get_global("muds")?;
|
|
let mud_value = muds.get_value(ctx, 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_raw(&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_raw(&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> {
|
|
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
|
let mud: Value<'gc> = stack
|
|
.pop_front()
|
|
.ok_or_else(|| anyhow::Error::msg("Missing argument to mudoutput"))?;
|
|
let output: &'gc [u8] = stack.from_front::<piccolo::String<'gc>>(ctx)?.as_bytes();
|
|
|
|
let conntab: Table<'gc> = Table::from_value(
|
|
ctx,
|
|
Table::from_value(ctx, ctx.get_global("muds")?)?.get_value(ctx, mud),
|
|
)?;
|
|
|
|
let buf: &'gc [u8] =
|
|
piccolo::String::from_value(ctx, conntab.get(ctx, ctx.intern_static(b"buffer"))?)?
|
|
.as_bytes();
|
|
let mut cur_buf: Vec<u8> = [buf, output].concat();
|
|
|
|
let mut fns: Vec<(&'static str, Vec<Value>)> = vec![];
|
|
loop {
|
|
match parse_telnet_buf(&cur_buf) {
|
|
(new_buf, None) => {
|
|
conntab.set(ctx, ctx.intern_static(b"buffer"), ctx.intern(&new_buf))?;
|
|
|
|
let seq = piccolo::async_sequence(&ctx, |locals, mut seq| {
|
|
let conntab = locals.stash(&ctx, conntab);
|
|
let fns: Vec<(&'static str, Vec<StashedValue>)> = fns
|
|
.into_iter()
|
|
.map(|fnv| {
|
|
(
|
|
fnv.0,
|
|
fnv.1.into_iter().map(|v| locals.stash(&ctx, v)).collect(),
|
|
)
|
|
})
|
|
.collect();
|
|
async move {
|
|
for (func_name, params) in fns {
|
|
console::log_1(&JsValue::from_str(&format!(
|
|
"Calling {}",
|
|
func_name
|
|
)));
|
|
call_checking_metatable::<StashedTable, _>(
|
|
&mut seq,
|
|
conntab.clone(),
|
|
func_name,
|
|
¶ms,
|
|
)
|
|
.await?;
|
|
}
|
|
Ok(piccolo::SequenceReturn::Return)
|
|
}
|
|
});
|
|
|
|
return Ok(piccolo::CallbackReturn::Sequence(seq));
|
|
}
|
|
(new_buf, Some(cmd)) => {
|
|
cur_buf = new_buf;
|
|
match cmd {
|
|
TelnetOutput::Line(l) => {
|
|
fns.push(("mudoutput_line", vec![ctx.intern(&l).into_value(ctx)]))
|
|
}
|
|
TelnetOutput::Prompt(p) => {
|
|
fns.push(("mudoutput_prompt", vec![ctx.intern(&p).into_value(ctx)]))
|
|
}
|
|
TelnetOutput::Nop => {}
|
|
TelnetOutput::Break => fns.push(("mudoutput_break", vec![])),
|
|
TelnetOutput::Sync => fns.push(("mudoutput_sync", vec![])),
|
|
TelnetOutput::Interrupt => fns.push(("mudoutput_interrupt", vec![])),
|
|
|
|
TelnetOutput::AbortOutput => fns.push(("mudoutput_abort_output", vec![])),
|
|
TelnetOutput::AreYouThere => fns.push(("mudoutput_areyouthere", vec![])),
|
|
TelnetOutput::Will(v) => {
|
|
fns.push(("mudoutput_will", vec![v.into_value(ctx)]))
|
|
}
|
|
TelnetOutput::Wont(v) => {
|
|
fns.push(("mudoutput_wont", vec![v.into_value(ctx)]))
|
|
}
|
|
TelnetOutput::Do(v) => fns.push(("mudoutput_do", vec![v.into_value(ctx)])),
|
|
TelnetOutput::Dont(v) => {
|
|
fns.push(("mudoutput_dont", vec![v.into_value(ctx)]))
|
|
}
|
|
TelnetOutput::Subnegotiation(t) => {
|
|
fns.push(("mudoutput_subnegotiation", vec![t.into_value(ctx)]))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
pub(super) fn mudoutput_line<'gc>(
|
|
ctx: Context<'gc>,
|
|
_global_memo: &GlobalMemoCell,
|
|
) -> Callback<'gc> {
|
|
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
|
let mud = Table::from_value(
|
|
ctx,
|
|
stack
|
|
.pop_front()
|
|
.ok_or_else(|| anyhow::Error::msg("Missing muds:mudoutput_line self"))?,
|
|
)?;
|
|
let line = piccolo::String::from_value(
|
|
ctx,
|
|
stack
|
|
.pop_front()
|
|
.ok_or_else(|| anyhow::Error::msg("Missing muds:mudoutput_line line"))?,
|
|
)?;
|
|
|
|
let frameroutes: Table = mud.get(ctx, "frameroutes")?;
|
|
let triggers: UserData = mud.get(ctx, "triggers")?;
|
|
let default_frame: Value = frameroutes
|
|
.iter()
|
|
.map(|(_k, v)| Ok::<Table, Error>(Table::from_value(ctx, v)?))
|
|
.next()
|
|
.unwrap_or_else(|| Ok(ctx.get_global::<Table>("frames")?.get(ctx, 1_i64)?))?
|
|
.get(ctx, "frame")?;
|
|
|
|
let seq = async_sequence(&ctx, |locals, mut seq| {
|
|
let frameroutes: Vec<StashedTable> = frameroutes
|
|
.iter()
|
|
.filter_map(|fr| {
|
|
Table::from_value(ctx, fr.1)
|
|
.ok()
|
|
.map(|v| locals.stash(&ctx, v))
|
|
})
|
|
.collect();
|
|
let line = locals.stash(&ctx, line.into_value(ctx));
|
|
let triggers = locals.stash(&ctx, triggers);
|
|
let default_frame = locals.stash(&ctx, default_frame);
|
|
async move {
|
|
call_checking_metatable::<StashedUserData, _>(
|
|
&mut seq,
|
|
triggers,
|
|
"try_run_sub",
|
|
&[line.clone(), default_frame],
|
|
)
|
|
.await?;
|
|
for frameroute in frameroutes {
|
|
call_checking_metatable::<StashedTable, _>(
|
|
&mut seq,
|
|
frameroute,
|
|
"route",
|
|
&[line.clone()],
|
|
)
|
|
.await?;
|
|
}
|
|
Ok(SequenceReturn::Return)
|
|
}
|
|
});
|
|
|
|
Ok(CallbackReturn::Sequence(seq))
|
|
})
|
|
}
|
|
pub(super) fn mudoutput_prompt<'gc>(
|
|
ctx: Context<'gc>,
|
|
_global_memo: &GlobalMemoCell,
|
|
) -> Callback<'gc> {
|
|
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
|
let mud = Table::from_value(
|
|
ctx,
|
|
stack
|
|
.pop_front()
|
|
.ok_or_else(|| anyhow::Error::msg("Missing muds:mudoutput_line self"))?,
|
|
)?;
|
|
let prompt = piccolo::String::from_value(
|
|
ctx,
|
|
stack
|
|
.pop_front()
|
|
.ok_or_else(|| anyhow::Error::msg("Missing muds:mudoutput_line line"))?,
|
|
)?;
|
|
|
|
let frameroutes: Table = mud.get(ctx, "frameroutes")?;
|
|
|
|
let seq = async_sequence(&ctx, |locals, mut seq| {
|
|
let frameroutes: Vec<StashedTable> = frameroutes
|
|
.iter()
|
|
.filter_map(|fr| {
|
|
Table::from_value(ctx, fr.1)
|
|
.ok()
|
|
.map(|v| locals.stash(&ctx, v))
|
|
})
|
|
.collect();
|
|
let line = locals.stash(&ctx, prompt.into_value(ctx));
|
|
async move {
|
|
for frameroute in frameroutes {
|
|
call_checking_metatable::<StashedTable, _>(
|
|
&mut seq,
|
|
frameroute,
|
|
"route",
|
|
&[line.clone()],
|
|
)
|
|
.await?;
|
|
}
|
|
Ok(SequenceReturn::Return)
|
|
}
|
|
});
|
|
|
|
Ok(CallbackReturn::Sequence(seq))
|
|
})
|
|
}
|
|
|
|
fn name_telopt(ctx: Context, optno: u8) -> anyhow::Result<String> {
|
|
for (k, v) in ctx
|
|
.get_global::<Table>("info")?
|
|
.get::<_, Table>(ctx, "telopts")?
|
|
{
|
|
if u8::from_value(ctx, v)? == optno {
|
|
return Ok(String::from_value(ctx, k)?);
|
|
}
|
|
}
|
|
Ok(format!("{}", optno))
|
|
}
|
|
|
|
pub(super) fn mudoutput_will<'gc>(
|
|
ctx: Context<'gc>,
|
|
global_memo: &GlobalMemoCell,
|
|
) -> Callback<'gc> {
|
|
let global_memo = global_memo.clone();
|
|
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
|
let (mud, optno): (Table, u8) = stack.consume(ctx)?;
|
|
let socket: Value = mud.get(ctx, "socket")?;
|
|
let socket: &WebSocketId =
|
|
UserData::from_value(ctx, socket)?.downcast::<Rootable![Gc<'_, WebSocketId>]>()?;
|
|
let mut mud_memo = MudWithMemo {
|
|
memo: global_memo.clone(),
|
|
mud: socket.clone(),
|
|
};
|
|
if handle_incoming_will(ctx, &mut mud_memo, &mud, &Telopt(optno)) {
|
|
let seq = async_sequence(&ctx, move |locals, mut seq| {
|
|
let mud = locals.stash(&ctx, mud);
|
|
async move {
|
|
let name =
|
|
seq.try_enter(|ctx, _locals, _ex, _stack| Ok(name_telopt(ctx, optno)?))?;
|
|
call_checking_metatable::<StashedTable, _>(
|
|
&mut seq,
|
|
mud,
|
|
format!("remote_{}_enabled", name),
|
|
&[],
|
|
)
|
|
.await?;
|
|
Ok(SequenceReturn::Return)
|
|
}
|
|
});
|
|
Ok(CallbackReturn::Sequence(seq))
|
|
} else {
|
|
Ok(CallbackReturn::Return)
|
|
}
|
|
})
|
|
}
|
|
pub(super) fn mudoutput_wont<'gc>(
|
|
ctx: Context<'gc>,
|
|
global_memo: &GlobalMemoCell,
|
|
) -> Callback<'gc> {
|
|
let global_memo = global_memo.clone();
|
|
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
|
let (mud, optno): (Table, u8) = stack.consume(ctx)?;
|
|
let socket: Value = mud.get(ctx, "socket")?;
|
|
let socket: &WebSocketId =
|
|
UserData::from_value(ctx, socket)?.downcast::<Rootable![Gc<'_, WebSocketId>]>()?;
|
|
let mut mud_memo = MudWithMemo {
|
|
memo: global_memo.clone(),
|
|
mud: socket.clone(),
|
|
};
|
|
handle_incoming_wont(ctx, &mut mud_memo, &mud, &Telopt(optno));
|
|
Ok(CallbackReturn::Return)
|
|
})
|
|
}
|
|
pub(super) fn mudoutput_do<'gc>(ctx: Context<'gc>, global_memo: &GlobalMemoCell) -> Callback<'gc> {
|
|
let global_memo = global_memo.clone();
|
|
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
|
let (mud, optno): (Table, u8) = stack.consume(ctx)?;
|
|
let socket: Value = mud.get(ctx, "socket")?;
|
|
let socket: &WebSocketId =
|
|
UserData::from_value(ctx, socket)?.downcast::<Rootable![Gc<'_, WebSocketId>]>()?;
|
|
let mut mud_memo = MudWithMemo {
|
|
memo: global_memo.clone(),
|
|
mud: socket.clone(),
|
|
};
|
|
if handle_incoming_do(ctx, &mut mud_memo, &mud, &Telopt(optno)) {
|
|
let seq = async_sequence(&ctx, move |locals, mut seq| {
|
|
let mud = locals.stash(&ctx, mud);
|
|
async move {
|
|
let name =
|
|
seq.try_enter(|ctx, _locals, _ex, _stack| Ok(name_telopt(ctx, optno)?))?;
|
|
call_checking_metatable::<StashedTable, _>(
|
|
&mut seq,
|
|
mud,
|
|
format!("local_{}_enabled", name),
|
|
&[],
|
|
)
|
|
.await?;
|
|
Ok(SequenceReturn::Return)
|
|
}
|
|
});
|
|
Ok(CallbackReturn::Sequence(seq))
|
|
} else {
|
|
Ok(CallbackReturn::Return)
|
|
}
|
|
})
|
|
}
|
|
pub(super) fn mudoutput_dont<'gc>(
|
|
ctx: Context<'gc>,
|
|
global_memo: &GlobalMemoCell,
|
|
) -> Callback<'gc> {
|
|
let global_memo = global_memo.clone();
|
|
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
|
let (mud, optno): (Table, u8) = stack.consume(ctx)?;
|
|
let socket: Value = mud.get(ctx, "socket")?;
|
|
let socket: &WebSocketId =
|
|
UserData::from_value(ctx, socket)?.downcast::<Rootable![Gc<'_, WebSocketId>]>()?;
|
|
let mut mud_memo = MudWithMemo {
|
|
memo: global_memo.clone(),
|
|
mud: socket.clone(),
|
|
};
|
|
handle_incoming_dont(ctx, &mut mud_memo, &mud, &Telopt(optno));
|
|
Ok(CallbackReturn::Return)
|
|
})
|
|
}
|
|
pub(super) fn mudoutput_subnegotiation<'gc>(ctx: Context<'gc>) -> Callback<'gc> {
|
|
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
|
let (mud, mut msg): (Table, Vec<u8>) = stack.consume(ctx)?;
|
|
if msg.is_empty() {
|
|
console::log_1(&JsValue::from_str(
|
|
"Received invalid subnegotiation with no type",
|
|
));
|
|
return Ok(CallbackReturn::Return);
|
|
}
|
|
let msg_type = msg.remove(0);
|
|
let msg_typename = name_telopt(ctx, msg_type)?;
|
|
|
|
let seq = async_sequence(&ctx, |locals, mut seq| {
|
|
let mud = locals.stash(&ctx, mud);
|
|
let msg = locals.stash(&ctx, msg.into_value(ctx));
|
|
async move {
|
|
call_checking_metatable(
|
|
&mut seq,
|
|
mud,
|
|
format!("mudoutput_subnegotiation_{}", &msg_typename),
|
|
&[msg],
|
|
)
|
|
.await?;
|
|
Ok(SequenceReturn::Return)
|
|
}
|
|
});
|
|
|
|
Ok(CallbackReturn::Sequence(seq))
|
|
})
|
|
}
|
|
|
|
pub(super) fn mudoutput_subnegotiation_termtype<'gc>(
|
|
ctx: Context<'gc>,
|
|
global_memo: &GlobalMemoCell,
|
|
) -> Callback<'gc> {
|
|
let global_memo = global_memo.clone();
|
|
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
|
let (mud, mut msg): (Table, Vec<u8>) = stack.consume(ctx)?;
|
|
if msg.is_empty() {
|
|
return Ok(CallbackReturn::Return);
|
|
}
|
|
let cmd = msg.remove(0);
|
|
|
|
let socket: Value = mud.get(ctx, "socket")?;
|
|
let socket: &WebSocketId =
|
|
UserData::from_value(ctx, socket)?.downcast::<Rootable![Gc<'_, WebSocketId>]>()?;
|
|
|
|
const SEND_CMD: u8 = 1;
|
|
match cmd {
|
|
SEND_CMD => {
|
|
send_subnegotiation_if_allowed(
|
|
ctx,
|
|
&mud,
|
|
&TERMTYPE_TELOPT,
|
|
&Side::Us,
|
|
&mut MudWithMemo {
|
|
memo: global_memo.clone(),
|
|
mud: socket.clone(),
|
|
},
|
|
format!("\x00{}", "XTERM").as_bytes(),
|
|
);
|
|
Ok(CallbackReturn::Return)
|
|
}
|
|
_ => Ok(CallbackReturn::Return),
|
|
}
|
|
})
|
|
}
|
|
|
|
pub(super) fn mudinput_line<'gc>(
|
|
ctx: Context<'gc>,
|
|
_global_memo: &GlobalMemoCell,
|
|
) -> Callback<'gc> {
|
|
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
|
let (mud, line): (Table, piccolo::String) = stack.consume(ctx)?;
|
|
stack.push_back(mud.get::<piccolo::String, Value>(ctx, ctx.intern_static(b"socket"))?);
|
|
let line = [line.as_bytes(), b"\r\n"].concat();
|
|
stack.push_back(ctx.intern(&line).into_value(ctx));
|
|
|
|
let func = ctx
|
|
.get_global::<Table>("commands")?
|
|
.get::<piccolo::String, Function>(ctx, ctx.intern_static(b"sendmud_raw"))?;
|
|
|
|
Ok(CallbackReturn::Call {
|
|
function: func,
|
|
then: None,
|
|
})
|
|
})
|
|
}
|
|
|
|
pub(super) fn mudclass_closed<'gc>(
|
|
ctx: Context<'gc>,
|
|
global_memo: &GlobalMemoCell,
|
|
) -> Callback<'gc> {
|
|
let global_memo = global_memo.clone();
|
|
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
|
let mud: Table = stack.consume(ctx)?;
|
|
let frameroutes: Table = mud.get(ctx, "frameroutes")?;
|
|
let default_frame: Value = frameroutes
|
|
.iter()
|
|
.map(|(_k, v)| Ok::<Table, Error>(Table::from_value(ctx, v)?))
|
|
.next()
|
|
.unwrap_or_else(|| Ok(ctx.get_global::<Table>("frames")?.get(ctx, 1_i64)?))?
|
|
.get(ctx, "frame")?;
|
|
let default_frame: FrameId = try_unwrap_frame(ctx, &default_frame)?;
|
|
echo_to_term_frame(
|
|
&global_memo,
|
|
&default_frame,
|
|
&format!("Connection to {} lost.\r\n", name_mud(ctx, &mud)),
|
|
)
|
|
.map_err(anyhow::Error::msg)?;
|
|
Ok(CallbackReturn::Return)
|
|
})
|
|
}
|
|
|
|
pub(super) fn new_mud<'gc>(ctx: Context<'gc>) -> Callback<'gc> {
|
|
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
|
let mud: Table = Table::from_value(
|
|
ctx,
|
|
stack
|
|
.pop_front()
|
|
.ok_or_else(|| anyhow::Error::msg("classes.mud:new missing object!"))?,
|
|
)?;
|
|
let frameroutes: Table = Table::new(&ctx);
|
|
let frameroute: Table = Table::new(&ctx);
|
|
frameroutes.set(ctx, 0, frameroute)?;
|
|
frameroute.set_metatable(
|
|
&ctx,
|
|
Some(
|
|
ctx.get_global::<Table>("classes")?
|
|
.get(ctx, ctx.intern_static(b"frameroute"))?,
|
|
),
|
|
);
|
|
mud.set(ctx, ctx.intern_static(b"frameroutes"), frameroutes)?;
|
|
|
|
let curr_frame: Value = ctx
|
|
.get_global::<Table>("info")?
|
|
.get(ctx, ctx.intern_static(b"current_frame"))?;
|
|
|
|
// And call new on the frameroute, for the current frame.
|
|
let seq = async_sequence(&ctx, |locals, mut seq| {
|
|
let frameroute = locals.stash(&ctx, frameroute);
|
|
let curr_frame = locals.stash(&ctx, curr_frame);
|
|
let create_match_tab = locals.stash(&ctx, Function::Callback(create_match_table(ctx)));
|
|
let mud = locals.stash(&ctx, mud);
|
|
|
|
async move {
|
|
call_checking_metatable::<StashedTable, _>(
|
|
&mut seq,
|
|
frameroute,
|
|
"new",
|
|
&[curr_frame],
|
|
)
|
|
.await?;
|
|
seq.call(&create_match_tab, 0).await?;
|
|
|
|
seq.try_enter(|ctx, locals, _ex, mut stack| {
|
|
let matchtab: Value = stack.consume(ctx)?;
|
|
locals.fetch(&mud).set(ctx, "triggers", matchtab)?;
|
|
Ok(())
|
|
})?;
|
|
|
|
Ok(SequenceReturn::Return)
|
|
}
|
|
});
|
|
|
|
Ok(piccolo::CallbackReturn::Sequence(seq))
|
|
})
|
|
}
|
|
|
|
pub(super) fn mud_trigger<'gc>(ctx: Context<'gc>) -> Callback<'gc> {
|
|
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
|
let info: Table = ctx.get_global("info")?;
|
|
let cur_frame_id: FrameId =
|
|
try_unwrap_frame(ctx, &info.get(ctx, ctx.intern_static(b"current_frame"))?)?;
|
|
let frames: Table = ctx.get_global("frames")?;
|
|
let cur_frame: Table = frames.get(ctx, cur_frame_id.0 as i64)?;
|
|
let linked_mud: Value = cur_frame.get(ctx, "linked_mud")?;
|
|
|
|
if linked_mud.is_nil() {
|
|
Err(anyhow::Error::msg(
|
|
"No MUD connection is linked to the current terminal. Triggers are per MUD and so can only be set after connecting.",
|
|
))?;
|
|
}
|
|
let linked_mud: Table = Table::from_value(ctx, linked_mud)?;
|
|
|
|
let triggers: UserData = linked_mud.get(ctx, "triggers")?;
|
|
|
|
if stack.is_empty() {
|
|
return list_match_tab(cur_frame_id, triggers, ctx);
|
|
}
|
|
|
|
let trigger_match: piccolo::String = piccolo::String::from_value(
|
|
ctx,
|
|
stack
|
|
.pop_front()
|
|
.ok_or_else(|| anyhow::Error::msg("Missing alias match"))?,
|
|
)?;
|
|
let sub_to: piccolo::String = piccolo::String::from_value(
|
|
ctx,
|
|
stack
|
|
.pop_front()
|
|
.ok_or_else(|| anyhow::Error::msg("Missing substitution match"))?,
|
|
)?;
|
|
if !stack.is_empty() {
|
|
Err(anyhow::Error::msg(
|
|
"Extra arguments to trigger command. Try wrapping the action in {}",
|
|
))?;
|
|
}
|
|
|
|
stack.push_back(triggers.into_value(ctx));
|
|
stack.push_back(trigger_match.into_value(ctx));
|
|
stack.push_back(sub_to.into_value(ctx));
|
|
let seq = async_sequence(&ctx, |locals, mut seq| {
|
|
let add_func = locals.stash(&ctx, Function::Callback(match_table_add(ctx)));
|
|
async move {
|
|
seq.call(&add_func, 0).await?;
|
|
Ok(SequenceReturn::Return)
|
|
}
|
|
});
|
|
|
|
Ok(piccolo::CallbackReturn::Sequence(seq))
|
|
})
|
|
}
|
|
|
|
pub(super) fn mud_untrigger(ctx: Context<'_>) -> Callback<'_> {
|
|
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
|
let info: Table = ctx.get_global("info")?;
|
|
let cur_frame_id: FrameId =
|
|
try_unwrap_frame(ctx, &info.get(ctx, ctx.intern_static(b"current_frame"))?)?;
|
|
let frames: Table = ctx.get_global("frames")?;
|
|
let cur_frame: Table = frames.get(ctx, cur_frame_id.0 as i64)?;
|
|
let linked_mud: Value = cur_frame.get(ctx, "linked_mud")?;
|
|
|
|
if linked_mud.is_nil() {
|
|
Err(anyhow::Error::msg(
|
|
"No MUD connection is linked to the current terminal. Triggers are per MUD and so can only be set after connecting.",
|
|
))?;
|
|
}
|
|
let linked_mud: Table = Table::from_value(ctx, linked_mud)?;
|
|
|
|
let triggers: UserData = linked_mud.get(ctx, "triggers")?;
|
|
|
|
let trigger_match: piccolo::String = piccolo::String::from_value(
|
|
ctx,
|
|
stack
|
|
.pop_front()
|
|
.ok_or_else(|| anyhow::Error::msg("Missing trigger match"))?,
|
|
)?;
|
|
if !stack.is_empty() {
|
|
Err(anyhow::Error::msg(
|
|
"Extra arguments to unact command. Giving the commands to trigger is not necessary.",
|
|
))?;
|
|
}
|
|
|
|
stack.push_back(triggers.into_value(ctx));
|
|
stack.push_back(trigger_match.into_value(ctx));
|
|
let seq = async_sequence(&ctx, |locals, mut seq| {
|
|
let remove_func = locals.stash(&ctx, Function::Callback(match_table_remove(ctx)));
|
|
async move {
|
|
seq.call(&remove_func, 0).await?;
|
|
Ok(SequenceReturn::Return)
|
|
}
|
|
});
|
|
|
|
Ok(piccolo::CallbackReturn::Sequence(seq))
|
|
})
|
|
}
|
|
|
|
pub(super) fn mud_term_resized<'gc>(
|
|
ctx: Context<'gc>,
|
|
global_memo: &GlobalMemoCell,
|
|
) -> Callback<'gc> {
|
|
let global_memo = global_memo.clone();
|
|
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
|
let (mud, width, height): (Table, u16, u16) = stack.consume(ctx)?;
|
|
let socket: Value = mud.get(ctx, "socket")?;
|
|
let socket: &WebSocketId =
|
|
UserData::from_value(ctx, socket)?.downcast::<Rootable![Gc<'_, WebSocketId>]>()?;
|
|
send_subnegotiation_if_allowed(
|
|
ctx,
|
|
&mud,
|
|
&NAWS_TELOPT,
|
|
&Side::Us,
|
|
&mut MudWithMemo {
|
|
memo: global_memo.clone(),
|
|
mud: socket.clone(),
|
|
},
|
|
&[
|
|
(width >> 8) as u8,
|
|
(width & 0xFF) as u8,
|
|
(height >> 8) as u8,
|
|
(height & 0xFF) as u8,
|
|
],
|
|
);
|
|
Ok(CallbackReturn::Return)
|
|
})
|
|
}
|
|
|
|
pub(super) fn local_naws_enabled<'gc>(
|
|
ctx: Context<'gc>,
|
|
global_state_memo: &GlobalMemoCell,
|
|
) -> Callback<'gc> {
|
|
let global_state_memo = global_state_memo.clone();
|
|
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
|
let mud: Table = stack.consume(ctx)?;
|
|
let frameroutes: Table = mud.get(ctx, "frameroutes")?;
|
|
let default_frame: FrameId = try_unwrap_frame(
|
|
ctx,
|
|
&frameroutes
|
|
.iter()
|
|
.map(|(_k, v)| Ok::<Table, Error>(Table::from_value(ctx, v)?))
|
|
.next()
|
|
.unwrap_or_else(|| Ok(ctx.get_global::<Table>("frames")?.get(ctx, 1_i64)?))?
|
|
.get(ctx, "frame")?,
|
|
)?;
|
|
let default_frame: Table = ctx
|
|
.get_global::<Table>("frames")?
|
|
.get(ctx, default_frame.0 as i64)?;
|
|
let width: Value = default_frame.get(ctx, "width")?;
|
|
let height: Value = default_frame.get(ctx, "height")?;
|
|
if width.is_nil() || height.is_nil() {
|
|
return Ok(CallbackReturn::Return);
|
|
}
|
|
stack.push_back(mud.into_value(ctx));
|
|
stack.push_back(width);
|
|
stack.push_back(height);
|
|
let cb = mud_term_resized(ctx, &global_state_memo);
|
|
|
|
Ok(CallbackReturn::Call {
|
|
function: Function::Callback(cb),
|
|
then: None,
|
|
})
|
|
})
|
|
}
|
|
|
|
pub(super) fn opt_enabled_noop<'gc>(ctx: Context<'gc>) -> Callback<'gc> {
|
|
Callback::from_fn(&ctx, move |_ctx, _ex, _stack| Ok(CallbackReturn::Return))
|
|
}
|
|
|
|
fn name_mud<'gc>(ctx: Context<'gc>, mud: &Table<'gc>) -> String {
|
|
fn name_mud_internal<'gc>(ctx: Context<'gc>, mud: &Table<'gc>) -> anyhow::Result<String> {
|
|
let socket: Value<'gc> = mud.get(ctx, "socket")?;
|
|
let socket: &'gc WebSocketId =
|
|
UserData::from_value(ctx, socket)?.downcast::<Rootable![Gc<'_, WebSocketId>]>()?;
|
|
for (k, v) in ctx.get_global::<Table>("muds")?.iter() {
|
|
if let Ok(v) = UserData::from_value(ctx, v)
|
|
.map_err(anyhow::Error::from)
|
|
.and_then(|ud| {
|
|
ud.downcast::<Rootable![Gc<'_, WebSocketId>]>()
|
|
.map_err(anyhow::Error::from)
|
|
})
|
|
{
|
|
if v.0 == socket.0 {
|
|
return Ok(piccolo::String::from_value(ctx, k)?.to_str()?.to_owned());
|
|
}
|
|
}
|
|
}
|
|
Err(anyhow::Error::msg("No match"))
|
|
}
|
|
|
|
match name_mud_internal(ctx, mud) {
|
|
Ok(name) => name,
|
|
Err(_) => "unknown MUD".to_owned(),
|
|
}
|
|
}
|
|
|
|
pub(super) fn mud_log<'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 muds: Table = ctx.get_global("muds")?;
|
|
let mud_value: Value = muds.get(ctx, name)?;
|
|
if mud_value.is_nil() {
|
|
Err(Error::msg(
|
|
"Attempt to delete MUD connection that wasn't found",
|
|
))?
|
|
}
|
|
|
|
let log_dest: String = stack.from_back(ctx)?;
|
|
let log_dest = if log_dest == "none" || log_dest == "off" {
|
|
None
|
|
} else {
|
|
Some(log_dest)
|
|
};
|
|
|
|
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) => {
|
|
v.log_dest = log_dest;
|
|
}
|
|
}
|
|
|
|
Ok(CallbackReturn::Return)
|
|
})
|
|
}
|
|
|
|
pub(super) fn cmd_list_logs<'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, _stack| {
|
|
let frame = try_unwrap_frame(
|
|
ctx,
|
|
&ctx.get_global::<Table>("info")?
|
|
.get::<&str, Value>(ctx, "current_frame")?,
|
|
)?;
|
|
start_listing_logs(&global_memo, &frame);
|
|
Ok(CallbackReturn::Return)
|
|
})
|
|
}
|
|
|
|
pub(super) fn cmd_delete_logs<'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 frame = try_unwrap_frame(
|
|
ctx,
|
|
&ctx.get_global::<Table>("info")?
|
|
.get::<&str, Value>(ctx, "current_frame")?,
|
|
)?;
|
|
let stream = String::from_utf8(
|
|
piccolo::String::from_value(
|
|
ctx,
|
|
stack
|
|
.pop_front()
|
|
.ok_or_else(|| anyhow::Error::msg("No stream name argument given"))?,
|
|
)?
|
|
.as_bytes()
|
|
.to_vec(),
|
|
)?;
|
|
let (min_date, max_date) = match stack.len() {
|
|
2 => {
|
|
let min_date = String::from_utf8(
|
|
piccolo::String::from_value(ctx, stack.pop_front().unwrap())?
|
|
.as_bytes()
|
|
.to_vec(),
|
|
)?;
|
|
let max_date = String::from_utf8(
|
|
piccolo::String::from_value(ctx, stack.pop_front().unwrap())?
|
|
.as_bytes()
|
|
.to_vec(),
|
|
)?;
|
|
(min_date, max_date)
|
|
}
|
|
1 => {
|
|
let max_date = String::from_utf8(
|
|
piccolo::String::from_value(ctx, stack.pop_front().unwrap())?
|
|
.as_bytes()
|
|
.to_vec(),
|
|
)?;
|
|
("0000-00-00".to_owned(), max_date)
|
|
}
|
|
0 => ("0000-00-00".to_owned(), "9999-12-31".to_owned()),
|
|
_ => Err(anyhow::Error::msg(
|
|
"At most three arguments expected: stream min-date max-date",
|
|
))?,
|
|
};
|
|
|
|
start_deleting_logs(&global_memo, &frame, &stream, &min_date, &max_date);
|
|
Ok(CallbackReturn::Return)
|
|
})
|
|
}
|
|
|
|
pub(super) fn cmd_download_logs<'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 frame = try_unwrap_frame(
|
|
ctx,
|
|
&ctx.get_global::<Table>("info")?
|
|
.get::<&str, Value>(ctx, "current_frame")?,
|
|
)?;
|
|
let stream = String::from_utf8(
|
|
piccolo::String::from_value(
|
|
ctx,
|
|
stack
|
|
.pop_front()
|
|
.ok_or_else(|| anyhow::Error::msg("No stream name argument given"))?,
|
|
)?
|
|
.as_bytes()
|
|
.to_vec(),
|
|
)?;
|
|
let (min_date, max_date) = match stack.len() {
|
|
2 => {
|
|
let min_date = String::from_utf8(
|
|
piccolo::String::from_value(ctx, stack.pop_front().unwrap())?
|
|
.as_bytes()
|
|
.to_vec(),
|
|
)?;
|
|
let max_date = String::from_utf8(
|
|
piccolo::String::from_value(ctx, stack.pop_front().unwrap())?
|
|
.as_bytes()
|
|
.to_vec(),
|
|
)?;
|
|
(min_date, max_date)
|
|
}
|
|
1 => {
|
|
let max_date = String::from_utf8(
|
|
piccolo::String::from_value(ctx, stack.pop_front().unwrap())?
|
|
.as_bytes()
|
|
.to_vec(),
|
|
)?;
|
|
("0000-00-00".to_owned(), max_date)
|
|
}
|
|
0 => ("0000-00-00".to_owned(), "9999-12-31".to_owned()),
|
|
_ => Err(anyhow::Error::msg(
|
|
"At most three arguments expected: stream min-date max-date",
|
|
))?,
|
|
};
|
|
|
|
start_downloading_logs(&global_memo, &frame, &stream, &min_date, &max_date);
|
|
Ok(CallbackReturn::Return)
|
|
})
|
|
}
|
|
|
|
pub fn connect_default_mud(global_memo: &GlobalMemoCell) -> anyhow::Result<()> {
|
|
if let Some(window) = window() {
|
|
if let Ok(host) = window.location().host() {
|
|
let mut engine = global_memo.lua_engine.borrow_mut();
|
|
let engine: &mut LuaState = &mut engine;
|
|
engine.try_set_current_frame(&FrameId(1))?;
|
|
engine.interp.try_enter(|ctx| {
|
|
let connect_function: Function = ctx
|
|
.get_global::<Table>("commands")?
|
|
.get(ctx, "connect_mud")?;
|
|
ctx.fetch(&engine.exec).restart(
|
|
ctx,
|
|
connect_function,
|
|
("-trust", "default", format!("wss://{}/ws", &host)),
|
|
);
|
|
Ok(())
|
|
})?;
|
|
engine.interp.execute::<()>(&engine.exec)?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|