From 3e3bcdc5daf50258cb8242d8f4890de800019431 Mon Sep 17 00:00:00 2001 From: iceiix <43691553+iceiix@users.noreply.github.com> Date: Sun, 24 Jan 2021 17:41:16 -0800 Subject: [PATCH] protocol, console, server: receive chat messages (#479) Implements support for receiving chat messages from the server, the first step towards #63 chat support. Long way to go, but is a start. * server: handle ServerMessage_* packets, on_servermessage * format: get the with arguments of translations * format: hardcode chat types * format: support bare strings, used in chat.type.text * format: use extra text if present, fixes missing username on 1.16.5 * console/main/server: activate on receiving chat messages --- protocol/src/format.rs | 40 ++++++++++++++++++++++++++++++++++++---- src/console/mod.rs | 4 ++++ src/main.rs | 6 ++++++ src/server/mod.rs | 31 +++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 4 deletions(-) diff --git a/protocol/src/format.rs b/protocol/src/format.rs index bfa0887..36744cd 100644 --- a/protocol/src/format.rs +++ b/protocol/src/format.rs @@ -44,10 +44,42 @@ impl Component { } else if v.get("text").is_some() { Component::Text(TextComponent::from_value(v, modifier)) } else if v.get("translate").is_some() { - // TODO: translations - Component::Text(TextComponent::new( - v.get("translate").unwrap().as_str().unwrap(), - )) + let translate_key = v.get("translate").unwrap().as_str().unwrap(); + if let Some(serde_json::Value::Array(args)) = v.get("with") { + // TODO: recursively build components, avoid throwing away all but "text" + let text_args: Vec<&str> = args + .iter() + .map(|v| { + if let serde_json::Value::Object(obj) = v { + // Usernames might be in "extra":["text":"foo"] and "text":"" empty for + // some reason; use extra instead if present TODO: use both + if let Some(serde_json::Value::Array(extra)) = obj.get("extra") { + if let Some(item) = extra.get(0) { + if let Some(text) = item.get("text") { + return text.as_str().unwrap(); + } + } + } + + obj.get("text").unwrap().as_str().unwrap() + } else { + v.as_str().unwrap() + } + }) + .collect(); + // TODO: translations, https://wiki.vg/Chat#Translation_component + Component::Text(TextComponent::new( + match translate_key { + "chat.type.text" => format!("<{}> {}", text_args[0], text_args[1]), + "chat.type.announcement" => format!("[{}] {}", text_args[0], text_args[1]), + _ => format!("unhandled: {}", translate_key), + } + .as_str(), + )) + } else { + // TODO + Component::Text(TextComponent::new(translate_key)) + } } else { modifier.color = Some(Color::RGB(255, 0, 0)); Component::Text(TextComponent { diff --git a/src/console/mod.rs b/src/console/mod.rs index aed1f42..aab12b8 100644 --- a/src/console/mod.rs +++ b/src/console/mod.rs @@ -290,6 +290,10 @@ impl Console { self.active = !self.active; } + pub fn activate(&mut self) { + self.active = true; + } + pub fn tick( &mut self, ui_container: &mut ui::Container, diff --git a/src/main.rs b/src/main.rs index 115de2e..76069b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -559,6 +559,12 @@ fn tick_all( game.screen_sys .tick(delta, &mut game.renderer, &mut ui_container); + if let Some(received_chat_at) = game.server.received_chat_at { + if Instant::now().duration_since(received_chat_at).as_secs() < 5 { + game.console.lock().unwrap().activate() + // TODO: automatically deactivate the console after inactivity + } + } game.console .lock() .unwrap() diff --git a/src/server/mod.rs b/src/server/mod.rs index 3fbee91..ca27fe1 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -25,6 +25,7 @@ use crate::types::Gamemode; use crate::world; use crate::world::block; use cgmath::prelude::*; +use instant::Instant; use log::{debug, error, info, warn}; use rand::{self, Rng}; use std::collections::HashMap; @@ -74,6 +75,7 @@ pub struct Server { tick_timer: f64, entity_tick_timer: f64, + pub received_chat_at: Option, sun_model: Option, target_info: target::Info, @@ -491,6 +493,7 @@ impl Server { tick_timer: 0.0, entity_tick_timer: 0.0, + received_chat_at: None, sun_model: None, target_info: target::Info::new(), @@ -620,6 +623,9 @@ impl Server { UpdateSign_u16 => on_sign_update_u16, PlayerInfo => on_player_info, PlayerInfo_String => on_player_info_string, + ServerMessage_NoPosition => on_servermessage_noposition, + ServerMessage_Position => on_servermessage_position, + ServerMessage_Sender => on_servermessage_sender, Disconnect => on_disconnect, // Entities EntityDestroy => on_entity_destroy, @@ -1851,6 +1857,31 @@ impl Server { } } + fn on_servermessage_noposition( + &mut self, + m: packet::play::clientbound::ServerMessage_NoPosition, + ) { + self.on_servermessage(&m.message, None, None); + } + + fn on_servermessage_position(&mut self, m: packet::play::clientbound::ServerMessage_Position) { + self.on_servermessage(&m.message, Some(m.position), None); + } + + fn on_servermessage_sender(&mut self, m: packet::play::clientbound::ServerMessage_Sender) { + self.on_servermessage(&m.message, Some(m.position), Some(m.sender)); + } + + fn on_servermessage( + &mut self, + message: &format::Component, + _position: Option, + _sender: Option, + ) { + info!("Received chat message: {}", message); + self.received_chat_at = Some(Instant::now()); + } + fn load_block_entities(&mut self, block_entities: Vec>) { for optional_block_entity in block_entities { if let Some(block_entity) = optional_block_entity {