From 4f9e67d7003194fca7632fcba51ad399b0ee362e Mon Sep 17 00:00:00 2001 From: Nemirtingas Date: Sat, 3 Aug 2019 12:58:48 +0200 Subject: [PATCH] Support for chat message (WIP) --- ImGui/imgui_draw.cpp | 30 +++++++++ dll/net.proto | 12 ++++ dll/network.cpp | 5 ++ dll/network.h | 1 + overlay_experimental/steam_overlay.cpp | 89 +++++++++++++++++++++++--- overlay_experimental/steam_overlay.h | 12 +++- 6 files changed, 138 insertions(+), 11 deletions(-) diff --git a/ImGui/imgui_draw.cpp b/ImGui/imgui_draw.cpp index e7c5e82..6fef016 100644 --- a/ImGui/imgui_draw.cpp +++ b/ImGui/imgui_draw.cpp @@ -2816,6 +2816,8 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const { + ImU32 color_bkp = col; + if (!text_end) text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls. @@ -2871,6 +2873,34 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col while (s < text_end) { + if (*s == 1) + { + ++s; + + unsigned char color[4]; + + for (int i = 0; i < 4; ++i, s+=2) + { + if (s[0] >= '0' && s[0] <= '9') + color[i] = (s[0] - '0') * 16; + else if (s[0] >= 'a' && s[0] <= 'f') + color[i] = (s[0] - 'a' + 10) * 16; + else if (s[0] >= 'A' && s[0] <= 'F') + color[i] = (s[0] - 'A' + 10) * 16; + + if (s[1] >= '0' && s[1] <= '9') + color[i] += (s[1] - '0'); + else if (s[1] >= 'a' && s[1] <= 'f') + color[i] += (s[1] - 'a' + 10); + else if (s[1] >= 'A' && s[1] <= 'F') + color[i] += (s[1] - 'A' + 10); + } + + col = ImColor(color[0], color[1], color[2], color[3]); + + continue; + } + if (word_wrap_enabled) { // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. diff --git a/dll/net.proto b/dll/net.proto index 6146adc..d30e9af 100644 --- a/dll/net.proto +++ b/dll/net.proto @@ -185,6 +185,17 @@ message Friend_Messages { } } +message Steam_Messages { + enum Types { + FRIEND_CHAT = 0; + } + + Types type = 1; + oneof message_data { + bytes message = 2; + } +} + message Common_Message { uint64 source_id = 1; uint64 dest_id = 2; @@ -200,6 +211,7 @@ message Common_Message { Friend_Messages friend_messages = 11; Network_Old network_old = 12; Networking_Sockets networking_sockets = 13; + Steam_Messages steam_messages = 14; } uint32 source_ip = 128; diff --git a/dll/network.cpp b/dll/network.cpp index b85ce3f..eff9746 100644 --- a/dll/network.cpp +++ b/dll/network.cpp @@ -533,6 +533,11 @@ void Networking::do_callbacks_message(Common_Message *msg) PRINT_DEBUG("has_networking_sockets\n"); run_callbacks(CALLBACK_ID_NETWORKING_SOCKETS, msg); } + + if (msg->has_steam_messages()) { + PRINT_DEBUG("has_steam_messages\n"); + run_callbacks(CALLBACK_ID_STEAM_MESSAGES, msg); + } } bool Networking::handle_tcp(Common_Message *msg, struct TCP_Socket &socket) diff --git a/dll/network.h b/dll/network.h index bc0ce91..324ed62 100644 --- a/dll/network.h +++ b/dll/network.h @@ -60,6 +60,7 @@ enum Callback_Ids { CALLBACK_ID_AUTH_TICKET, CALLBACK_ID_FRIEND_MESSAGES, CALLBACK_ID_NETWORKING_SOCKETS, + CALLBACK_ID_STEAM_MESSAGES, CALLBACK_IDS_MAX }; diff --git a/overlay_experimental/steam_overlay.cpp b/overlay_experimental/steam_overlay.cpp index 3e46414..bdf84ee 100644 --- a/overlay_experimental/steam_overlay.cpp +++ b/overlay_experimental/steam_overlay.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -98,6 +99,7 @@ Steam_Overlay::Steam_Overlay(Settings* settings, SteamCallResults* callback_resu overlay_state_changed(false) { run_every_runcb->add(&Steam_Overlay::steam_overlay_run_every_runcb, this); + this->network->setCallback(CALLBACK_ID_STEAM_MESSAGES, settings->get_local_steam_id(), &Steam_Overlay::steam_overlay_callback, this); } Steam_Overlay::~Steam_Overlay() @@ -105,6 +107,12 @@ Steam_Overlay::~Steam_Overlay() run_every_runcb->remove(&Steam_Overlay::steam_overlay_run_every_runcb, this); } +void Steam_Overlay::steam_overlay_callback(void* object, Common_Message* msg) +{ + Steam_Overlay* _this = reinterpret_cast(object); + _this->Callback(msg); +} + void Steam_Overlay::steam_overlay_run_every_runcb(void* object) { Steam_Overlay* _this = reinterpret_cast(object); @@ -262,6 +270,7 @@ void Steam_Overlay::FriendConnect(Friend _friend) { std::lock_guard lock(global_mutex); friends[_friend].window_state = window_state_none; + memset(friends[_friend].chat_input, 0, max_chat_len); } void Steam_Overlay::FriendDisconnect(Friend _friend) @@ -296,13 +305,13 @@ void Steam_Overlay::BuildFriendWindow(Friend const& frd, friend_window_state& st return; bool show = true; - ImGui::SetNextWindowSizeConstraints({ 160.0,90.0 }, { 9999.0, 9999.0 }); + if (ImGui::Begin(frd.name().c_str(), &show)) { // Fill this with the chat box and maybe the invitation if (state.window_state & (window_state_lobby_invite | window_state_rich_invite)) { - ImGui::LabelText("", "%s invited you to join the game.", frd.name().c_str()); + ImGui::LabelText("##label", "%s invited you to join the game.", frd.name().c_str()); ImGui::SameLine(); if (ImGui::Button("Accept")) { @@ -314,6 +323,30 @@ void Steam_Overlay::BuildFriendWindow(Friend const& frd, friend_window_state& st state.window_state &= ~(window_state_lobby_invite | window_state_rich_invite); } } + + ImGui::PushItemWidth(-1.0f); + ImGui::InputTextMultiline("##chat_history", &state.chat_history[0], state.chat_history.length(), { -1.0f, 0 }, ImGuiInputTextFlags_ReadOnly); + ImGui::PopItemWidth(); + + if (ImGui::InputText("##chat_line", state.chat_input, max_chat_len, ImGuiInputTextFlags_EnterReturnsTrue)) + { + if (!(state.window_state & window_state_send_message)) + { + has_friend_action.push(frd); + state.window_state |= window_state_send_message; + } + } + + ImGui::SameLine(); + + if (ImGui::Button("Send")) + { + if (!(state.window_state & window_state_send_message)) + { + has_friend_action.push(frd); + state.window_state |= window_state_send_message; + } + } } // User closed the friend window if (!show) @@ -335,18 +368,17 @@ void Steam_Overlay::OverlayProc( int width, int height ) ImGui::SetNextWindowSize({ static_cast(width), static_cast(height) }); - bool open_overlay = show_overlay; - if (ImGui::Begin("SteamOverlay", &open_overlay, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus)) + if (ImGui::Begin("SteamOverlay", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus)) { - ImGui::LabelText("", "Username: %s(%llu) playing %u", + ImGui::LabelText("##label", "Username: %s(%llu) playing %u", settings->get_local_name(), settings->get_local_steam_id().ConvertToUint64(), settings->get_local_game_id().AppID()); ImGui::Spacing(); - ImGui::LabelText("", "Friends"); - ImGui::ListBoxHeader("", friend_size); + ImGui::LabelText("##label", "Friends"); + ImGui::ListBoxHeader("##label", friend_size); std::for_each(friends.begin(), friends.end(), [this]( auto& i) { ImGui::PushID(i.first.id()); @@ -372,11 +404,25 @@ void Steam_Overlay::OverlayProc( int width, int height ) } ImGui::End(); - ShowOverlay(open_overlay); - //ImGui::ShowDemoWindow(); } +void Steam_Overlay::Callback(Common_Message *msg) +{ + if (msg->has_steam_messages()) + { + Friend frd; + frd.set_id(msg->source_id()); + auto friend_info = friends.find(frd); + if (friend_info != friends.end()) + { + Steam_Messages const& steam_message = msg->steam_messages(); + // Change color to cyan for friend + friend_info->second.chat_history.append("\x1", 1).append("00FFFFFF", 8).append(steam_message.message()).append("\n", 1); + } + } +} + void Steam_Overlay::RunCallbacks() { if (overlay_state_changed) @@ -397,6 +443,31 @@ void Steam_Overlay::RunCallbacks() if (friend_info != friends.end()) { uint64 friend_id = friend_info->first.id(); + // The user clicken on "Send" + if (friend_info->second.window_state & window_state_send_message) + { + char* input = friend_info->second.chat_input; + char* end_input = input + strlen(input); + char* printable_char = std::find_if(input, end_input, [](char c) { + return std::isgraph(c); + }); + if (printable_char != end_input) + { + // Handle chat send + Common_Message msg; + Steam_Messages* steam_messages = new Steam_Messages; + steam_messages->set_type(Steam_Messages::FRIEND_CHAT); + steam_messages->set_message(friend_info->second.chat_input); + msg.set_allocated_steam_messages(steam_messages); + msg.set_source_id(settings->get_local_steam_id().ConvertToUint64()); + msg.set_dest_id(friend_id); + network->sendTo(&msg, true); + + friend_info->second.chat_history.append("\x1", 1).append("00FF00FF", 8).append(input).append("\n", 1); + } + *input = 0; // Reset the input field + friend_info->second.window_state &= ~window_state_send_message; + } // The user clicked on "Invite" if (friend_info->second.window_state & window_state_invite) { diff --git a/overlay_experimental/steam_overlay.h b/overlay_experimental/steam_overlay.h index fc879ef..376ed67 100644 --- a/overlay_experimental/steam_overlay.h +++ b/overlay_experimental/steam_overlay.h @@ -6,6 +6,8 @@ #include #include +static constexpr size_t max_chat_len = 768; + enum window_state { window_state_none = 0, @@ -13,17 +15,20 @@ enum window_state window_state_invite = 1<<1, window_state_join = 1<<2, window_state_lobby_invite = 1<<3, - window_state_rich_invite = 1<<4 + window_state_rich_invite = 1<<4, + window_state_send_message = 1<<5, }; struct friend_window_state { uint8 window_state; - union + union // The invitation (if any) { uint64 lobbyId; char connect[k_cchMaxRichPresenceValueLength]; }; + std::string chat_history; + char chat_input[max_chat_len]; }; struct Friend_Less @@ -70,6 +75,9 @@ class Steam_Overlay static LRESULT WINAPI MyDispatchMessageW(const MSG* lpMsg); static void steam_overlay_run_every_runcb(void* object); + static void steam_overlay_callback(void* object, Common_Message* msg); + + void Callback(Common_Message* msg); void RunCallbacks(); // Right click on friend