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", "안녕하세요!");
}
}