跳转到主要内容

ChatModels

聊天系统使用的数据模型及接口定义。

IChatListener 接口

用于接收聊天事件的监听器接口。

方法说明
OnConnected服务器连接成功时调用
OnDisconnected服务器断开连接时调用
OnError发生错误时调用
OnChannels接收频道列表时调用
OnSubscribed订阅频道时调用
OnUnSubscribed取消订阅频道时调用
OnPublicMessage接收公开消息时调用
OnPrivateMessage接收私密消息时调用
OnNotifyMessage接收通知消息时调用
OnPlayerOnline接收玩家在线状态时调用

数据模型

FChatServerResponse

字段类型说明
ErrorCodeFString错误代码
MessageFString消息
ServersTArray<FChatServerItem>服务器 列表

FChatServerItem

字段类型说明
AddrFString服务器地址
Portint32服务器端口
SecureFStringSSL 使用 与否 (Y/N)

FChatMessageModel

字段类型说明
midFString消息 ID
typeuint8事件 类型
errorint32错误代码
gameIdFString游戏 ID
serviceKeyFString服务 Key
channelIdFString频道 ID
userUniqueIdFString用户唯一ID
userNameFString用户 名称
privateToUserUniqueIdFString私密消息目标ID
messageFString消息内容
channelsTArray<FChatChannelInfo>频道 信息 数组
playersTArray<FChatPlayerInfo>玩家信息 数组
onlinePlayersTArray<FString>在线 玩家 ID 数组
prevMessageCountint32获取之前消息的数量
ipAddrFStringIP地址
createdAtFString创建日期

FChatUserInfo

字段类型说明
visitorIdFString访客ID
visitorNameFString访客名称

FChatChannelInfo

字段类型说明
channelFString频道名称
countint32在线人数

FChatPlayerInfo

字段类型说明
userUniqueIdFString用户唯一ID
onlineFString在线 状态

FChatFilterResponse

字段类型说明
ErrorCodeFString错误代码
MessageFString消息
FilterWordsTArray<FString>过滤 词 列表

Unreal C++实现

头文件 (ChatModels.h)

// ChatModels.h
#pragma once

#include "CoreMinimal.h"
#include "Json.h"
#include "JsonUtilities.h"
#include "ChatModels.generated.h"

// 채팅 이벤트 리스너 인터페이스
class IChatListener
{
public:
virtual ~IChatListener() = default;
virtual void OnConnected() = 0;
virtual void OnDisconnected() = 0;
virtual void OnError(const FString& Code, const FString& Message) = 0;
virtual void OnChannels(const TArray<struct FChatChannelInfo>& Channels) = 0;
virtual void OnSubscribed(const struct FChatUserInfo& User) = 0;
virtual void OnUnSubscribed(const struct FChatUserInfo& User) = 0;
virtual void OnPublicMessage(const struct FChatUserInfo& Sender, const FString& Message) = 0;
virtual void OnPrivateMessage(const struct FChatUserInfo& Sender, const FString& Message) = 0;
virtual void OnNotifyMessage(const struct FChatUserInfo& Sender, const FString& Message) = 0;
virtual void OnPlayerOnline(const TArray<struct FChatPlayerInfo>& Players) = 0;
};

// 서버 응답 구조체
USTRUCT(BlueprintType)
struct FChatServerItem
{
GENERATED_BODY()

UPROPERTY()
FString Addr;

UPROPERTY()
int32 Port;

UPROPERTY()
FString Secure;

void FromJson(const TSharedPtr<FJsonObject>& JsonObject)
{
JsonObject->TryGetStringField(TEXT("Addr"), Addr);
JsonObject->TryGetNumberField(TEXT("Port"), Port);
JsonObject->TryGetStringField(TEXT("Secure"), Secure);
}
};

USTRUCT(BlueprintType)
struct FChatServerResponse
{
GENERATED_BODY()

UPROPERTY()
FString ErrorCode;

UPROPERTY()
FString Message;

UPROPERTY()
TArray<FChatServerItem> Servers;

bool FromJson(const FString& JsonString)
{
TSharedPtr<FJsonObject> JsonObject;
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);

if (FJsonSerializer::Deserialize(Reader, JsonObject) && JsonObject.IsValid())
{
JsonObject->TryGetStringField(TEXT("ErrorCode"), ErrorCode);
JsonObject->TryGetStringField(TEXT("Message"), Message);

const TArray<TSharedPtr<FJsonValue>>* ServersArray;
if (JsonObject->TryGetArrayField(TEXT("Servers"), ServersArray))
{
for (const auto& ServerValue : *ServersArray)
{
FChatServerItem Item;
Item.FromJson(ServerValue->AsObject());
Servers.Add(Item);
}
}
return true;
}
return false;
}
};

// 채팅 사용자 정보
USTRUCT(BlueprintType)
struct FChatUserInfo
{
GENERATED_BODY()

UPROPERTY()
FString visitorId;

UPROPERTY()
FString visitorName;
};

// 채널 정보
USTRUCT(BlueprintType)
struct FChatChannelInfo
{
GENERATED_BODY()

UPROPERTY()
FString channel;

UPROPERTY()
int32 count;

void FromJson(const TSharedPtr<FJsonObject>& JsonObject)
{
JsonObject->TryGetStringField(TEXT("channel"), channel);
JsonObject->TryGetNumberField(TEXT("count"), count);
}
};

// 플레이어 온라인 정보
USTRUCT(BlueprintType)
struct FChatPlayerInfo
{
GENERATED_BODY()

UPROPERTY()
FString userUniqueId;

UPROPERTY()
FString online;

void FromJson(const TSharedPtr<FJsonObject>& JsonObject)
{
JsonObject->TryGetStringField(TEXT("userUniqueId"), userUniqueId);
JsonObject->TryGetStringField(TEXT("online"), online);
}
};

// 채팅 메시지 모델
USTRUCT(BlueprintType)
struct FChatMessageModel
{
GENERATED_BODY()

UPROPERTY()
FString mid;

UPROPERTY()
uint8 type;

UPROPERTY()
int32 error;

UPROPERTY()
FString gameId;

UPROPERTY()
FString serviceKey;

UPROPERTY()
FString channelId;

UPROPERTY()
FString userUniqueId;

UPROPERTY()
FString userName;

UPROPERTY()
FString privateToUserUniqueId;

UPROPERTY()
FString message;

UPROPERTY()
TArray<FChatChannelInfo> channels;

UPROPERTY()
TArray<FChatPlayerInfo> players;

UPROPERTY()
TArray<FString> onlinePlayers;

UPROPERTY()
int32 prevMessageCount;

UPROPERTY()
FString ipAddr;

UPROPERTY()
FString createdAt;

FString ToJson() const
{
TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject);

JsonObject->SetStringField(TEXT("mid"), mid);
JsonObject->SetNumberField(TEXT("type"), type);
JsonObject->SetStringField(TEXT("gameId"), gameId);
JsonObject->SetStringField(TEXT("serviceKey"), serviceKey);
JsonObject->SetStringField(TEXT("channelId"), channelId);
JsonObject->SetStringField(TEXT("userUniqueId"), userUniqueId);
JsonObject->SetStringField(TEXT("userName"), userName);
if (!privateToUserUniqueId.IsEmpty())
JsonObject->SetStringField(TEXT("privateToUserUniqueId"), privateToUserUniqueId);
if (!message.IsEmpty())
JsonObject->SetStringField(TEXT("message"), message);
JsonObject->SetNumberField(TEXT("prevMessageCount"), prevMessageCount);
JsonObject->SetStringField(TEXT("ipAddr"), ipAddr);
JsonObject->SetStringField(TEXT("createdAt"), createdAt);

if (onlinePlayers.Num() > 0)
{
TArray<TSharedPtr<FJsonValue>> PlayersArray;
for (const FString& PlayerId : onlinePlayers)
{
PlayersArray.Add(MakeShareable(new FJsonValueString(PlayerId)));
}
JsonObject->SetArrayField(TEXT("onlinePlayers"), PlayersArray);
}

FString OutputString;
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&OutputString);
FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer);
return OutputString;
}

bool FromJson(const FString& JsonString)
{
TSharedPtr<FJsonObject> JsonObject;
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);

if (FJsonSerializer::Deserialize(Reader, JsonObject) && JsonObject.IsValid())
{
JsonObject->TryGetStringField(TEXT("mid"), mid);
int32 TypeInt;
if (JsonObject->TryGetNumberField(TEXT("type"), TypeInt))
type = static_cast<uint8>(TypeInt);
JsonObject->TryGetNumberField(TEXT("error"), error);
JsonObject->TryGetStringField(TEXT("gameId"), gameId);
JsonObject->TryGetStringField(TEXT("serviceKey"), serviceKey);
JsonObject->TryGetStringField(TEXT("channelId"), channelId);
JsonObject->TryGetStringField(TEXT("userUniqueId"), userUniqueId);
JsonObject->TryGetStringField(TEXT("userName"), userName);
JsonObject->TryGetStringField(TEXT("privateToUserUniqueId"), privateToUserUniqueId);
JsonObject->TryGetStringField(TEXT("message"), message);
JsonObject->TryGetNumberField(TEXT("prevMessageCount"), prevMessageCount);
JsonObject->TryGetStringField(TEXT("ipAddr"), ipAddr);
JsonObject->TryGetStringField(TEXT("createdAt"), createdAt);

const TArray<TSharedPtr<FJsonValue>>* ChannelsArray;
if (JsonObject->TryGetArrayField(TEXT("channels"), ChannelsArray))
{
for (const auto& ChannelValue : *ChannelsArray)
{
FChatChannelInfo Info;
Info.FromJson(ChannelValue->AsObject());
channels.Add(Info);
}
}

const TArray<TSharedPtr<FJsonValue>>* PlayersArray;
if (JsonObject->TryGetArrayField(TEXT("players"), PlayersArray))
{
for (const auto& PlayerValue : *PlayersArray)
{
FChatPlayerInfo Info;
Info.FromJson(PlayerValue->AsObject());
players.Add(Info);
}
}

return true;
}
return false;
}
};

// 금칙어 필터 응답
USTRUCT(BlueprintType)
struct FChatFilterResponse
{
GENERATED_BODY()

UPROPERTY()
FString ErrorCode;

UPROPERTY()
FString Message;

UPROPERTY()
TArray<FString> FilterWords;

bool FromJson(const FString& JsonString)
{
TSharedPtr<FJsonObject> JsonObject;
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);

if (FJsonSerializer::Deserialize(Reader, JsonObject) && JsonObject.IsValid())
{
JsonObject->TryGetStringField(TEXT("ErrorCode"), ErrorCode);
JsonObject->TryGetStringField(TEXT("Message"), Message);

const TArray<TSharedPtr<FJsonValue>>* WordsArray;
if (JsonObject->TryGetArrayField(TEXT("FilterWords"), WordsArray))
{
for (const auto& WordValue : *WordsArray)
{
FilterWords.Add(WordValue->AsString());
}
}
return true;
}
return false;
}
};