mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-09 02:07:04 +08:00
feat(chat): implement client-server chat messaging
Add protobuf definition for ChatMsg, new packet ID CHAT_MSG (4001), and handle sending/receiving chat messages on both client and server side. Wire up UI components (ChatBox, TextField) to allow message input and display, including a message queue in ClientWorld for thread-safe processing. Also add default initialization for ChatMessage::time.
This commit is contained in:
@@ -6,6 +6,6 @@ namespace Cubed {
|
|||||||
struct ChatMessage {
|
struct ChatMessage {
|
||||||
std::string player;
|
std::string player;
|
||||||
std::string text;
|
std::string text;
|
||||||
uint64_t time;
|
uint64_t time = 0;
|
||||||
};
|
};
|
||||||
} // namespace Cubed
|
} // namespace Cubed
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "Cubed/audio/audio_engine.hpp"
|
#include "Cubed/audio/audio_engine.hpp"
|
||||||
#include "Cubed/config.hpp"
|
#include "Cubed/config.hpp"
|
||||||
#include "Cubed/gameplay/block.hpp"
|
#include "Cubed/gameplay/block.hpp"
|
||||||
|
#include "Cubed/gameplay/chat_message.hpp"
|
||||||
#include "Cubed/gameplay/chunk_pos.hpp"
|
#include "Cubed/gameplay/chunk_pos.hpp"
|
||||||
#include "Cubed/gameplay/client_chunk.hpp"
|
#include "Cubed/gameplay/client_chunk.hpp"
|
||||||
#include "Cubed/gameplay/client_player.hpp"
|
#include "Cubed/gameplay/client_player.hpp"
|
||||||
@@ -102,6 +103,10 @@ public:
|
|||||||
Config& get_config();
|
Config& get_config();
|
||||||
WorldScene& world_scene();
|
WorldScene& world_scene();
|
||||||
void set_direct_exit();
|
void set_direct_exit();
|
||||||
|
|
||||||
|
void receive_chat_message(ChatMsg& msg);
|
||||||
|
void send_chat_message(ChatMessage& message);
|
||||||
|
|
||||||
template <typename Fn>
|
template <typename Fn>
|
||||||
void register_ticktimer(std::string_view id, TickType threshold, Fn&& f) {
|
void register_ticktimer(std::string_view id, TickType threshold, Fn&& f) {
|
||||||
m_ticktimers.emplace(
|
m_ticktimers.emplace(
|
||||||
@@ -147,6 +152,7 @@ private:
|
|||||||
tbb::concurrent_queue<std::unique_ptr<ClientChunk>> m_pending_upload_queue;
|
tbb::concurrent_queue<std::unique_ptr<ClientChunk>> m_pending_upload_queue;
|
||||||
tbb::concurrent_queue<ChunkPos> m_dirty_chunk_queue;
|
tbb::concurrent_queue<ChunkPos> m_dirty_chunk_queue;
|
||||||
tbb::concurrent_queue<PendingSound> m_pending_sound;
|
tbb::concurrent_queue<PendingSound> m_pending_sound;
|
||||||
|
tbb::concurrent_queue<ChatMessage> m_message_queue;
|
||||||
|
|
||||||
std::deque<ChunkPos> m_dirty_queue;
|
std::deque<ChunkPos> m_dirty_queue;
|
||||||
std::vector<const ChunkRenderSnapshot*> m_render_snapshots;
|
std::vector<const ChunkRenderSnapshot*> m_render_snapshots;
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ enum class PacketEnum : uint16_t {
|
|||||||
BLOCK_CHANGE_RSP = 3004,
|
BLOCK_CHANGE_RSP = 3004,
|
||||||
S2C_CLEAR_ALL_CHUNKS = 3005,
|
S2C_CLEAR_ALL_CHUNKS = 3005,
|
||||||
UPDATE_TIME = 3006,
|
UPDATE_TIME = 3006,
|
||||||
|
CHAT_MSG = 4001,
|
||||||
PING = 9001,
|
PING = 9001,
|
||||||
PONG = 9002
|
PONG = 9002
|
||||||
|
|
||||||
@@ -121,6 +122,9 @@ template <> constexpr uint16_t get_packet_id<Pong>() {
|
|||||||
template <> constexpr uint16_t get_packet_id<PlayerWaterSound>() {
|
template <> constexpr uint16_t get_packet_id<PlayerWaterSound>() {
|
||||||
return std::to_underlying(PacketEnum::PLAYER_WATER_SOUND);
|
return std::to_underlying(PacketEnum::PLAYER_WATER_SOUND);
|
||||||
}
|
}
|
||||||
|
template <> constexpr uint16_t get_packet_id<ChatMsg>() {
|
||||||
|
return std::to_underlying(PacketEnum::CHAT_MSG);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
requires std::derived_from<T, google::protobuf::Message>
|
requires std::derived_from<T, google::protobuf::Message>
|
||||||
|
|||||||
@@ -80,6 +80,8 @@ public:
|
|||||||
void handle_chunk_req(int task_id, const std::string& uuid, ChunkPos pos);
|
void handle_chunk_req(int task_id, const std::string& uuid, ChunkPos pos);
|
||||||
void handle_block_change(const BlockChangeReq& req);
|
void handle_block_change(const BlockChangeReq& req);
|
||||||
|
|
||||||
|
void handle_chat_message(ChatMsg& msg);
|
||||||
|
|
||||||
int chunk_size() const;
|
int chunk_size() const;
|
||||||
template <typename Fn>
|
template <typename Fn>
|
||||||
void register_timer(std::string_view id, TickType threshold, Fn&& f) {
|
void register_timer(std::string_view id, TickType threshold, Fn&& f) {
|
||||||
@@ -189,5 +191,7 @@ private:
|
|||||||
std::atomic<std::shared_ptr<PriorityThreadPool>>& thread_pool,
|
std::atomic<std::shared_ptr<PriorityThreadPool>>& thread_pool,
|
||||||
int threads);
|
int threads);
|
||||||
void send_server_stop();
|
void send_server_stop();
|
||||||
|
|
||||||
|
void boardcast_message(const std::string& name, const std::string& message);
|
||||||
};
|
};
|
||||||
} // namespace Cubed
|
} // namespace Cubed
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ public:
|
|||||||
void set_error(std::string_view error);
|
void set_error(std::string_view error);
|
||||||
void set_mouse(bool pause);
|
void set_mouse(bool pause);
|
||||||
void set_chatting(bool chatting);
|
void set_chatting(bool chatting);
|
||||||
|
// Not thread safe
|
||||||
|
void handle_chat_message(ChatMessage& message);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool handle_mouse_move_event(const MouseMoveEvent& e) override;
|
bool handle_mouse_move_event(const MouseMoveEvent& e) override;
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ public:
|
|||||||
ChatBox& set_width(float width);
|
ChatBox& set_width(float width);
|
||||||
ChatBox& set_show_lines(int lines);
|
ChatBox& set_show_lines(int lines);
|
||||||
ChatBox& set_spacing(float spacing);
|
ChatBox& set_spacing(float spacing);
|
||||||
|
ChatBox& clear_input();
|
||||||
|
std::string& get_input_text();
|
||||||
float width() const override;
|
float width() const override;
|
||||||
float height() const override;
|
float height() const override;
|
||||||
void set_d_image(TextureManager& m);
|
void set_d_image(TextureManager& m);
|
||||||
|
|||||||
@@ -28,13 +28,14 @@ public:
|
|||||||
TextField& set_auto_scale(bool auto_scale);
|
TextField& set_auto_scale(bool auto_scale);
|
||||||
TextField& set_app(App* app);
|
TextField& set_app(App* app);
|
||||||
TextField& set_typing(bool typing);
|
TextField& set_typing(bool typing);
|
||||||
|
TextField& clear_input();
|
||||||
bool handle_mouse_move_event(const MouseMoveEvent& e) override;
|
bool handle_mouse_move_event(const MouseMoveEvent& e) override;
|
||||||
bool handle_mouse_button_event(const MouseButtonEvent& e) override;
|
bool handle_mouse_button_event(const MouseButtonEvent& e) override;
|
||||||
bool handle_text_input_event(const TextInputEvent& e) override;
|
bool handle_text_input_event(const TextInputEvent& e) override;
|
||||||
bool handle_key_event(const KeyEvent& e) override;
|
bool handle_key_event(const KeyEvent& e) override;
|
||||||
bool handle_window_resize_event(const WindowResizeEvent& e) override;
|
bool handle_window_resize_event(const WindowResizeEvent& e) override;
|
||||||
const std::string& input_text() const;
|
const std::string& input_text() const;
|
||||||
|
std::string& input_text();
|
||||||
template <typename F> TextField& set_on_finish(F&& f) {
|
template <typename F> TextField& set_on_finish(F&& f) {
|
||||||
m_on_finished = std::forward<F>(f);
|
m_on_finished = std::forward<F>(f);
|
||||||
return *this;
|
return *this;
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ public:
|
|||||||
|
|
||||||
void set_chatting(bool chantting);
|
void set_chatting(bool chantting);
|
||||||
|
|
||||||
|
void add_chat_message(ChatMessage& message);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool handle_key_event(const KeyEvent& e) override;
|
bool handle_key_event(const KeyEvent& e) override;
|
||||||
WorldScene& m_scene;
|
WorldScene& m_scene;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "Cubed/gameplay/packet.hpp"
|
#include "Cubed/gameplay/packet.hpp"
|
||||||
#include "Cubed/scene/world_scene.hpp"
|
#include "Cubed/scene/world_scene.hpp"
|
||||||
#include "Cubed/tools/math_tools.hpp"
|
#include "Cubed/tools/math_tools.hpp"
|
||||||
|
#include "Cubed/tools/time_tools.hpp"
|
||||||
|
|
||||||
#include <absl/container/inlined_vector.h>
|
#include <absl/container/inlined_vector.h>
|
||||||
#include <numbers>
|
#include <numbers>
|
||||||
@@ -750,6 +751,17 @@ void ClientWorld::request_exit() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClientWorld::receive_chat_message(ChatMsg& msg) {
|
||||||
|
m_message_queue.emplace(msg.name(), msg.msg(), Tools::get_time_ticks());
|
||||||
|
}
|
||||||
|
void ClientWorld::send_chat_message(ChatMessage& message) {
|
||||||
|
Arena arena;
|
||||||
|
auto msg = Arena::Create<ChatMsg>(&arena);
|
||||||
|
msg->set_name(message.player);
|
||||||
|
msg->set_msg(message.text);
|
||||||
|
m_client->send(make_packet(*msg));
|
||||||
|
}
|
||||||
|
|
||||||
void ClientWorld::update(float delta_time) {
|
void ClientWorld::update(float delta_time) {
|
||||||
|
|
||||||
m_player.update(delta_time);
|
m_player.update(delta_time);
|
||||||
@@ -916,6 +928,10 @@ void ClientWorld::update(float delta_time) {
|
|||||||
for (auto& [pos, timer] : m_timers) {
|
for (auto& [pos, timer] : m_timers) {
|
||||||
timer.update(delta_time);
|
timer.update(delta_time);
|
||||||
}
|
}
|
||||||
|
ChatMessage message;
|
||||||
|
while (m_message_queue.try_pop(message)) {
|
||||||
|
m_world_scene.handle_chat_message(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ClientWorld::handle_event(const Event& e) {
|
bool ClientWorld::handle_event(const Event& e) {
|
||||||
|
|||||||
@@ -135,6 +135,12 @@ asio::awaitable<void> NetworkClient::read_loop() {
|
|||||||
m_world.receive_player_water_sound(*rsp);
|
m_world.receive_player_water_sound(*rsp);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
case std::to_underlying(PacketEnum::CHAT_MSG): {
|
||||||
|
auto* msg = Arena::Create<ChatMsg>(&arena);
|
||||||
|
if (decode_packet(*msg, body_data, header)) {
|
||||||
|
m_world.receive_chat_message(*msg);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (const asio::system_error& e) {
|
} catch (const asio::system_error& e) {
|
||||||
|
|||||||
@@ -753,6 +753,16 @@ void ServerWorld::handle_chunk_req(int task_id, const std::string& uuid,
|
|||||||
[task_id, uuid, pos, this]() { send_chunk(task_id, uuid, pos); });
|
[task_id, uuid, pos, this]() { send_chunk(task_id, uuid, pos); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ServerWorld::handle_chat_message(ChatMsg& msg) {
|
||||||
|
|
||||||
|
std::string name = msg.name();
|
||||||
|
std::string message = msg.msg();
|
||||||
|
auto pool = m_net_thread_pool.load();
|
||||||
|
pool->enqueue([this, player = std::move(name), m = std::move(message)]() {
|
||||||
|
boardcast_message(player, m);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void ServerWorld::handle_block_change(const BlockChangeReq& req) {
|
void ServerWorld::handle_block_change(const BlockChangeReq& req) {
|
||||||
float x = std::floor(req.pos().x());
|
float x = std::floor(req.pos().x());
|
||||||
float y = std::floor(req.pos().y());
|
float y = std::floor(req.pos().y());
|
||||||
@@ -861,6 +871,28 @@ void ServerWorld::send_server_stop() {
|
|||||||
Logger::info("Send Server Mesaage Success");
|
Logger::info("Send Server Mesaage Success");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ServerWorld::boardcast_message(const std::string& name,
|
||||||
|
const std::string& message) {
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Session>> m_session;
|
||||||
|
{
|
||||||
|
std::shared_lock lock(m_player_mutex);
|
||||||
|
for (auto& [_, p] : m_players) {
|
||||||
|
m_session.emplace_back(p.get_session());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Arena arena;
|
||||||
|
auto msg = Arena::Create<ChatMsg>(&arena);
|
||||||
|
|
||||||
|
msg->set_msg(message);
|
||||||
|
msg->set_name(name);
|
||||||
|
|
||||||
|
for (auto& s : m_session) {
|
||||||
|
s->send(make_packet(*msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int ServerWorld::chunk_load_style() const {
|
int ServerWorld::chunk_load_style() const {
|
||||||
return std::to_underlying(m_chunk_load_style.load());
|
return std::to_underlying(m_chunk_load_style.load());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,6 +99,12 @@ asio::awaitable<void> Session::read_loop() {
|
|||||||
m_server_world.sync_player_water_sound(*req);
|
m_server_world.sync_player_water_sound(*req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (cmd_id == std::to_underlying(PacketEnum::CHAT_MSG)) {
|
||||||
|
auto* msg = Arena::Create<ChatMsg>(&arena);
|
||||||
|
if (decode_packet(*msg, body_data, header)) {
|
||||||
|
m_server_world.handle_chat_message(*msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (const asio::system_error& e) {
|
} catch (const asio::system_error& e) {
|
||||||
auto ec = e.code();
|
auto ec = e.code();
|
||||||
|
|||||||
6
src/proto/chat/chat.proto
Normal file
6
src/proto/chat/chat.proto
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
message ChatMsg {
|
||||||
|
string name = 1;
|
||||||
|
string msg = 2;
|
||||||
|
}
|
||||||
@@ -10,3 +10,4 @@ import "system/pong.proto";
|
|||||||
import "world/chunk_data.proto";
|
import "world/chunk_data.proto";
|
||||||
import "world/block_change.proto";
|
import "world/block_change.proto";
|
||||||
import "world/time.proto";
|
import "world/time.proto";
|
||||||
|
import "chat/chat.proto";
|
||||||
@@ -334,6 +334,11 @@ void WorldScene::set_chatting(bool chatting) {
|
|||||||
Logger::info("World Scene Chatting {}", chatting);
|
Logger::info("World Scene Chatting {}", chatting);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Not thread safe
|
||||||
|
void WorldScene::handle_chat_message(ChatMessage& message) {
|
||||||
|
m_hud_ui.add_chat_message(message);
|
||||||
|
}
|
||||||
|
|
||||||
void WorldScene::set_error(std::string_view error) {
|
void WorldScene::set_error(std::string_view error) {
|
||||||
Logger::error("WorldScene Error Set {}", error);
|
Logger::error("WorldScene Error Set {}", error);
|
||||||
m_error_ui.set_error(error);
|
m_error_ui.set_error(error);
|
||||||
|
|||||||
@@ -52,6 +52,12 @@ ChatBox& ChatBox::set_spacing(float spacing) {
|
|||||||
m_spacing = spacing;
|
m_spacing = spacing;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
ChatBox& ChatBox::clear_input() {
|
||||||
|
m_text_field->clear_input();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
std::string& ChatBox::get_input_text() { return m_text_field->input_text(); }
|
||||||
|
|
||||||
float ChatBox::width() const { return m_width * m_scale; }
|
float ChatBox::width() const { return m_width * m_scale; }
|
||||||
float ChatBox::height() const {
|
float ChatBox::height() const {
|
||||||
// Height is dynamically calculated, no scaling needed
|
// Height is dynamically calculated, no scaling needed
|
||||||
|
|||||||
@@ -79,18 +79,15 @@ void TextField::on_update(float dt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TextField::update_show_text() {
|
void TextField::update_show_text() {
|
||||||
if (m_input_text.empty()) {
|
|
||||||
if (!m_typing) {
|
|
||||||
m_foreground->set_text(m_show_text);
|
|
||||||
m_foreground->set_color(Color::GRAY);
|
|
||||||
} else {
|
|
||||||
m_foreground->set_text(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!m_typing) {
|
||||||
|
m_foreground->set_text(m_show_text);
|
||||||
|
m_foreground->set_color(Color::GRAY);
|
||||||
} else {
|
} else {
|
||||||
m_foreground->set_text(m_input_text);
|
m_foreground->set_text(m_input_text);
|
||||||
m_foreground->set_color(Color::WHITE);
|
m_foreground->set_color(Color::WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
update_text_scale();
|
update_text_scale();
|
||||||
update_input_area();
|
update_input_area();
|
||||||
m_cursor->set_offset({13.0f + m_foreground->width(), 0.0f});
|
m_cursor->set_offset({13.0f + m_foreground->width(), 0.0f});
|
||||||
@@ -162,6 +159,12 @@ TextField& TextField::set_typing(bool typing) {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextField& TextField::clear_input() {
|
||||||
|
m_input_text.clear();
|
||||||
|
update_show_text();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
void TextField::start_typing() {
|
void TextField::start_typing() {
|
||||||
if (m_app) {
|
if (m_app) {
|
||||||
m_app->start_text_input();
|
m_app->start_text_input();
|
||||||
@@ -178,7 +181,7 @@ void TextField::stop_typing() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const std::string& TextField::input_text() const { return m_input_text; }
|
const std::string& TextField::input_text() const { return m_input_text; }
|
||||||
|
std::string& TextField::input_text() { return m_input_text; }
|
||||||
bool TextField::handle_mouse_move_event(const MouseMoveEvent& e) {
|
bool TextField::handle_mouse_move_event(const MouseMoveEvent& e) {
|
||||||
auto p = pos();
|
auto p = pos();
|
||||||
if (e.xpos >= p.x && e.xpos <= p.x + width() && e.ypos >= p.y &&
|
if (e.xpos >= p.x && e.xpos <= p.x + width() && e.ypos >= p.y &&
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
|
|
||||||
#include "Cubed/ui/ui_vertex_data.hpp"
|
#include "Cubed/ui/ui_vertex_data.hpp"
|
||||||
|
|
||||||
#include "Cubed/tools/cubed_assert.hpp"
|
|
||||||
#include "Cubed/tools/log.hpp"
|
|
||||||
namespace Cubed {
|
namespace Cubed {
|
||||||
UIVertexData::UIVertexData() {}
|
UIVertexData::UIVertexData() {}
|
||||||
UIVertexData::~UIVertexData() {
|
UIVertexData::~UIVertexData() {
|
||||||
@@ -12,8 +9,7 @@ UIVertexData::~UIVertexData() {
|
|||||||
|
|
||||||
void UIVertexData::upload() {
|
void UIVertexData::upload() {
|
||||||
if (m_sum == 0) {
|
if (m_sum == 0) {
|
||||||
Logger::error("You need update_sum first");
|
return;
|
||||||
ASSERT(false);
|
|
||||||
}
|
}
|
||||||
if (m_vertices.size() == 0) {
|
if (m_vertices.size() == 0) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "Cubed/app.hpp"
|
#include "Cubed/app.hpp"
|
||||||
#include "Cubed/debug_collector.hpp"
|
#include "Cubed/debug_collector.hpp"
|
||||||
|
#include "Cubed/gameplay/client_world.hpp"
|
||||||
#include "Cubed/render/renderer.hpp"
|
#include "Cubed/render/renderer.hpp"
|
||||||
#include "Cubed/scene/scene_manager.hpp"
|
#include "Cubed/scene/scene_manager.hpp"
|
||||||
#include "Cubed/scene/world_scene.hpp"
|
#include "Cubed/scene/world_scene.hpp"
|
||||||
@@ -38,7 +39,12 @@ void WorldUIManager::init() {
|
|||||||
chat_box.set_text_scale(0.6f);
|
chat_box.set_text_scale(0.6f);
|
||||||
chat_box.set_d_image(m_scene.scene_manager().app().texture_manager());
|
chat_box.set_d_image(m_scene.scene_manager().app().texture_manager());
|
||||||
chat_box.set_app(&m_scene.scene_manager().app());
|
chat_box.set_app(&m_scene.scene_manager().app());
|
||||||
chat_box.set_on_finish([]() {});
|
chat_box.set_on_finish([this, &chat_box]() {
|
||||||
|
ChatMessage message{m_scene.client_world().get_player().get_name(),
|
||||||
|
std::move(chat_box.get_input_text()), 0};
|
||||||
|
chat_box.clear_input();
|
||||||
|
m_scene.client_world().send_chat_message(message);
|
||||||
|
});
|
||||||
m_chat_box = &chat_box;
|
m_chat_box = &chat_box;
|
||||||
}
|
}
|
||||||
void WorldUIManager::render(Renderer& renderer) {
|
void WorldUIManager::render(Renderer& renderer) {
|
||||||
@@ -56,6 +62,11 @@ void WorldUIManager::set_chatting(bool chantting) {
|
|||||||
m_chat_box->set_typing(chantting);
|
m_chat_box->set_typing(chantting);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WorldUIManager::add_chat_message(ChatMessage& message) {
|
||||||
|
|
||||||
|
m_chat_box->add_message(message);
|
||||||
|
}
|
||||||
|
|
||||||
bool WorldUIManager::handle_key_event(const KeyEvent& e) {
|
bool WorldUIManager::handle_key_event(const KeyEvent& e) {
|
||||||
|
|
||||||
return UIManager::handle_key_event(e);
|
return UIManager::handle_key_event(e);
|
||||||
|
|||||||
Reference in New Issue
Block a user