Skip to content

feat(chat): show chat messages from Discord as such (C4) - #708

Open
sven-n wants to merge 5 commits into
claude/discord-presence-mumain-597-l2q09dfrom
claude/discord-presence-mumain-597-l2q09d-c4-chat
Open

sven-n wants to merge 5 commits into
claude/discord-presence-mumain-597-l2q09dfrom
claude/discord-presence-mumain-597-l2q09d-c4-chat

Conversation

@sven-n

@sven-n sven-n commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner

Summary

Stacked on #705 (Discord Rich Presence), only for docs/discord.md, which this extends. The code doesn't depend on #705. Once #705 is merged, GitHub retargets this PR to main.

Messages written in Discord are shown as such in the chat log. This works with OpenMU's chat bridge (MUnique/OpenMU#1161) today. That bridge sends Discord messages through the ordinary guild and alliance chat, as the linked character with an @ prefix that no character name can contain (DiscordChatText.DiscordSenderPrefix). The client recognises that prefix, so no new packet is needed.

  • Display path: UI::Chat::External::Show(message). The bridged guild and alliance lines in ReceiveChat go through it (IsBridgedSender / ShowBridged). A dedicated packet later (ExternalChatMessage, as planned in the issue) would go through it too.
  • Sender: shown as [Discord] <name>. With OpenMU's bridge the name is the linked character. A packet carrying the Discord name could show it with up to 32 characters.
  • Scope: the message takes the chat type and colours of its scope (guild or alliance). That covers "badge or distinct colour, using the colour of the scope" from the issue.
  • Discord view: every message from Discord is also listed in a new chat-log view, TYPE_DISCORD_MESSAGE. The chat log has no tab bar, so F2 now cycles all → whispers → Discord → all. The Discord view is only offered once a message from Discord has arrived, so F2 behaves exactly as before on servers without a bridge.
  • No whispering Discord users: nothing is added to the whisper allow-list and no speech balloon is shown. Right-clicking their line doesn't start a whisper (CMessageText::IsExternal).
  • Text length: the text is cut to MAX_CHAT_SIZE, like an in-game message.
  • Control socket: Discord lines are recorded on the control socket as chat kind discord instead of guild/union.
  • Refactor: I extracted the nine repeated create-and-append blocks of CNewUIChatLogWindow::ProcessAddText into AppendMessage. The "also copy into a second view" rule moved into GetSecondaryMsgs. Until now that second view was only the view the error came from; now it can also be the Discord view. The behaviour is otherwise unchanged, and a test covers the error-copy case.
  • F2 toggle: CNewUIChatInputBox::m_bShowMessageElseNormal is gone. F2 now asks the chat log for the next view, so the toggle can no longer drift from what is shown.
  • Docs: docs/discord.md gets a "Messages from Discord" section.

Not covered:

  • World chat: OpenMU's /world chat reaches the client as a server-formatted blue system message (PlayerMessage.WorldChatMessage). That can't be told apart reliably, so world-chat lines from Discord stay as they are.
  • Discord display names: these need the dedicated F5 packet from [Discord] C4: Show messages from Discord as such #700. The client side is ready for it.

Verification

  • Linux linux-x64 Debug build: full ctest run, 447/447 passed, including 10 new tests in tests/ui/test_external_chat.cpp:
    • sender label and name limit
    • the bridged-sender prefix
    • scope → chat type, and the text cut
    • an external message lands in its scope, in "all" and in the Discord view
    • an in-game message stays out of the Discord view
    • the F2 cycle with and without Discord messages
    • an error is still copied to the view it came from (regression check for the refactor)
  • clang-format 21.1.8 range check and cppcheck (CI flags, including WSclient.cpp) are clean on the changed lines.
  • Not tested yet: a running client against OpenMU with #1161 and a bound guild channel.

Related issues

Part of #700 (C4). Works with MUnique/OpenMU#1161. The dedicated packet (Discord display names, world scope) is still open.

Checklist

🤖 Generated with Claude Code

https://claude.ai/code/session_012feEciycvL8PGjsezyRFqj

Groundwork for #700 that needs no server support yet: the display path the
ExternalChatMessage packet handler will call (UI::Chat::External::Show).

- The sender is shown as "[Discord] <name>", with the full Discord name
  (up to 32 characters) instead of the 10-byte character name.
- The message takes its scope's chat type and colours (guild, alliance,
  world) and is also listed in a new Discord view of the chat log.
- Discord users can't be whispered: no whisper allow-list entry, no speech
  balloon, and right-clicking their line doesn't start a whisper.
- F2 cycles all -> whispers -> Discord -> all; the Discord view is only
  offered once a message from Discord arrived, so nothing changes on
  servers without a bridge.

The repeated create-and-append blocks of CNewUIChatLogWindow::ProcessAddText
are extracted into AppendMessage, and the copy into a second view (an
error's origin, now also the Discord view) into GetSecondaryMsgs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012feEciycvL8PGjsezyRFqj
@sven-n
sven-n requested a review from Mosch0512 October 10, 2026 08:57
OpenMU's chat bridge (MUnique/OpenMU#1161) sends messages written in Discord
through the ordinary guild and alliance chat, as the linked character with
an '@' prefix that no character name can contain. Such lines are now shown
through UI::Chat::External as messages from Discord: "[Discord] <name>" in
the guild or alliance colours, in the Discord view, and not whisperable.

This makes the C4 display path work against OpenMU today, without waiting
for a dedicated packet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012feEciycvL8PGjsezyRFqj

@sven-n sven-n left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Part of a review pass over the whole Discord integration.

The separation is good — UI::Chat::External owns the badge, the truncation and the scope mapping, and WSclient.cpp just routes into it, so the legacy chat path gains four lines rather than growing new logic. Deliberately skipping the speech balloon and the whisper allow-list for bridged senders is the right call and the comment explains why, which is the kind of thing that would otherwise get "fixed" later by someone assuming it was an omission.

One comment inline, on the @-prefix heuristic. It's low severity and the fix mostly belongs server-side.

Process note: #709 is based on #707's branch rather than on this one, so its diff re-includes everything here (ExternalChat.*, NewUIChatLogWindow, test_external_chat.cpp are byte-identical in both). This branch is an ancestor of #709, so there's no conflict risk — but reviewing #709 as it stands means re-reviewing this PR's content. Retargeting #709's base onto this branch, or landing this one first, would make its diff show only the C2–C5 work.


Generated by Claude Code

Comment thread src/source/UI/Chat/ExternalChat.cpp
Says next to IsBridgedSender that the prefix is a convention of the
server (enforced in character names by MUnique/OpenMU#1174), not something
the client can verify, and that ExternalChatMessage is the authoritative
signal where the server sends it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012feEciycvL8PGjsezyRFqj
…discord-presence-mumain-597-l2q09d-c4-chat

main moved the chat log to UI/HUD/ChatLogWindow (mu::ui::window, drawn
with RmlUi), the message types to UI/Chat/MessageType.h and the chat
posting to UI::Chat::PostChat. The Discord view and the external
messages move along: TYPE_DISCORD_MESSAGE joins MessageType.h, and the
RmlUi right-click no longer offers a whisper to a Discord sender.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01USFZ9xErMB1EwkpfdUHzoZ
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants