Implement NEW-ENVIRON / MNES
This commit is contained in:
parent
de00a4de66
commit
ad3fd0207e
@ -318,6 +318,7 @@ pub fn install_lua_globals(
|
|||||||
register_class_function!(mud_class_table, mudoutput_dont);
|
register_class_function!(mud_class_table, mudoutput_dont);
|
||||||
register_stateless_class_function!(mud_class_table, mudoutput_subnegotiation);
|
register_stateless_class_function!(mud_class_table, mudoutput_subnegotiation);
|
||||||
register_class_function!(mud_class_table, mudoutput_subnegotiation_termtype);
|
register_class_function!(mud_class_table, mudoutput_subnegotiation_termtype);
|
||||||
|
register_class_function!(mud_class_table, mudoutput_subnegotiation_environ);
|
||||||
register_class_function!(mud_class_table, mudinput_line);
|
register_class_function!(mud_class_table, mudinput_line);
|
||||||
register_class_function!(mud_class_table, "closed", mudclass_closed);
|
register_class_function!(mud_class_table, "closed", mudclass_closed);
|
||||||
register_stateless_class_function!(mud_class_table, "new", new_mud);
|
register_stateless_class_function!(mud_class_table, "new", new_mud);
|
||||||
@ -328,6 +329,11 @@ pub fn install_lua_globals(
|
|||||||
"local_termtype_enabled",
|
"local_termtype_enabled",
|
||||||
opt_enabled_noop
|
opt_enabled_noop
|
||||||
);
|
);
|
||||||
|
register_stateless_class_function!(
|
||||||
|
mud_class_table,
|
||||||
|
"local_environ_enabled",
|
||||||
|
opt_enabled_noop
|
||||||
|
);
|
||||||
register_stateless_class_function!(
|
register_stateless_class_function!(
|
||||||
mud_class_table,
|
mud_class_table,
|
||||||
"remote_eor_enabled",
|
"remote_eor_enabled",
|
||||||
|
@ -12,8 +12,6 @@ use piccolo::{
|
|||||||
SequenceReturn, StashedTable, StashedUserData, StashedValue, Table, UserData, Value, Variadic,
|
SequenceReturn, StashedTable, StashedUserData, StashedValue, Table, UserData, Value, Variadic,
|
||||||
};
|
};
|
||||||
use std::{rc::Rc, str};
|
use std::{rc::Rc, str};
|
||||||
use wasm_bindgen::JsValue;
|
|
||||||
use web_sys::console;
|
|
||||||
use yew::UseStateSetter;
|
use yew::UseStateSetter;
|
||||||
|
|
||||||
use super::call_checking_metatable;
|
use super::call_checking_metatable;
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
use anyhow::Error;
|
use core::str;
|
||||||
|
use std::mem::swap;
|
||||||
|
|
||||||
|
use anyhow::{bail, Error};
|
||||||
use gc_arena::{Gc, Rootable};
|
use gc_arena::{Gc, Rootable};
|
||||||
use piccolo::{
|
use piccolo::{
|
||||||
self, async_sequence, Callback, CallbackReturn, Context, FromValue, Function, IntoValue,
|
self, async_sequence, Callback, CallbackReturn, Context, FromValue, Function, IntoValue,
|
||||||
SequenceReturn, StashedTable, StashedUserData, StashedValue, Table, UserData, Value,
|
SequenceReturn, StashedTable, StashedUserData, StashedValue, Table, UserData, Value,
|
||||||
};
|
};
|
||||||
use telopt::{EOR_TELOPT, TERMTYPE_TELOPT};
|
use telopt::{ENVIRON_TELOPT, EOR_TELOPT, TERMTYPE_TELOPT};
|
||||||
use wasm_bindgen::JsValue;
|
use wasm_bindgen::JsValue;
|
||||||
use web_sys::{console, window};
|
use web_sys::{console, window};
|
||||||
use yew::UseStateSetter;
|
use yew::UseStateSetter;
|
||||||
@ -242,6 +245,7 @@ pub(super) fn connect_mud<'gc>(
|
|||||||
}
|
}
|
||||||
set_option_supported(ctx, &conntab, &TERMTYPE_TELOPT, &Side::Us);
|
set_option_supported(ctx, &conntab, &TERMTYPE_TELOPT, &Side::Us);
|
||||||
set_option_supported(ctx, &conntab, &EOR_TELOPT, &Side::Him);
|
set_option_supported(ctx, &conntab, &EOR_TELOPT, &Side::Him);
|
||||||
|
set_option_supported(ctx, &conntab, &ENVIRON_TELOPT, &Side::Us);
|
||||||
|
|
||||||
// Call conntab:new...
|
// Call conntab:new...
|
||||||
let seq = async_sequence(&ctx, |locals, mut seq| {
|
let seq = async_sequence(&ctx, |locals, mut seq| {
|
||||||
@ -698,11 +702,14 @@ pub(super) fn mudoutput_subnegotiation_termtype<'gc>(
|
|||||||
};
|
};
|
||||||
let supported_termtypes: Table = mud.get(ctx, "supported_termtypes")?;
|
let supported_termtypes: Table = mud.get(ctx, "supported_termtypes")?;
|
||||||
let optlen = supported_termtypes.length() as u64;
|
let optlen = supported_termtypes.length() as u64;
|
||||||
if negidx > optlen {
|
if negidx > optlen + 1 {
|
||||||
negidx = 1;
|
negidx = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let termtype: String = supported_termtypes.get(ctx, negidx as i64)?;
|
// We repeat the last one before cycling.
|
||||||
|
let effective_negidx = if negidx > optlen { optlen } else { negidx };
|
||||||
|
|
||||||
|
let termtype: String = supported_termtypes.get(ctx, effective_negidx as i64)?;
|
||||||
|
|
||||||
send_subnegotiation_if_allowed(
|
send_subnegotiation_if_allowed(
|
||||||
ctx,
|
ctx,
|
||||||
@ -725,6 +732,154 @@ pub(super) fn mudoutput_subnegotiation_termtype<'gc>(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_environ_sendcmd(mut input: &[u8]) -> anyhow::Result<Vec<Vec<u8>>> {
|
||||||
|
let mut args: Vec<Vec<u8>> = vec![];
|
||||||
|
let mut curstr: Vec<u8> = vec![];
|
||||||
|
|
||||||
|
const VAR: u8 = 0;
|
||||||
|
const ESC: u8 = 2;
|
||||||
|
const USERVAR: u8 = 3;
|
||||||
|
loop {
|
||||||
|
match input.first() {
|
||||||
|
None => {
|
||||||
|
if !curstr.is_empty() {
|
||||||
|
args.push(curstr);
|
||||||
|
}
|
||||||
|
return Ok(args);
|
||||||
|
}
|
||||||
|
Some(c) => {
|
||||||
|
input = &input[1..];
|
||||||
|
match *c {
|
||||||
|
ESC => match input.first() {
|
||||||
|
None => {
|
||||||
|
bail!("new-environ SEND command ended with escape, which is invalid");
|
||||||
|
}
|
||||||
|
Some(c) => {
|
||||||
|
input = &input[1..];
|
||||||
|
curstr.push(*c);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
VAR | USERVAR => {
|
||||||
|
if !curstr.is_empty() {
|
||||||
|
let mut laststr: Vec<u8> = vec![];
|
||||||
|
swap(&mut curstr, &mut laststr);
|
||||||
|
args.push(laststr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => curstr.push(*c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn escape_environ(input: &[u8]) -> Vec<u8> {
|
||||||
|
let mut buf: Vec<u8> = vec![];
|
||||||
|
const VAR: u8 = 0;
|
||||||
|
const VALUE: u8 = 1;
|
||||||
|
const ESC: u8 = 2;
|
||||||
|
const USERVAR: u8 = 3;
|
||||||
|
for c in input {
|
||||||
|
match *c {
|
||||||
|
VAR => {
|
||||||
|
buf.push(ESC);
|
||||||
|
buf.push(VAR);
|
||||||
|
}
|
||||||
|
VALUE => {
|
||||||
|
buf.push(ESC);
|
||||||
|
buf.push(VALUE);
|
||||||
|
}
|
||||||
|
ESC => {
|
||||||
|
buf.push(ESC);
|
||||||
|
buf.push(ESC);
|
||||||
|
}
|
||||||
|
USERVAR => {
|
||||||
|
buf.push(ESC);
|
||||||
|
buf.push(USERVAR);
|
||||||
|
}
|
||||||
|
c => buf.push(c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn mudoutput_subnegotiation_environ<'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>]>()?;
|
||||||
|
|
||||||
|
let environ: Table = mud.get(ctx, "environ_for_remote")?;
|
||||||
|
|
||||||
|
const IS_CMD: u8 = 0;
|
||||||
|
const SEND_CMD: u8 = 1;
|
||||||
|
|
||||||
|
const VAR: u8 = 0;
|
||||||
|
const VALUE: u8 = 1;
|
||||||
|
match cmd {
|
||||||
|
SEND_CMD => {
|
||||||
|
let mut requested_env = parse_environ_sendcmd(&msg)?;
|
||||||
|
console::log_1(&JsValue::from_str(&format!(
|
||||||
|
"Environment request of length: {}",
|
||||||
|
requested_env.len()
|
||||||
|
)));
|
||||||
|
if requested_env.is_empty() {
|
||||||
|
requested_env = environ
|
||||||
|
.iter()
|
||||||
|
.map(|(k, _v)| {
|
||||||
|
Ok::<_, anyhow::Error>(
|
||||||
|
piccolo::String::from_value(ctx, k)?.as_bytes().to_owned(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<anyhow::Result<Vec<_>>>()?;
|
||||||
|
}
|
||||||
|
console::log_1(&JsValue::from_str(&format!(
|
||||||
|
"After expansion, environment request of length: {}",
|
||||||
|
requested_env.len()
|
||||||
|
)));
|
||||||
|
|
||||||
|
let mut buf: Vec<u8> = vec![];
|
||||||
|
buf.push(IS_CMD);
|
||||||
|
for env in &requested_env {
|
||||||
|
if let Ok(envstr) = environ.get::<_, piccolo::String>(
|
||||||
|
ctx,
|
||||||
|
piccolo::String::from_slice(&ctx, env.as_slice()),
|
||||||
|
) {
|
||||||
|
buf.push(VAR);
|
||||||
|
buf.extend_from_slice(escape_environ(env).as_slice());
|
||||||
|
buf.push(VALUE);
|
||||||
|
buf.extend_from_slice(escape_environ(envstr.as_bytes()).as_slice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
send_subnegotiation_if_allowed(
|
||||||
|
ctx,
|
||||||
|
&mud,
|
||||||
|
&ENVIRON_TELOPT,
|
||||||
|
&Side::Us,
|
||||||
|
&mut MudWithMemo {
|
||||||
|
memo: global_memo.clone(),
|
||||||
|
mud: socket.clone(),
|
||||||
|
},
|
||||||
|
buf.as_slice(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(CallbackReturn::Return)
|
||||||
|
}
|
||||||
|
_ => Ok(CallbackReturn::Return),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn mudinput_line<'gc>(
|
pub(super) fn mudinput_line<'gc>(
|
||||||
ctx: Context<'gc>,
|
ctx: Context<'gc>,
|
||||||
_global_memo: &GlobalMemoCell,
|
_global_memo: &GlobalMemoCell,
|
||||||
@ -796,6 +951,14 @@ pub(super) fn new_mud<'gc>(ctx: Context<'gc>) -> Callback<'gc> {
|
|||||||
termtypes.set(ctx, 3_i64, "MTTS 815")?;
|
termtypes.set(ctx, 3_i64, "MTTS 815")?;
|
||||||
mud.set(ctx, ctx.intern_static(b"supported_termtypes"), termtypes)?;
|
mud.set(ctx, ctx.intern_static(b"supported_termtypes"), termtypes)?;
|
||||||
|
|
||||||
|
let environ: Table = Table::new(&ctx);
|
||||||
|
environ.set(ctx, "CLIENT_NAME", "worldwideportal")?;
|
||||||
|
environ.set(ctx, "CLIENT_VERSION", "0.0.1")?;
|
||||||
|
environ.set(ctx, "CHARSET", "UTF-8")?;
|
||||||
|
environ.set(ctx, "MTTS", "815")?;
|
||||||
|
environ.set(ctx, "TERMINAL_TYPE", "XTERM")?;
|
||||||
|
mud.set(ctx, ctx.intern_static(b"environ_for_remote"), environ)?;
|
||||||
|
|
||||||
let curr_frame: Value = ctx
|
let curr_frame: Value = ctx
|
||||||
.get_global::<Table>("info")?
|
.get_global::<Table>("info")?
|
||||||
.get(ctx, ctx.intern_static(b"current_frame"))?;
|
.get(ctx, ctx.intern_static(b"current_frame"))?;
|
||||||
|
@ -430,6 +430,7 @@ pub fn handle_incoming_dont<'gc, T: SendOptNeg>(
|
|||||||
pub const TERMTYPE_TELOPT: Telopt = Telopt(24);
|
pub const TERMTYPE_TELOPT: Telopt = Telopt(24);
|
||||||
pub const EOR_TELOPT: Telopt = Telopt(25);
|
pub const EOR_TELOPT: Telopt = Telopt(25);
|
||||||
pub const NAWS_TELOPT: Telopt = Telopt(31);
|
pub const NAWS_TELOPT: Telopt = Telopt(31);
|
||||||
|
pub const ENVIRON_TELOPT: Telopt = Telopt(39);
|
||||||
pub const GMCP_TELOPT: Telopt = Telopt(201);
|
pub const GMCP_TELOPT: Telopt = Telopt(201);
|
||||||
|
|
||||||
fn negotiate_option_on<'gc, T: SendOptNeg>(
|
fn negotiate_option_on<'gc, T: SendOptNeg>(
|
||||||
@ -533,6 +534,9 @@ pub fn configure_telopt_table<'gc>(ctx: Context<'gc>, table: &Table<'gc>) {
|
|||||||
table
|
table
|
||||||
.set(ctx, "termtype", TERMTYPE_TELOPT.0)
|
.set(ctx, "termtype", TERMTYPE_TELOPT.0)
|
||||||
.expect("Can't set TERMTYPE in telopt table");
|
.expect("Can't set TERMTYPE in telopt table");
|
||||||
|
table
|
||||||
|
.set(ctx, "environ", ENVIRON_TELOPT.0)
|
||||||
|
.expect("Can't set ENVIRON in telopt table");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
Loading…
Reference in New Issue
Block a user