Skip to content
Back to skills

Chat System

ASecurity

Chat system — MVP pattern, message bus decorators, commands, auto-translation, encrypted history, state machine. Use when adding chat commands, modifying message flow, or working with chat services.

  • 23 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added May 27, 2026
developmentgoapi

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned May 27, 2026

npx -y skills add decentraland/unity-explorer --skill chat-system --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Chat System?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Chat System
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/decentraland-chat-system/badge)](https://www.skillsdirectory.com/skills/decentraland-chat-system)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: chat-system
description: "Chat system — MVP pattern, message bus decorators, commands, auto-translation, encrypted history, state machine. Use when adding chat commands, modifying message flow, or working with chat services."
user-invocable: false
---

# Chat System

## Sources

- `docs/chat.md` — Chat architecture (MVP, state machine, commands, services, event bus, auto-translation)
- `docs/chat-emojis.md` — Emoji atlas creation with TextMesh Pro, noto-emoji font
- `docs/chat-history-local-storage.md` — Local encrypted chat history persistence, feature flag control

---

## Architecture Overview

The chat system uses **MVP + State Machine + EventBus + Commands**:

- **View** (MonoBehaviour): draw-only; forwards UI events
- **Presenter** (POCO): listens to View + EventBus, delegates to Commands/Services, updates View
- **Model**: embodied by Services and data stores (`IChatHistory`)
- **Commands**: single-purpose business logic (e.g., `SendMessageCommand`)
- **Services**: long-lived shared state or I/O boundaries (history, member lists, input blocking)
- **EventBus**: decoupled comms via `ChatEvents` (no tight coupling between Presenters)
- **State Machine**: `ChatStateMachine` controls top-level UI mode; replaces boolean soup
- **Composition root**: `ChatPlugin` wires Views, Presenters, Commands, Services, and State Machine

### Component Map

| Layer | Key Classes |
|-------|------------|
| Composition | `ChatPlugin`, `ChatMainSharedAreaController` |
| Presenters | `ChatPanelPresenter`, `ChatTitlebarPresenter`, `ChatInputPresenter`, `ChatMessageFeedPresenter`, `ChatChannelsPresenter`, `ChatMemberListPresenter` |
| Commands | `SendMessageCommand`, `SelectChannelCommand`, `InitializeChatSystemCommand`, `GetMessageHistoryCommand`, `ResolveInputStateCommand`, ~20 more |
| Services | `CurrentChannelService`, `ChatHistoryService`, `ChatMemberListService`, `ChatInputBlockingService`, `ChatContextMenuService`, `ChatWorldBubbleService` |
| State Machine | `ChatStateMachine` with `Init`, `Default`, `Focused`, `Members`, `Minimized`, `Hidden` states |

### Message Send Flow

1. `ChatInputView` raises `onSubmit("Hello")`
2. `ChatInputPresenter` calls `SendMessageCommand`
3. Command pulls active channel from `CurrentChannelService`, calls `IChatMessagesBus`
4. Bus echoes `MessageAdded` locally (optimistic UI)
5. `ChatHistoryService` persists to `IChatHistory`
6. `ChatMessageFeedPresenter` maps via `CreateMessageViewModelCommand` and updates View

---

## Message Bus Decorator Chain

`IChatMessagesBus` is wrapped in decorators, each adding a concern. The chain is composed in `DynamicWorldContainer`.

### Decorator Responsibilities

| Decorator | Role |
|-----------|------|
| `MultiplayerChatMessagesBus` | Core transport: sends via LiveKit pipes (Island/Scene/Chat), receives from subscribed pipes, deduplicates, rate-limits, buffers nearby messages |
| `SelfResendChatMessageBus` | On `Send()`, also fires `MessageAdded` for the sender's own message (optimistic local echo) |
| `IgnoreWithSymbolsChatMessageBus` | Filters messages containing forbidden control characters (`\u2410`, `\u2406`, `\u2411`) |
| `CommandsHandleChatMessageBus` | Intercepts `/`-prefixed messages, parses command + args, dispatches to `IChatCommand`, sends system response |

### Interface

```csharp
public interface IChatMessagesBus : IDisposable
{
    event Action<ChatChannel.ChannelId, ChatChannel.ChatChannelType, ChatMessage> MessageAdded;
    void Send(ChatChannel channel, string message, ChatMessageOrigin origin, double timestamp);
}
```

Each decorator wraps `origin.Send()` and forwards `origin.MessageAdded` events, adding its own logic before or after.

---

## Chat Command Pattern

### Interface

```csharp
public interface IChatCommand
{
    string Command { get; }
    string Description { get; }
    bool DebugOnly => false;
    bool ValidateParameters(string[] parameters) => parameters.Length == 0;
    UniTask<string> ExecuteCommandAsync(string[] parameters, CancellationToken ct);
}
```

### Existing Commands

| Command | Class | Debug? |
|---------|-------|--------|
| `/goto <x,y\|random\|crowd\|world>` | `GoToChatCommand` | No |
| `/help` | `HelpChatCommand` | No |
| `/version` | `VersionChatCommand` | No |
| `/gotolocal <x,y>` | `GoToLocalChatCommand` | Yes |
| `/reloadscene` | `ReloadSceneChatCommand` | Yes |
| `/debugpanel` | `DebugPanelChatCommand` | Yes |
| `/showentity <id>` | `ShowEntityChatCommand` | Yes |
| `/logs <category> <level>` | `LogsChatCommand` | Yes |
| `/logmatrix` | `LogMatrixChatCommand` | Yes |
| `/rooms` | `RoomsChatCommand` | Yes |
| `/loadpx <urn>` | `LoadPortableExperienceChatCommand` | Yes |
| `/killpx <urn>` | `KillPortableExperienceChatCommand` | Yes |
| `/appargs` | `AppArgsChatCommand` | Yes |

Commands are registered in `DynamicWorldContainer` as `IReadOnlyList<IChatCommand>` and looked up by name in a dictionary.

---

## Input State Machine

`ChatStateMachine` manages 6 UI states via `MVCStateMachine<ChatState>`. Each state class contains only logic relevant to that state.

### State Transitions

```
Init --> Default (on view show)
Default --> Focused (click inside / focus requested)
Default --> Minimized (close requested)
Default --> Members (toggle members)
Focused --> Default (click outside)
Focused --> Minimized (close requested)
Focused --> Members (toggle members)
Members --> Default (click outside)
Members --> Focused (close/back/toggle)
Minimized --> Focused (focus requested / minimize toggle)
Hidden <-- (SharedSpaceManager hides chat when other panels open)
Hidden --> Default (SharedSpaceManager restores chat)
```

All states inherit `ChatState` which provides virtual no-op methods: `OnClickOutside`, `OnClickInside`, `OnCloseRequested`, `OnFocusRequested`, `OnMinimizeRequested`, `OnToggleMembers`, `OnPointerEnter`, `OnPointerExit`. States override only the transitions they handle.

---

## Auto-Translation

Translation is orchestrated by `TranslationService` with modular components:

| Component | Role |
|-----------|------|
| `ITranslationService` | Central orchestrator for auto and manual translation |
| `ITranslationProvider` (`DclTranslationProvider`) | External API adapter |
| `IMessageProcessor` (`ChatMessageProcessor`) | Tokenizes complex messages, protects non-translatable parts |
| `IConversationTranslationPolicy` | Decides if auto-translate applies (global flag + per-conversation toggle) |
| `ITranslationCache` (`InMemoryTranslationCache`) | Prevents redundant API calls; capacity configured via `ChatConfig.TranslationCacheCapacity` (default 200) |
| `ITranslationMemory` (`InMemoryTranslationMemory`) | Tracks per-message translation state (`Original`, `Pending`, `Success`, `Failed`); capacity 200 |

### Configuration (ChatConfig ScriptableObject)

```csharp
[field: SerializeField] public int TranslationMaxRetries { get; set; } = 1;
[field: SerializeField] public float TranslationTimeoutSeconds { get; set; } = 10.0f;
public int TranslationMemoryCapacity = 200;
public int TranslationCacheCapacity = 200;
```

### Flow

1. `TranslationService.ProcessIncomingMessage()` checks `IConversationTranslationPolicy`
2. Creates `MessageTranslation` with `Pending` state in `ITranslationMemory`
3. Publishes `TranslationEvents.MessageTranslationRequested`
4. Checks `ITranslationCache` for existing result
5. Runs `RequiresProcessing()` -- if text has TMP tags, emojis, dates, or slash commands, uses `IMessageProcessor` (tokenize, protect, batch-translate); otherwise calls `ITranslationProvider` directly
6. Stores result in cache + memory, publishes `TranslationEvents.MessageTranslated`
7. On failure, sets `Failed` state, publishes `TranslationEvents.MessageTranslationFailed`

Concurrency: per-sender serialization via leader/follower gates, global cap of 10 in-flight translations.

---

## Encrypted History

Feature flag: `explorer-alfa-chat-history-local-storage`. Core classes in `Explorer/Assets/DCL/Chat/History/`.

### Storage Layout

```
{persistentDataPath}/c/
  {Base64(AES(walletAddress))}/           <-- per-account folder
    {Base64(AES("UserConversationSettings"))}   <-- encrypted JSON: open channels + order
    {Base64(AES(channelId))}              <-- encrypted CSV per conversation
```

### Key Behaviors

- **Per-account isolation**: wallet address is the encryption key; folder name is `Base64(AES(address))`
- **Lazy file I/O**: files stay open for 5 seconds after last write, then auto-close
- **Background processing**: message queue processed on a thread pool via `UniTask.RunOnThreadPool`
- **Rehydration**: on first DM message, snapshots session messages, loads history from disk, replays session tail to avoid data loss
- **Nearby excluded**: only `ChatChannelType.USER` conversations are persisted

---

## Detailed Reference

For detailed code examples, see [reference.md](reference.md).

---

## Cross-References

- **mvc-and-ui-architecture** -- MVC controller pattern, `SharedSpaceManager` panel coordination
- **web-requests** -- `IWebRequestController` used by `DclTranslationProvider` and `GoToChatCommand` for API calls
- **feature-flags-and-configuration** -- `FeatureFlagsStrings.CHAT_HISTORY_LOCAL_STORAGE` gates history; `FeaturesRegistry` gates rate limiting and message buffering

Files in this skill

  • SKILL.md9.1 KB
  • reference.md1.9 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.