跳转到主要内容

ChatManager

聊天系统的单例管理器类。管理聊天服务器连接和消息收发。

URL 确认

此 API 使用 service-api.playnanoo.com 域名。

API 信息

  • 服务器列表 URL: https://service-api.playnanoo.com/chat/v20211101/server
  • 过滤器 URL: https://service-api.playnanoo.com/chat/v20211101/filter
  • Method: PUT
  • 需要认证: 否

主要功能

方法说明
Connect连接到聊天服务器
IsConnected检查连接状态
Subscribe订阅频道(可设置历史消息数量)
Unsubscribe取消订阅频道
GetChannels查询频道列表
GetPlayersOnline查询玩家在线状态
SendPublicMessage发送公开消息
SendPrivateMessage发送私密消息
FetchFilterWords获取禁用词列表
Filter对消息应用禁用词过滤

Unity C# 实现

using System.Collections;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Networking;

public class ChatManager : MonoBehaviour
{
public static ChatManager Instance
{
get
{
if (_instance == null)
{
var go = new GameObject("ChatManager");
_instance = go.AddComponent<ChatManager>();
}
return _instance;
}
}
private static ChatManager _instance;

private const string CHAT_SERVER_URL = "https://service-api.playnanoo.com/chat/v20211101/server";
private const string CHAT_FILTER_URL = "https://service-api.playnanoo.com/chat/v20211101/filter";

private ChatClient _chatClient;
private IChatListener _listener;
private string[] _filterWords;

void Awake()
{
if (_instance != null && _instance != this)
{
Destroy(gameObject);
return;
}
_instance = this;
DontDestroyOnLoad(gameObject);
}

public void Connect(IChatListener listener)
{
_listener = listener;
_chatClient = new ChatClient(listener, DataManager.Instance.Nickname);
StartCoroutine(FetchServerListAndConnect());
}

void Update()
{
// 실시간 메시지를 수신하기 위해선 지속적으로 이벤트를 발생 시켜야 합니다.
// 메시지 수신 이벤트가 실행 되지 않는 경우 사용자의 메시지가 수신 되지 않습니다.
_chatClient?.Service();
}

void OnDestroy()
{
if (_instance == this)
_instance = null;
_chatClient?.Disconnect();
}

#region Server Connection

private IEnumerator FetchServerListAndConnect()
{
bool done = false;
ChatServerResponse response = null;
string error = null;

yield return HttpClient.Send<object, ChatServerResponse>(
UnityWebRequest.kHttpVerbPUT, CHAT_SERVER_URL, false, null,
res => { response = res; done = true; },
err => { error = err?.Message; done = true; });

while (!done) yield return null;

if (error != null || response?.Servers == null || response.Servers.Length == 0)
{
_listener?.OnError("SERVER_LIST_FAILED", "Failed to fetch server list");
yield break;
}

var server = response.Servers[Random.Range(0, response.Servers.Length)];
_chatClient.Connect(server);
}

#endregion

#region Filter

public void FetchFilterWords()
{
StartCoroutine(FetchFilterWordsCoroutine());
}

private IEnumerator FetchFilterWordsCoroutine()
{
bool done = false;
ChatFilterResponse response = null;

yield return HttpClient.Send<object, ChatFilterResponse>(
UnityWebRequest.kHttpVerbPUT, CHAT_FILTER_URL, false, null,
res => { response = res; done = true; },
err => { done = true; });

while (!done) yield return null;

if (response?.FilterWords != null)
_filterWords = response.FilterWords;
}

public string Filter(string message, char separator = '*')
{
if (_filterWords == null || _filterWords.Length == 0)
return message;

foreach (var word in _filterWords)
{
if (!string.IsNullOrEmpty(word))
message = Regex.Replace(message, word, new string(separator, word.Length), RegexOptions.IgnoreCase);
}
return message;
}

#endregion

#region Public API

public bool IsConnected() => _chatClient?.IsConnected ?? false;
public void Subscribe(string channel, int prevMessageCount = 0) => _chatClient?.Subscribe(channel, prevMessageCount);
public void Unsubscribe(string channel) => _chatClient?.Unsubscribe(channel);
public void GetChannels() => _chatClient?.GetChannels();
public void GetPlayersOnline(string[] userIds) => _chatClient?.GetPlayersOnline(userIds);
public void SendPublicMessage(string channel, string text) => _chatClient?.SendPublicMessage(channel, text);
public void SendPrivateMessage(string targetUserId, string text) => _chatClient?.SendPrivateMessage(targetUserId, text);

#endregion
}

使用示例

public class ChatExample : MonoBehaviour, IChatListener
{
void Start()
{
ChatManager.Instance.Connect(this);
}

public void OnConnected()
{
Debug.Log("채팅 서버 연결 성공");
// 금칙어 목록 가져오기
ChatManager.Instance.FetchFilterWords();
// 이전 채팅 내역 10개 가져오기
ChatManager.Instance.Subscribe("global", 10);
}

public void OnDisconnected()
{
Debug.Log("채팅 서버 연결 해제");
}

public void OnError(string code, string message)
{
Debug.LogError($"채팅 오류: [{code}] {message}");
}

public void OnChannels(ChatChannelInfo[] channels)
{
foreach (var ch in channels)
{
Debug.Log($"채널: {ch.channel}, 인원: {ch.count}");
}
}

public void OnSubscribed(ChatUserInfo user)
{
Debug.Log($"{user.visitorName}님이 입장했습니다.");
}

public void OnUnSubscribed(ChatUserInfo user)
{
Debug.Log($"{user.visitorName}님이 퇴장했습니다.");
}

public void OnPublicMessage(ChatUserInfo sender, string message)
{
// 금칙어 필터 적용
Debug.Log($"[{sender.visitorName}]: {ChatManager.Instance.Filter(message)}");
}

public void OnPrivateMessage(ChatUserInfo sender, string message)
{
// 금칙어 필터 적용
Debug.Log($"[귓속말][{sender.visitorName}]: {ChatManager.Instance.Filter(message)}");
}

public void OnNotifyMessage(ChatUserInfo sender, string message)
{
Debug.Log($"[알림]: {ChatManager.Instance.Filter(message)}");
}

public void OnPlayerOnline(ChatPlayerInfo[] players)
{
foreach (var p in players)
{
Debug.Log($"플레이어: {p.userUniqueId}, 온라인: {p.online}");
}
}

void SendMessage()
{
// 메시지 전송 (서버에 원본 그대로 전송)
ChatManager.Instance.SendPublicMessage("global", "안녕하세요!");
}
}