27. 内容支持Part 3: 组播、聊天¶
组播和聊天是向连到频道上的所有用户传输消息的功能。 此时,当用户属于同一频道时,即使属于不同服务器,也能收到消息。
组播和聊天的差异如下。
组播对传输的消息没有限制。 即可自由发送
JSON
或Protobuf
,相关消息将原封不动地传输给已连接在频道上的所有用户。Tip
由于它具有随意发送消息,接受共享的特性,所以组播功能本身可作为中继服务器使用。
聊天是专门负责玩家之间收发聊天消息的功能。 因此只能输入字符串,且仅能传输相应的字符串。 聊天作为组播的一种特殊情况,使用组播功能进行处理。
27.1. 使用组播功能时所需要的设置¶
27.1.1. 设置MANIFEST¶
按如下所示,设置MANIFEST.json文件。
Important
为了使用组播和聊天功能,RpcService须要被激活。与 RpcService
的设置有关的具体说明,请参考 分布式处理功能的设置参数 。
{
"dependency": {
.....,
"MulticastServer": {
"max_member_count_per_channel": 0
},
....,
"RpcService": {
"rpc_enabled": true,
...
}
}
}
max_member_count_per_channel: 指定每个频道的玩家定员数量。如为0,则为无限制。如非0,则可从2个以上开始指定。(type=uint64, default=0)
Note
通道入场定员数无法设置为1人。
27.1.2. 服务器代码¶
为了使用组播和聊天功能,须要在服务器中做的工作只有 上面提到的设置MANIFEST。如使用客户端插件中的组播和 聊天功能,插件和iFun引擎将自动处理。
但是若想要对组播或聊天消息进行hooking时,可使用 멀티캐스팅 메시지 전송 후킹 中所介绍的方法。
27.1.3. 客户端代码¶
客户端的组播和聊天功能已经在客户端插件中实现。 具体内容请参考客户端插件文档的 组播和聊天 。
Important
无法在客户端同时使用组播和聊天。须要仅使用组播(FunapiMulticastClient
)或仅使用聊天(FunapiChatClient
)。
Tip
在通过Unity3d写好的 GitHub engine-plugin-unity3d 中也可以查看组播和聊天功能的使用示例源代码。请参考 GitHub FunapiMulticastClient 。
27.2. 멀티캐스팅 메시지 전송 후킹¶
아이펀 엔진에서는 전송되는 멀티캐스팅 메시지를 후킹할 수 있는 기능을 제공하고 있습니다. 메시지를 수정하거나 전송 여부를 선택할 수 있으며, 전송 완료된 메시지를 전달받을 수도 있습니다.
27.2.1. 메시지 확인하기¶
채널 안에서 전송되는 메시지를 확인할 때 사용합니다. 메시지를 수정하거나 전송 여부를 선택할 수 있습니다. 인자로 넘어오는 세션에게 별도의 게임 메시지를 보낼 수도 있습니다.
멀티캐스팅 메시지로 JSON 을 이용할 경우에는 MulticastServer::InstallJsonMessageChecker() 함수를 사용합니다. Protobuf 인 경우에는 MulticastServer::InstallProtobufMessageChecker() 함수를 사용합니다.
class MulticastServer {
public:
typedef function<
bool(const string & /*channel*/,
const string & /*sender*/,
const Ptr<Session> & /*session*/,
Json * /*message*/)> JsonMessageChecker;
typedef function<
bool(const string & /*channel*/,
const string & /*sender*/,
const Ptr<Session> & /*session*/,
const Ptr<FunMessage> & /*message*/)> ProtobufMessageChecker;
static void InstallJsonMessageChecker(
const JsonMessageChecker &json_message_checker);
static void InstallProtobufMessageChecker(
const ProtobufMessageChecker &protobuf_message_checker);
};
public static class MulticastServer
{
public delegate bool JsonMessageChecker(
string channel, string sender, Session session, ref JObject message);
public delegate bool ProtobufMessageChecker(
string channel, string sender, Session session, ref FunMessage message);
public static void InstallJsonMessageChecker (JsonMessageChecker chekcer);
public static void InstallProtobufMessageChecker (ProtobufMessageChecker chekcer);
}
27.2.1.1. 예제 - 메시지 변경하기¶
아래 예제에서는 JSON 을 이용한 채팅 메시지의 내용을 변경해보겠습니다. 전달되는 JSON 메시지는 다음과 같다고 가정하겠습니다.
{
"message": "Hello World"
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | bool CheckJsonMessage(const string &channel, const string &sender,
const Ptr<Session> &session, Json *message) {
LOG_ASSERT(message != NULL);
LOG_ASSERT(message->HasAttribute("message", Json::kString));
// 메시지를 변경합니다.
(*message)["message"] = "Hello iFun";
// true 를 반환하여 변경된 message 를 전송합니다.
return true;
}
void Install() {
MulticastServer::InstallJsonMessageChecker(CheckJsonMessage);
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public static bool CheckJsonMessage(
string channel, string sender, Session session, ref JObject message)
{
if (message["message"] != null) {
// 메시지를 변경합니다.
message["message"] = "Hello iFun";
}
return true;
}
public static bool Install(ArgumentMap arguments)
{
MulticastServer.InstallJsonMessageChecker(CheckJsonMessage);
}
|
27.2.1.2. 예제 - 메시지 전송 실패 처리하기¶
다음은 1초안에 2회 이상 채팅 메시지를 전송할 경우 실패 처리하는 예제입니다. 실패하면 예제에서는 sc_chat_error 라는 가상의 메시지 타입으로 메시지를 보냅니다. 엔진에서는 메시지 타입명과 내용을 제한하지 않기 때문에 게임에 맞게 메시지를 만들어서 보낼 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | bool CheckJsonMessage(const string &channel, const string &sender,
const Ptr<Session> &session, Json *message) {
LOG_ASSERT(message != NULL);
LOG_ASSERT(message->HasAttribute("message", Json::kString));
// 마지막 채팅 시간을 검사합니다.
int64_t last_chat_time = 0;
if (session->GetFromContext("last_chat_time", &last_chat_time)) {
int64_t elapsed_time = WallClock::GetTimestampInSec() - last_chat_time;
if (elapsed_time < 1) {
// 1초안에 2회 이상 채팅은 금지되어 있습니다.
Json response;
response["result"] = false;
response["message"] = "your error message.";
session->SendMessage("sc_chat_error", response, kDefaultEncryption, kTcp);
return false;
}
}
// 만약 다른 조건이 있다면 여기서 처리합니다.
last_chat_time = WallClock::GetTimestampInSec();
session->AddToContext("last_chat_time", last_chat_time);
(*message)["message"] = "Hello iFun";
return true;
}
void Install() {
MulticastServer::InstallJsonMessageChecker(CheckJsonMessage);
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | public static bool JsonChecker(
string channel, string sender, Session session, ref JObject message)
{
long last_chat_time = 0;
if (session.GetFromContext("last_chat_time", out last_chat_time))
{
long elapsed_time = WallClock.GetTimestampInSec() - last_chat_time;
if (elapsed_time < 1)
{
// 1초안에 2회 이상 채팅은 금지되어 있습니다.
JObject response = new JObject();
response["result"] = false;
response["message"] = "your error message.";
session.SendMessage("sc_chat_error", response,
Session.Encryption.kDefault, Session.Transport.kTcp);
return false;
}
}
last_chat_time = WallClock.GetTimestampInSec();
session.AddToContext("last_chat_time", last_chat_time);
if (message["message"] != null) {
// 메시지를 변경합니다.
message["message"] = "Hello iFun";
}
return true;
}
public static bool Install(ArgumentMap arguments)
{
MulticastServer.InstallJsonMessageChecker(CheckJsonMessage);
}
|
27.2.2. 메시지 전달받기¶
전송 완료된 메시지를 전달받고 싶을 때 사용합니다. 전송 완료된 메시지를 로그로 기록하고 싶다면 이 기능을 사용하면 됩니다.
멀티캐스팅 메시지로 JSON 을 이용할 경우에는 MulticastServer::InstallJsonMessageHook() 함수를 사용합니다. Protobuf 인 경우에는 MulticastServer::InstallProtobufMessageHook() 함수를 사용합니다.
class MulticastServer {
public:
typedef function<
void(const string & /*channel*/,
const string & /*sender*/,
const SessionId & /*session_id*/,
const Json & /*message*/)> JsonMessageHook;
typedef function<
void(const string & /*channel*/,
const string & /*sender*/,
const SessionId & /*session_id*/,
const Ptr<const FunMessage> & /*message*/)> ProtobufMessageHook;
static void InstallJsonMessageHook(const JsonMessageHook &hook);
static void InstallProtobufMessageHook(const ProtobufMessageHook &hook);
};
public static class MulticastServer
{
public delegate void JsonMessageHook (string channel, string sender,
Guid session_id, JObject message);
public delegate void ProtobufMessageHook (string channel, string sender,
Guid session_id, FunMessage message);
public static void InstallJsonMessageHook (JsonMessageHook hook);
public static void InstallProtobufMessageHook (ProtobufMessageHook hook);
}
27.2.3. 예제: 채널 내 메시지를 로그로 남기기¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | void OnJsonHook(const string &channel,
const string &sender,
const SessionId &session_id,
const Json &message) {
// message 에는 hello 가 있다고 가정하겠습니다.
const string msg = message["hello"].GetString();
LOG(INFO) << "OnJsonHook: "
<< "channel=" << channel
<< ", sender=" << sender
<< ", session_id=" << session_id
<< ", message=" << msg
<< ", json_message=" << message.ToString();
}
void OnProtobufHook(const string &channel,
const string &sender,
const SessionId &session_id,
const Ptr<const FunMessage> &message) {
// FunMulticastMessage 를 확장하여 만든 PbufHelloMessage 를
// 전송했다고 가정하겠습니다.
const FunMulticastMessage &multicast_msg = message->GetExtension(multicast);
const PbufHelloMessage &pbuf_hello_msg = multicast_msg.GetExtension(pbuf_hello);
const string &msg = pbuf_hello_msg.message();
LOG(INFO) << "OnProtobufHook: "
<< "channel=" << channel
<< ", sender=" << sender
<< ", session_id=" << session_id
<< ", message=" << msg
<< ", pbuf_message=" << message->ShortDebugString();
}
void Install() {
MulticastServer::InstallJsonMessageHook(OnJsonHook);
MulticastServer::InstallProtobufMessageHook(OnProtobufHook);
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | static string kJsonHookMsgFormat = String.Join(
"", new string[]{
"OnJsonHook: channel= {0},",
"sender= {1},",
"session_id= {2},",
"message={3},",
"json_message = {4}"});
static string kPbufHookMsgFormat = String.Join(
"", new string[]{
"OnProtobufHook: channel= {0},",
"sender= {1},",
"session_id= {2},",
"message={3},",
"json_message = {4}"});
public static void OnJsonHook(string channel, string sender,
Guid session_id, JObject message)
{
// message 에는 hello 가 있다고 가정하겠습니다.
string msg = (string) message ["hello"];
Log.Info (kJsonHookMsgFormat, channel, sender,
session_id.ToString(), msg, message.ToString());
}
public static void OnProtobufHook(string channel, string sender,
Guid session_id, FunMessage message)
{
FunMulticastMessage multicast_msg;
if (!message.TryGetExtension_multicast (out multicast_msg)) {
return;
}
// FunMulticastMessage 를 확장하여 만든 PbufHelloMessage 를
// 전송했다고 가정하겠습니다.
PbufHelloMessage hello_msg;
if (!multicast_msg.TryGetExtension_pbuf_hello (out hello_msg)){
return;
}
Log.Info (kPbufHookMsgFormat, channel, sender, session_id.ToString (),
hello_msg, message.ToString ());
}
public static void Install(ArgumentMap arguments)
{
MulticastServer.InstallJsonMessageHook (OnJsonHook);
MulticastServer.InstallProtobufMessageHook (OnProtobufHook);
}
|