Skip to main content

Chat System Overview

The PlayNANOO chat system provides real-time WebSocket-based chat functionality. This document explains the overall structure and usage of the chat system.

System Architecture

The chat system consists of 3 core classes.

ClassRole
ChatManagerSingleton manager that handles chat server connection and API calls
ChatClientCore class that processes WebSocket connection and message transmission/reception
ChatModelsDefines data models and event listener interfaces
Required Implementation Guide

The above 3 classes are required implementation classes and can be freely modified to fit your project structure.

Required Implementation Elements:

  • Implement all callback methods of the IChatListener interface
  • Periodically call the ChatClient.Service() method in Update() (required for message reception)
  • Call server list API for WebSocket connection (PUT /chat/v20211101/server)
  • Implement HMAC-SHA256 hash authentication logic

Workflow

1. Call ChatManager.Connect()

2. Request server list API (PUT /chat/v20211101/server)

3. Select random server after receiving server list

4. ChatClient performs WebSocket connection

5. Call IChatListener.OnConnected() on successful connection

6. Subscribe to channels and send/receive messages

Step 1: Implement Listener

To receive chat events, you must implement the IChatListener interface.

public class MyChatHandler : MonoBehaviour, IChatListener
{
public void OnConnected()
{
// Server connection successful
Debug.Log("Connected to chat server.");
}

public void OnDisconnected()
{
// Server disconnected
Debug.Log("Disconnected from chat server.");
}

public void OnError(string code, string message)
{
// Error occurred
Debug.LogError($"Error: [{code}] {message}");
}

public void OnChannels(ChatChannelInfo[] channels)
{
// Channel list received
foreach (var ch in channels)
{
Debug.Log($"Channel: {ch.channel}, Users: {ch.count}");
}
}

public void OnSubscribed(ChatUserInfo user)
{
// User entered channel
Debug.Log($"{user.visitorName} has entered.");
}

public void OnUnSubscribed(ChatUserInfo user)
{
// User left channel
Debug.Log($"{user.visitorName} has left.");
}

public void OnPublicMessage(ChatUserInfo sender, string message)
{
// Public message received
Debug.Log($"[{sender.visitorName}]: {message}");
}

public void OnPrivateMessage(ChatUserInfo sender, string message)
{
// Whisper received
Debug.Log($"[Whisper][{sender.visitorName}]: {message}");
}

public void OnNotifyMessage(ChatUserInfo sender, string message)
{
// System notification received
Debug.Log($"[System]: {message}");
}

public void OnPlayerOnline(ChatPlayerInfo[] players)
{
// Player online status received
foreach (var p in players)
{
string status = p.online == "Y" ? "online" : "offline";
Debug.Log($"{p.userUniqueId}: {status}");
}
}
}

Step 2: Connect to Chat Server

Connect to the chat server by calling ChatManager.Instance.Connect().

void Start()
{
// Pass object that implements IChatListener
ChatManager.Instance.Connect(this);
}

When connection is successful, the OnConnected() callback is called.

Step 3: Subscribe to Channel

After connecting, subscribe to the desired channel.

public void OnConnected()
{
// Subscribe to global channel
ChatManager.Instance.Subscribe("global");

// Or subscribe to specific channel
ChatManager.Instance.Subscribe("room_123");
}

Step 4: Send Messages

Send Public Message

Send a message to all users in the channel.

public void SendChatMessage(string text)
{
ChatManager.Instance.SendPublicMessage("global", text);
}

Send Whisper

Send a message only to a specific user.

public void SendWhisper(string targetUserId, string text)
{
ChatManager.Instance.SendPrivateMessage(targetUserId, text);
}

Step 5: Other Features

Query Channel List

ChatManager.Instance.GetChannels();
// Result received in OnChannels() callback

Unsubscribe from Channel

ChatManager.Instance.Unsubscribe("global");

Query Player Online Status

string[] userIds = new string[] { "user_001", "user_002", "user_003" };
ChatManager.Instance.GetPlayersOnline(userIds);
// Result received in OnPlayerOnline() callback

Check Connection Status

if (ChatManager.Instance.IsConnected())
{
Debug.Log("Connected to chat server.");
}

Complete Usage Example

using UnityEngine;

public class ChatExample : MonoBehaviour, IChatListener
{
void Start()
{
// 1. Connect to chat server
ChatManager.Instance.Connect(this);
}

// 2. Subscribe to channel on successful connection
public void OnConnected()
{
Debug.Log("Connection successful!");
ChatManager.Instance.Subscribe("global");
}

// 3. Channel entry notification
public void OnSubscribed(ChatUserInfo user)
{
Debug.Log($"{user.visitorName} has entered.");
}

// 4. Receive messages
public void OnPublicMessage(ChatUserInfo sender, string message)
{
Debug.Log($"[{sender.visitorName}]: {message}");
}

// 5. Send message (called from UI button)
public void OnSendButtonClick()
{
ChatManager.Instance.SendPublicMessage("global", "Hello!");
}

// Other callback implementations
public void OnDisconnected() => Debug.Log("Disconnected");
public void OnError(string code, string message) => Debug.LogError($"[{code}] {message}");
public void OnChannels(ChatChannelInfo[] channels) { }
public void OnUnSubscribed(ChatUserInfo user) => Debug.Log($"{user.visitorName} has left.");
public void OnPrivateMessage(ChatUserInfo sender, string message) => Debug.Log($"[Whisper] {message}");
public void OnNotifyMessage(ChatUserInfo sender, string message) => Debug.Log($"[Notification] {message}");
public void OnPlayerOnline(ChatPlayerInfo[] players) { }
}

Important Notes

  1. Call Service in Update: ChatClient.Service() is called in ChatManager's Update() to process WebSocket callbacks on the main thread. If you modify ChatManager directly, you must maintain this part.

  2. Singleton Pattern: ChatManager is implemented as a singleton and accessed via ChatManager.Instance. It persists across scene transitions.

  3. Disconnect: The connection is automatically disconnected in ChatManager.OnDestroy() when the app exits.