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
This commit is contained in:
parent
6b961622aa
commit
3e3bcdc5da
|
@ -44,10 +44,42 @@ impl Component {
|
||||||
} else if v.get("text").is_some() {
|
} else if v.get("text").is_some() {
|
||||||
Component::Text(TextComponent::from_value(v, modifier))
|
Component::Text(TextComponent::from_value(v, modifier))
|
||||||
} else if v.get("translate").is_some() {
|
} else if v.get("translate").is_some() {
|
||||||
// TODO: translations
|
let translate_key = v.get("translate").unwrap().as_str().unwrap();
|
||||||
Component::Text(TextComponent::new(
|
if let Some(serde_json::Value::Array(args)) = v.get("with") {
|
||||||
v.get("translate").unwrap().as_str().unwrap(),
|
// 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 {
|
} else {
|
||||||
modifier.color = Some(Color::RGB(255, 0, 0));
|
modifier.color = Some(Color::RGB(255, 0, 0));
|
||||||
Component::Text(TextComponent {
|
Component::Text(TextComponent {
|
||||||
|
|
|
@ -290,6 +290,10 @@ impl Console {
|
||||||
self.active = !self.active;
|
self.active = !self.active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn activate(&mut self) {
|
||||||
|
self.active = true;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn tick(
|
pub fn tick(
|
||||||
&mut self,
|
&mut self,
|
||||||
ui_container: &mut ui::Container,
|
ui_container: &mut ui::Container,
|
||||||
|
|
|
@ -559,6 +559,12 @@ fn tick_all(
|
||||||
|
|
||||||
game.screen_sys
|
game.screen_sys
|
||||||
.tick(delta, &mut game.renderer, &mut ui_container);
|
.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
|
game.console
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
|
@ -25,6 +25,7 @@ use crate::types::Gamemode;
|
||||||
use crate::world;
|
use crate::world;
|
||||||
use crate::world::block;
|
use crate::world::block;
|
||||||
use cgmath::prelude::*;
|
use cgmath::prelude::*;
|
||||||
|
use instant::Instant;
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
use rand::{self, Rng};
|
use rand::{self, Rng};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -74,6 +75,7 @@ pub struct Server {
|
||||||
|
|
||||||
tick_timer: f64,
|
tick_timer: f64,
|
||||||
entity_tick_timer: f64,
|
entity_tick_timer: f64,
|
||||||
|
pub received_chat_at: Option<Instant>,
|
||||||
|
|
||||||
sun_model: Option<sun::SunModel>,
|
sun_model: Option<sun::SunModel>,
|
||||||
target_info: target::Info,
|
target_info: target::Info,
|
||||||
|
@ -491,6 +493,7 @@ impl Server {
|
||||||
|
|
||||||
tick_timer: 0.0,
|
tick_timer: 0.0,
|
||||||
entity_tick_timer: 0.0,
|
entity_tick_timer: 0.0,
|
||||||
|
received_chat_at: None,
|
||||||
sun_model: None,
|
sun_model: None,
|
||||||
|
|
||||||
target_info: target::Info::new(),
|
target_info: target::Info::new(),
|
||||||
|
@ -620,6 +623,9 @@ impl Server {
|
||||||
UpdateSign_u16 => on_sign_update_u16,
|
UpdateSign_u16 => on_sign_update_u16,
|
||||||
PlayerInfo => on_player_info,
|
PlayerInfo => on_player_info,
|
||||||
PlayerInfo_String => on_player_info_string,
|
PlayerInfo_String => on_player_info_string,
|
||||||
|
ServerMessage_NoPosition => on_servermessage_noposition,
|
||||||
|
ServerMessage_Position => on_servermessage_position,
|
||||||
|
ServerMessage_Sender => on_servermessage_sender,
|
||||||
Disconnect => on_disconnect,
|
Disconnect => on_disconnect,
|
||||||
// Entities
|
// Entities
|
||||||
EntityDestroy => on_entity_destroy,
|
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<u8>,
|
||||||
|
_sender: Option<protocol::UUID>,
|
||||||
|
) {
|
||||||
|
info!("Received chat message: {}", message);
|
||||||
|
self.received_chat_at = Some(Instant::now());
|
||||||
|
}
|
||||||
|
|
||||||
fn load_block_entities(&mut self, block_entities: Vec<Option<crate::nbt::NamedTag>>) {
|
fn load_block_entities(&mut self, block_entities: Vec<Option<crate::nbt::NamedTag>>) {
|
||||||
for optional_block_entity in block_entities {
|
for optional_block_entity in block_entities {
|
||||||
if let Some(block_entity) = optional_block_entity {
|
if let Some(block_entity) = optional_block_entity {
|
||||||
|
|
Loading…
Reference in New Issue