diff --git a/crates/console-host/src/main.rs b/crates/console-host/src/main.rs index dd06e1ec..e468757f 100644 --- a/crates/console-host/src/main.rs +++ b/crates/console-host/src/main.rs @@ -252,7 +252,7 @@ fn console_loop( printer .print( (match msg.event() { - moor_values::tasks::Event::TextNotify(s) => s, + moor_values::tasks::Event::Notify(s) => s, }) .to_string(), ) diff --git a/crates/kernel/src/builtins/bf_server.rs b/crates/kernel/src/builtins/bf_server.rs index cfdce958..17971bff 100644 --- a/crates/kernel/src/builtins/bf_server.rs +++ b/crates/kernel/src/builtins/bf_server.rs @@ -35,6 +35,7 @@ use crate::builtins::BfRet::{Ret, VmInstr}; use crate::builtins::{world_state_bf_err, BfCallState, BfErr, BfRet, BuiltinFunction}; use crate::vm::ExecutionResult; use moor_values::tasks::TaskId; +use moor_values::var::VarType::TYPE_STR; fn bf_noop(bf_args: &mut BfCallState<'_>) -> Result { error!( @@ -57,10 +58,12 @@ fn bf_notify(bf_args: &mut BfCallState<'_>) -> Result { let Variant::Obj(player) = player else { return Err(BfErr::Code(E_TYPE)); }; - let msg = bf_args.args[1].variant(); - let Variant::Str(msg) = msg else { + + // If in "strict" mode `notify` can only send text. + // Otherwise, it can send any value, and it's up to the host/client to interpret it. + if bf_args.config.strict_mode && bf_args.args[1].type_id() != TYPE_STR { return Err(BfErr::Code(E_TYPE)); - }; + } // If player is not the calling task perms, or a caller is not a wizard, raise E_PERM. bf_args @@ -69,7 +72,7 @@ fn bf_notify(bf_args: &mut BfCallState<'_>) -> Result { .check_obj_owner_perms(*player) .map_err(world_state_bf_err)?; - let event = NarrativeEvent::notify_text(bf_args.exec_state.caller(), msg.to_string()); + let event = NarrativeEvent::notify(bf_args.exec_state.caller(), bf_args.args[1].clone()); bf_args.task_scheduler_client.notify(*player, event); // MOO docs say this should return none, but in reality it returns 1? diff --git a/crates/kernel/src/builtins/mod.rs b/crates/kernel/src/builtins/mod.rs index cafade89..ab3136bd 100644 --- a/crates/kernel/src/builtins/mod.rs +++ b/crates/kernel/src/builtins/mod.rs @@ -33,6 +33,7 @@ use crate::builtins::bf_server::{register_bf_server, BfNoop}; use crate::builtins::bf_strings::register_bf_strings; use crate::builtins::bf_values::register_bf_values; use crate::builtins::bf_verbs::register_bf_verbs; +use crate::config::Config; use crate::tasks::sessions::Session; use crate::tasks::task_scheduler_client::TaskSchedulerClient; use crate::vm::activation::{BfFrame, Frame}; @@ -96,6 +97,8 @@ pub struct BfCallState<'a> { pub(crate) session: Arc, /// For sending messages up to the scheduler pub(crate) task_scheduler_client: TaskSchedulerClient, + /// Config + pub(crate) config: Arc, } impl BfCallState<'_> { diff --git a/crates/kernel/src/tasks/task.rs b/crates/kernel/src/tasks/task.rs index d165f518..8b4a2d1e 100644 --- a/crates/kernel/src/tasks/task.rs +++ b/crates/kernel/src/tasks/task.rs @@ -857,7 +857,7 @@ mod tests { }; assert_eq!(player, SYSTEM_OBJECT); assert_eq!(event.author(), SYSTEM_OBJECT); - assert_eq!(event.event, Event::TextNotify("12345".to_string())); + assert_eq!(event.event, Event::Notify(v_str("12345"))); // Also scheduler should have received a TaskSuccess message. let (task_id, msg) = control_receiver.recv().unwrap(); diff --git a/crates/kernel/src/vm/vm_call.rs b/crates/kernel/src/vm/vm_call.rs index 121dd5ab..a84e5610 100644 --- a/crates/kernel/src/vm/vm_call.rs +++ b/crates/kernel/src/vm/vm_call.rs @@ -260,6 +260,7 @@ impl VMExecState { // TODO: avoid copy here by using List inside BfCallState args: args.iter().collect(), task_scheduler_client: exec_args.task_scheduler_client.clone(), + config: exec_args.config.clone(), }; let call_results = match bf.call(&mut bf_args) { @@ -308,6 +309,7 @@ impl VMExecState { // TODO: avoid copy here by using List inside BfCallState args: args.iter().collect(), task_scheduler_client: exec_args.task_scheduler_client.clone(), + config: exec_args.config.clone(), }; match bf.call(&mut bf_args) { diff --git a/crates/telnet-host/src/telnet.rs b/crates/telnet-host/src/telnet.rs index 8071adf7..21ad1d22 100644 --- a/crates/telnet-host/src/telnet.rs +++ b/crates/telnet-host/src/telnet.rs @@ -32,7 +32,7 @@ use uuid::Uuid; use moor_values::tasks::{AbortLimitReason, CommandError, Event, SchedulerError, VerbProgramError}; use moor_values::util::parse_into_words; -use moor_values::var::Objid; +use moor_values::var::{Objid, Variant}; use rpc_async_client::pubsub_client::{broadcast_recv, events_recv}; use rpc_async_client::rpc_client::RpcSendClient; use rpc_common::RpcRequest::ConnectionEstablish; @@ -142,8 +142,15 @@ impl TelnetConnection { } ConnectionEvent::Narrative(_author, event) => { let msg = event.event(); - let Event::TextNotify(msg_text) = msg; - self.write.send(msg_text).await.with_context(|| "Unable to send message to client")?; + let Event::Notify(msg) = msg; + + // Strings output as text lines to the client, otherwise send the + // literal form (for e.g. lists, objrefs, etc) + if let Variant::Str(msg_text) = msg.variant() { + self.write.send(msg_text.to_string()).await.with_context(|| "Unable to send message to client")?; + } else { + self.write.send(msg.to_literal()).await.with_context(|| "Unable to send message to client")?; + } } ConnectionEvent::RequestInput(_request_id) => { bail!("RequestInput before login"); @@ -307,8 +314,15 @@ impl TelnetConnection { } ConnectionEvent::Narrative(_author, event) => { let msg = event.event(); - let Event::TextNotify(msg_text) = msg; - self.write.send(msg_text).await.with_context(|| "Unable to send message to client")?; + let Event::Notify(msg) = msg; + + // Strings output as text lines to the client, otherwise send the + // literal form (for e.g. lists, objrefs, etc) + if let Variant::Str(msg_text) = msg.variant() { + self.write.send(msg_text.to_string()).await.with_context(|| "Unable to send message to client")?; + } else { + self.write.send(msg.to_literal()).await.with_context(|| "Unable to send message to client")?; + } } ConnectionEvent::RequestInput(request_id) => { // Server is requesting that the next line of input get sent through as a response to this request. diff --git a/crates/values/src/tasks/events.rs b/crates/values/src/tasks/events.rs index 9f59675d..e35c95cb 100644 --- a/crates/values/src/tasks/events.rs +++ b/crates/values/src/tasks/events.rs @@ -12,7 +12,7 @@ // this program. If not, see . // -use crate::var::Objid; +use crate::var::{Objid, Var}; use bincode::{Decode, Encode}; use std::time::SystemTime; @@ -32,7 +32,7 @@ pub struct NarrativeEvent { #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] pub enum Event { /// The typical "something happened" descriptive event. - TextNotify(String), + Notify(Var), // TODO: Other Event types on Session stream // other events that might happen here would be things like (local) "object moved" or "object // created." @@ -40,11 +40,11 @@ pub enum Event { impl NarrativeEvent { #[must_use] - pub fn notify_text(author: Objid, event: String) -> Self { + pub fn notify(author: Objid, value: Var) -> Self { Self { timestamp: SystemTime::now(), author, - event: Event::TextNotify(event), + event: Event::Notify(value), } } diff --git a/crates/web-host/src/host/ws_connection.rs b/crates/web-host/src/host/ws_connection.rs index 261f64ec..760343fc 100644 --- a/crates/web-host/src/host/ws_connection.rs +++ b/crates/web-host/src/host/ws_connection.rs @@ -12,7 +12,7 @@ // this program. If not, see . // -use crate::host::serialize_var; +use crate::host::{serialize_var, var_as_json}; use axum::extract::ws::{Message, WebSocket}; use futures_util::stream::SplitSink; use futures_util::{SinkExt, StreamExt}; @@ -26,6 +26,7 @@ use rpc_common::ConnectionEvent; use rpc_common::{ AuthToken, ClientToken, ConnectType, RpcRequest, RpcRequestError, RpcResponse, RpcResult, }; +use serde_json::Value; use std::net::SocketAddr; use std::time::SystemTime; use tmq::subscribe::Subscribe; @@ -51,7 +52,7 @@ pub struct NarrativeOutput { #[serde(skip_serializing_if = "Option::is_none")] system_message: Option, #[serde(skip_serializing_if = "Option::is_none")] - message: Option, + message: Option, server_time: SystemTime, } @@ -124,7 +125,7 @@ impl WebSocketConnection { origin_player: author.0, system_message: None, message: Some(match msg { - Event::TextNotify(msg) => msg, + Event::Notify(msg) => var_as_json(&msg), }), server_time: event.timestamp(), }).await;