37. Client programming part 1: Plugins

You can use client plugins for network communication, to check announcements, to download resources, etc. Client plugins that can be used in Unity, Unreal Engine 4, and Cocos2d-x are currently supported. You can download client plugins from GitHub.

This documentation will explain how to use client plugins as well as provide and explain tips and warnings. The explanation below was written based on a Unity plugin.

37.1. Summary

37.1.1. What is a session?

Server-client network connections are managed through sessions. Sessions can be directly closed or maintained as long as there is no timeout if the network communication lasts longer than a set time. That is, a session can be maintained on the server even if the network connection is closed, and if there is a session with the same ID upon reconnection, messages continue to be transmitted as if there had been no disconnection.

One session can have up to 3 transport connections for each of TCP, UDP, and HTTP. If you want several connections with the same protocol, you also need to create several sessions.

Tip

Details about internal session implementation are explained in (Advanced) iFun Engine Network Stack.

37.1.1.1. Session Id

When the server is contacted, a session ID is issued by the first server, and all messages transmitted to and from the server later include this session ID.

If you use JSON, the session ID is a text string of 36 bytes. You can use 16-byte arrays if using Protobuf, or you can use 36-byte text strings as with JSON. As all messages transmitted to and from the server include the session ID, you can reduce network usage by using a 16-byte array.

To use a session ID as a byte array in Protobuf, the server MANIFEST file send_session_id_as_string should be set to false. This option’s default value is true. If you are using JSON, this option does nothing, and you should keep using 36-byte text strings.

Tip

For development convenience, the plugin’s initial setting is to request a new session ID for each connection. If you want clients to keep using the same session ID, use the Session Reliability option.

37.1.1.2. FunapiSession classes

FunapiSession classes were added to replace FunapiNetwork classes to use plugins more easily and conveniently.

FunapiNetwork is no longer updated. (After plugin version 158)

FunapiSession classes have most of the features of the previous FunapiNetwork classes, and interface and callback functions have been made more concise. Anyone using the previous FunapiNetwork can easily switch to FunapiSession by familiarizing themselves with a few changes.

Please refer to 클라이언트 플러그인 documentation for help with FunapiNetwork.

37.1.1.3. Session reliability options

Session reliability is a Boolean parameter sent when generating a FunapiSession class.

This option maintains the same session even if connection to the server is lost and restored. You can omit the authentication process required after server reconnection and keep the same user data values on the server as well. This option is recommended in environments like mobile games with unstable connections.

Tip

Session reliability can only be used with the TCP protocol.

The server and client settings must always be the same for session reliability. If the server MANIFEST file’s use_session_reliability option is set to true, when the FunapiSession class is also created on the client, true must be passed to session_reliability parameters. If these values are different, the below log is output on the server.

// 서버는 Session reliability 옵션이 꺼져 있고 클라이언트는 켜져 있을 경우...
message with seq number. but seq number validation disabled: sid=..., transport_protocol=Tcp

// 서버는 Session reliability 옵션이 켜져 있고 클라이언트는 꺼져 있을 경우...
message without seq number: session_id=...

37.2. Connecting to a server

This section provides a simple introduction of how to use and implement plugins with sample code. You can see all code in the sample project included in the plugin.

37.2.1. Creating FunapiSession objects

This is how to generate FunapiSession classes to communicate with the server.

// FunapiSession 객체를 생성합니다.
// 전달하는 파라미터는 서버 주소와 Session reliability 옵션 사용 여부입니다.
FunapiSession session = FunapiSession.Create("127.0.0.1", true);

// 받은 메시지를 처리하기 위한 콜백을 등록합니다.
session.ReceivedMessageCallback += onReceivedMessage;

// 이벤트 콜백을 등록합니다.
session.SessionEventCallback += onSessionEvent;
session.TransportEventCallback += onTransportEvent;
session.TransportErrorCallback += onTransportError;

// Transport 옵션과 port를 지정한 후 연결을 시도합니다.
ushort port = getPort(protocol, encoding);
TransportOption option = makeOption(protocol);

session.Connect(protocol, encoding, port, option);

Callbacks used in the plugin use Unity event objects. As in the example, register callbacks as events using the ‘+=’ operators. You can register several callbacks in one event.

The FunapiSession.Connect function in the last line uses set parameters to generate a transport and attempt connection.

If this protocol‘s transport already exists, it can be reused without reconnecting. In this case, the option parameter is ignored, and if reconnection is made using a particular transport, it is best to use only the FunapiSession.Connect function designated in the protocol.

The code below declares``protocol`` and encoding types that can be used in the FunapiSession.Connect function.

// 프로토콜은 TCP, UDP, HTTP 3가지 타입을 지원합니다.
public enum TransportProtocol
{
  kDefault = 0,
  kTcp,
  kUdp,
  kHttp
};

// 메시지 인코딩 방식은 JSON과 Protobuf 2가지 방법이 있습니다.
public enum FunEncoding
{
  kNone,
  kJson,
  kProtobuf
}

37.2.2. Event callback function

FunapiSession has callback functions to report session status changes or transport status changes. There is also a separate callback function to report internal transport errors. When an error occurs in transport, the error callback and transport event callback can be called together.

37.2.2.1. Session Event

If the callback function is registered in SessionEventCallback, the following session-related event notifications can be received every time the session status changes.

public enum SessionEventType
{
  kOpened,           // 세션이 처음 연결되면 호출됩니다. 같은 세션으로 재연결시에는 호출되지 않습니다.
  kChanged,          // 세션 Id가 변경되면 호출됩니다. 정상적인 상황이라면 이 이벤트는 발생되지 않습니다.
  kStopped,          // 세션에 연결된 모든 Transport의 연결이 끊기면 호출됩니다.
  kClosed,           // 세션이 닫히면 호출됩니다. Transport의 연결이 끊겼다고 세션이 닫히지는 않습니다.
                    // 세션은 서버에서 명시적으로 Close가 호출되거나 세션 타임아웃이 되었을 때 종료됩니다.
  kRedirectStarted,  // Redirect 관련 이벤트는 아래 [서버간 이동] 항목을 참고해주세요.
  kRedirectSucceeded,
  kRedirectFailed
};

The SessionEventCallback in FunapiSession is announced as follows.

void SessionEventHandler (SessionEventType type, string session_id);

37.2.2.2. Transport Event

Events related to transport are as follows. Events that occur when all connections fail or are disconnected, excluding kStarted. kStopped events that occur upon normal disconnection or when the connection is closed due to an error, and always occur when transport is disconnected.

public enum TransportEventType
{
  kStarted,              // Transport가 시작되면 호출됩니다.
  kStopped,              // Transport가 종료되면 호출됩니다.
  kConnectionFailed,     // 연결에 실패하면 호출됩니다.
  kConnectionTimedOut,   // ConnectionTimeout 시간 초과로 연결에 실패하면 호출됩니다.
  kDisconnected          // 의도치 않게 연결이 끊겼을 때 호출됩니다.
};

kDisconnected is invoked upon physical (socket) disconnection or ping timeout, and the point at which the physical disconnection is detected may vary widely depending on network status or type of device. If ping is used, it is good to judge disconnection of Disconnected events, but if ping is not used, do not judge disconnection from Disconnected events alone; it is recommended to check through periodic communication with the server.

The TransportEventCallback in FunapiSession is announced as follows.

void TransportEventHandler (TransportProtocol protocol, TransportEventType type);

37.2.2.3. Transport Error

Error type and message type in which the error occurred are sent when there is a transport error callback.

If connection was being attempted when the transport error occurred, the connection attempt is ended internally and a kConnectionFailed event is invoked. If the error occurred while connected to the server, only the error is sent and the connection is not closed. However, most errors occur when connection is impossible or when messages can’t be transmitted to and from the server, so if a transport error occurs, it is better to close that connection and try to reconnect.

public class TransportError
{
  public enum Type
  {
    kNone,
    kStartingFailed,      // Transport 초기화에 실패했을 때 호출됩니다.
    kConnectingFailed,    // TCP 연결에 실패했을 때 호출됩니다.
    kInvalidSequence,     // 서버와 메시지 동기화에 실패했을 때 호출됩니다.
    kEncryptionFailed,    // 메시지 암호화에 실패했을 때 호출됩니다.
    kSendingFailed,       // 메시지를 보내는 과정에서 오류가 발생했을 때 호출됩니다.
    kReceivingFailed,     // 메시지를 받는 과정에서 오류가 발생했을 때 호출됩니다.
    kRequestFailed,       // HTTP의 요청에 실패했을 때 호출됩니다.
    kDisconnected         // 소켓 연결이 끊겼을 때 호출됩니다.
  }

  public Type type = Type.kNone;
  public string message = null;
}

The TransportErrorCallback in FunapiSession is announced as follows.

void TransportErrorHandler (TransportProtocol protocol, TransportError type);

37.2.3. Transport options

You can send transport options as parameters when invoking FunapiSession’s connect function. If this value is null, the default value is used. You need to create and designate TransportOption classes depending on the protocol to be used (TCP, UDP, HTTP). For example, if using TCP, create and send the TcpTransportOption class.

TransportOption class codes are as follows.

// Transport 옵션의 기본 클래스입니다.
// UDP 연결에만 이 기본 클래스를 사용할 수 있습니다.
public class TransportOption
{
    // 암호화 타입을 지정합니다.
    // 서버에서 암호화를 사용할 경우 이 값을 반드시 같은 타입 값으로 입력해야 합니다.
    // 암호화를 사용하지 않을 경우 이 값을 변경하지 마세요.
    public EncryptionType Encryption = EncryptionType.kDefaultEncryption;

    // 메시지에 sequence number를 붙여서 메시지의 유효성을 보장해주는 옵션입니다.
    // Session reliability 옵션을 사용하지 않고 메시지의 유효성만 보장하고 싶을 때 사용하면 됩니다.
    // Session reliability 옵션을 사용하면 이 옵션과 상관없이 메시지의 유효성을 보장해줍니다.
    // 이 옵션은 TCP, HTTP 프로토콜에서만 사용 가능합니다.
    public bool SequenceValidation = false;

    // 서버와 연결할 때 Timeout 시간을 정하고 싶다면 이 값을 수정하면 됩니다.
    // 지정한 시간 내에 연결이 되지 않으면 ConnectionTimeoutCallback 함수가 호출됩니다.
    public float ConnectionTimeout = 0f;
}

// TCP Transport 옵션 클래스입니다.
// TransportOption 값도 포함하면서 TCP에만 적용되는 옵션이 추가되어 있습니다.
public class TcpTransportOption : TransportOption
{
    // 이 값이 true일 경우 처음 서버에 연결할 때 연결 실패시 3-4회 정도 재연결을 시도합니다.
    // 같은 객체로 Stop 후 재연결시에는 연결에 실패해도 재시도하지 않습니다.
    public bool AutoReconnect = false;

    // 이 값을 true로 하면 네이글 알고리즘을 사용하지 않습니다.
    // 네이글 알고리즘은 효율적인 네트워크 사용을 위해 작은 크기의 패킷을 모아서 전송하는 기능입니다.
    // 기본 값은 false로 네이글 알고리즘을 사용합니다.
    public bool DisableNagle = false;

    // 클라이언트 핑 사용 옵션입니다.
    public bool EnablePing = false;
    // 핑 값을 로그로 표시할지 여부를 결정합니다.
    public bool EnablePingLog = false;
    // 핑 메시지를 보내는 간격에 대한 시간 값입니다.
    public int PingIntervalSeconds = 0;
    // 서버로부터 핑에 대한 응답을 기다리는 최대 시간 값입니다.
    // 이 시간 내에 핑 응답이 하나도 오지 않는다면 서버와의 연결이 끊긴 것으로 보고 Disconnect 처리됩니다.
    // 보통 PingIntervalSeconds 값의 3-4배 이상의 시간을 권장합니다.
    public float PingTimeoutSeconds = 0f;

    // 위의 핑 관련 옵션 값을 한 번에 설정할 수 있는 핑 사용 설정 함수입니다.
    public void SetPing (int interval, float timeout, bool enable_log = false);
}

// HTTP Transport 옵션 클래스입니다.
public class HttpTransportOption : TransportOption
{
    // HTTPs 사용 여부에 대한 옵션입니다.
    // 서버가 HTTPs일 경우 이 값을 true로 입력해야 합니다.
    public bool HTTPS = false;
    // UnityEngine.WWW 클래스 사용 여부에 대한 옵션입니다.
    public bool UseWWW = false;
}

In the plugin, the HttpWebRequest class is used when an HTTP request is handled. If this function is used in Windows with Unity Editor, the editor may sometimes not respond. (A problem that occurs because the socket is not explicitly closed) A UseWWW option has been added so that UnityEngine’s WWW classes can be used to avoid this problem. As this problem only occurs in the editor, we recommend leaving this option as false by default and using a WWW class only when executing in the editor.

Sample code setting transport options is in the makeOption function in the sample project Tester.cs file.

37.3. Sending and receiving messages

37.3.1. Sending messages

If you want to send messages, you can call the FunapiSession class SendMessage function.

public void SendMessage (string msg_type, object message,
                         TransportProtocol protocol = TransportProtocol.kDefault,
                         EncryptionType enc_type = EncryptionType.kDefaultEncryption);

msg_type is the string type parameter showing the message type. If sending messages through Protobuf, you can also use enum MessageType instead of string type.

message is the object type for the message including actual data. You can send both JSON and Protobuf messages.

If protocol is not set, messages are sent in the initial protocol used to connect to FunapiSession. If you have several transport protocols registered and want to send a message using a particular one, designate the protocol value.

If you want to set a default protocol when sending messages with connections to FunapiSession through several protocols, you can modify FunapiSession’s DefaultProtocol properties.

session.DefaultProtocol = TransportProtocol.kHttp;

enc_type is the encryption type. If the type of encryption used is not set, the default type is used, and an error occurs if this value is entered when encryption is not used.

If several types of encryption are allowed in one protocol on the server, you can select the encryption type to be used when sending messages. To learn more about encryption, please read Message encryption.

37.3.1.1. Sending JSON messages

Dictionary<string, object> message = new Dictionary<string, object>();
message["message"] = "hello world";
session.SendMessage("echo", message);

The plugin uses MiniJSON library for JSON. The MiniJSON data type is Dictionary<string, object>. If necessary, you can use other JSON libraries. To learn more about this, please read:ref:client-plugin-json-helper.

37.3.1.2. Sending Protobuf messages

PbufEchoMessage echo = new PbufEchoMessage();
echo.msg = "hello proto";
FunMessage message = FunapiMessage.CreateFunMessage(echo, MessageType.pbuf_echo);
session.SendMessage(MessageType.pbuf_echo, message);

Protobuf messages are FunMessage by default. User messages are in extended form. Extended fields from 0-15 are reserved. User messages can use fields starting at 16.

MessageType is the enum list also created when making message DLL by building .proto files on the server. With this, you can use message names instead of numbers for extended messages.

Important

To use Protobuf-net messages in Unity, you need to follow an additional build process. See Using Protobuf in Unity for an explanation.

37.3.2. Message reception

To receive messages from the server, you need to register a message callback function. ReceivedMessageCallback is declared as follows.

void ReceivedMessageHandler (string msg_type, object message);

The reason the data type is an object is because messages can be JSON or Protobuf. You can cast message objects as JSON or Protobuf depending on the message type registered in the handler.

An example of a message handler follows. All code can be found in the Tester.Session.cs file.

// 받은 메시지를 처리할 콜백을 등록합니다.
session.ReceivedMessageCallback += onReceivedMessage;

// 기다리는 메시지에 timeout을 정해서 알림을 받고 싶다면 이 콜백을 등록합니다.
// 메시지가 지정한 시간내에 오지 않으면 이 콜백 함수가 호출됩니다.
session.ResponseTimeoutCallback += onResponseTimedOut;

// 메시지를 처리하는 방식은 정해진 형식이 없으며 사용하기 편한 형태로 쓰시면 됩니다.
// 여기에서는 받은 메시지를 처리하기 위해 각각의 메시지를 Key-Value 방식으로 저장하겠습니다.
message_handler_["echo"] = onEcho;
message_handler_["pbuf_echo"] = onEchoWithProtobuf;

Since already deserialized messages are sent with the ReceivedMessageCallback function inside the plugin, there is no need to deserialize them again. Just cast received object messages to use.

Implementation of a message callback function registered in message_handler_ is as follows:

// JSON 메시지에 대한 처리 함수입니다.
void onEcho (object message)
{
    // 예제에서는 MiniJSON을 사용합니다.
    // 다른 JSON 라이브러리를 사용하고 싶다면 아래 JSON Helper 관련 설명을 참고해주세요.
    FunDebug.Assert(message is Dictionary<string, object>);
    // 이미 Deserialize 된 메시지를 문자열로 출력하기 위해 다시 Serialize 합니다.
    string strJson = Json.Serialize(message);
    FunDebug.Log("Received an echo message: {0}", strJson);
}

// Protobuf 메시지에 대한 처리 함수입니다.
void onEchoWithProtobuf (object message)
{
    // iFun Engine에서 사용하는 protobuf 메시지는 FunMessage를 기본으로 하고
    // 사용자 메시지는 FunMessage 안에 extend 형태로 추가해서 사용합니다.
    FunDebug.Assert(message is FunMessage);
    FunMessage msg = message as FunMessage;
    // extend 된 echo 메시지를 가져옵니다.
    object obj = FunapiMessage.GetMessage(msg, MessageType.pbuf_echo);
    if (obj == null)
        return;

    PbufEchoMessage echo = obj as PbufEchoMessage;
    FunDebug.Log("Received an echo message: {0}", echo.msg);
}

37.3.3. Response Timeout

If you want to set a timeout for awaiting messages from the server, you can use the SetResponseTimeout function in FunapiSession classes to designate your desired message timeout period.

// 기다리는 메시지 타입과 시간을 지정합니다. 등록되는 순간부터 시간이 적용됩니다.
// 'sc_login' 메시지가 10초 내에 오지 않을 경우 ResponseTimeoutCallback 함수가 호출됩니다.
session.SetResponseTimeout("sc_login", 10f);

Tip

You cannot register multiples of the same message type.

If you want to reset timeout for the same messages for a one-time event, you need to invoke ReceivedMessageCallback once the message has been received or the ResponseTimeoutCallback function once the message has timed out, then set timeout again with the SetResponseTimeout function.

ResponseTimeoutCallback is declared as follows. Message type transmitted with the SetResponseTimeout function is received as a parameter. If that message is received from the server within the timeout period, this callback function is not invoked.

public delegate void ResponseTimeoutHandler (string msg_type);

37.3.4. JSON helper classes

MiniJSON is the default plugin. If you want to use another JSON library, have it inherit the JsonAccessor interface class to create a class to handle the JSON library by changing JsonHelper in FunapiMessage.

If the necessary function exists but is not in the JasonAccessor class, add it to the JsonAccessor derived class to use.

The newly created JsonAccessor class can be registered as follows: Messages sent and received later are all serialized or deserialized through the newly registered JSON class.

NewJsonAccessor json_helper = NewJsonAccessor();
FunapiMessage.JsonHelper = json_helper;

Note

The JsonAccessor class interface was changed in plugin version 164. (August 2016 update) If JsonHelper was implemented after inheriting the JsonAccessor class, you need to additionally implement it on interfaces added during plugin update.

37.4. Message encryption

Messages transmitted to and from the server can be encrypted.

37.4.1. Encryption types

Encryption types are as follows:

public enum EncryptionType
{
  // 암호화를 사용하지만 특정 메시지를 암호화하지 않은 상태로 보내고 싶을 때
  // SendMessage의 파라미터로 이 값을 전달하면 됩니다.
  kDummyEncryption,

  // iFun Engine에서 제공하는 암호화 타입입니다.
  // 메시지를 주고 받을 때마다 키 값이 변경되어 안정적인 암호화 방식입니다.
  // Tcp 프로토콜에서만 사용 가능합니다.
  kIFunEngine1Encryption,

  // iFun Engine에서 제공하는 암호화 타입입니다.
  // 고정된 암호화 키를 사용합니다. 프로토콜에 상관없이 사용 가능합니다.
  kIFunEngine2Encryption,

  // ChaCha20 암호화 타입입니다.
  // Tcp 프로토콜에서만 사용 가능합니다.
  kChaCha20Encryption,

  // Aes 128 암호화 타입입니다.
  // Tcp 프로토콜에서만 사용 가능합니다.
  kAes128Encryption
}

If encryption is set, all messages are sent encrypted, but some messages may be sent as-is without encryption if you wish. If you want to use encryption but want to send particular messages without it, set SendMessage encryption type to kDummyEncryption.

The kIFunEngine1Encryption type changes the encryption key each time messages are transmitted. This is a safe encryption method as the encryption key values are different every time, even for the same message. kIFunEngine2Encryption uses an inherent encryption key, so it is a relatively weaker encryption type than kIFunEngine1Encryption.

Tip

IFunEngine1, ChaCha20, and AES-128 encryption can only be used in the TCP protocol.

ChaCha20 and Aes128 are used with external libraries (Sodium). Files for this library are in the Plugins folder.

37.4.2. Using encryption

When TCP messages are encrypted on the server without any other setting needed, this is followed on the client. When the server is first contacted, encryption is synchronized, and when the server sends the relevant protocol’s encryption type list, the client uses the encryption method sent by the server.

If there are several types of encryption, the first type is used as the default. If no particular encryption type is set when a message is sent, the default type of encryption is used. If you want to set another default encryption type, you can set the TransportOption encryption value when the FunapiSession.Connect function is called..

TcpTransportOption option = new TcpTransportOption();
option.Encryption = EncryptionType.kIFunEngine1Encryption;

If the client uses a type of encryption not used by the server, an error occurs and messages cannot be transmitted.

For UDP and HTTP, there is no encryption type synchronization with the server after contact, so the encryption type needs to be set on the client. The only type of encryption provided by iFun Engine that can be used by UDP and HTTP is IFunEngine2Encryption. If you want to encrypt in UDP or HTTP, set this type in TransportOption encryption.

TransportOption option = new TransportOption();
option.Encryption = EncryptionType.kIFunEngine2Encryption;

Note

The client plugin distributed via GitHub does not include IFunEngine1Encryption and IFunEngine2Encryption and is a free version. You can use ChaCha20 or AES-128. Paid customers can email Funapi support to request a plugin source that includes all encryption types.

37.5. Closing connections and reconnecting

37.5.1. Closing connections

To close a connection to the server, you can invoke the FunapiSession stop function.

session.Stop();

The stop function without parameters disconnects all connected transports. If you want to disconnect a particular transport, you can send the protocol type as a parameter.

session.Stop(TransportProtocol.kTcp);

The stop function is an asynchronous method that disconnects all connected transports. The reason connections are immediately closed when stop is invoked is that queued or unsent messages left in the buffer when a transport is attempting to connect to the server are sent and then the connection is closed.

Note

The reason a transport waits when connecting to the server is that if stop is invoked during connection, iOS crashes occur due to an IL2CPP-related bug.

The function that sends unsent messages before the connection closes is only supported in TCP. If SendMessage is invoked or unsent messages are left during a TCP connection, remaining messages are all sent and then the connection is closed. Thus, even if messages like logout are sent immediately before invoking stop, they are guaranteed to be sent.

There are some cases in which the connection is closed without waiting: if the session is closed from the server, if a session must be closed for movement between servers, or if an app is closed.

37.5.2. Disconnection notification

37.5.2.1. Transport Event

When a transport connection is closed, kStopped is sent as a transport event. These events always occur when a transport connection is closed, regardless of whether it is closed due to an error or whether there is a normal disconnection.

If a transport is unintentionally disconnected, a kDisconnected event is sent as a transport event. If using ping, connection to the server is checked, and if there is no server response during a set period of time, a kDisconnected event occurs. If not using ping, it is unreasonably difficult to check disconnection in varied mobile environments through this event only. This is because it often takes a long time to detect disconnection, depending on the device and network type. If you want to check server connection using a kDisconnected event, you should use ping.

Tip

kDisconnected events do not occur in the HTTP protocol. Ping can only be used for TCP.

You can register FunapiSession’s TransportEventCallback to get transport events.

37.5.2.2. Session Event

If all transports connected to a session are disconnected, a kStopped event is sent as a session event.

Session kClosed events do not occur from transport disconnections, but from session timeout or when the server explicitly ends the session. When a message saying a session is closed is sent from the server, all transports are disconnected and a kClosed event is sent.

You can register FunapiSession’s SessionEventCallback to get session events.

37.5.3. Reconnecting

Use the FunapiSession.Connect function to reconnect after a server connection is closed. You just need to send the transport protocol type to connect with as a parameter.

public void Connect (TransportProtocol protocol)

You can use the connect function, including the TransportOption parameter, to reconnect, but you cannot change transport options once registered and, even if a new TransportOption is sent, it is ignored. Thus, it is advisable to set only the protocol to invoke the connect function during reconnection.

For TCP, even if the AutoReconnect option is set to true, reconnection is not attempted if the reconnection attempt fails. AutoReconnect is only used for the first connection.

37.6. Using ping

There is a command called ping used on the terminal to check network connection status. There is a similar ping function in the plugin as well. You can use it to check whether connection to the server is maintained.

You can continuously check connection status using this ping function in unpredictable mobile network environments. Thus, you can more quickly assess and respond to disconnections.

Tip

This function is only available for TCP.

37.6.1. Ping configuration

Ping can be set as a transport option before invoking the FunapiSession.Connect function. To use ping, invoke the SetPing function as follows or enter each individual value.

TcpTransportOption tcp_option = new TcpTransportOption();

// 이 함수만 호출하면 핑 설정이 완료됩니다.
tcp_option.SetPing(3, 20, true);

...

// 각각의 값을 따로 입력하고 싶다면 아래와 같이 하면 됩니다.

// 이 값을 true로 주면 Ping 기능을 사용할 수 있습니다.
// EnablePing의 기본 값은 false 입니다.
tcp_option.EnablePing = true;
// 핑 값을 로그로 보고 싶다면 이 값을 true로 주면 됩니다.
tcp_option.EnablePingLog = true;
// 핑 메시지를 보내는 간격을 지정합니다. (초단위)
tcp_option.PingIntervalSeconds = 3;
// 서버로부터 핑 응답을 기다리는 최대 시간을 지정합니다. (초단위)
// 이 시간 이내에 응답이 하나도 없을 경우 연결을 끊고 Disconnect 처리를 하게 됩니다.
tcp_option.PingTimeoutSeconds = 20;

If there is no response at all from the server during the timeout period, the connection is closed and handled as a disconnection. When disconnected, kDisconnected is sent as a FunapiSession TransportEventCallback.

You can learn how to activate ping on the server in Session Ping (RTT).

37.7. Server redirect

In some cases, it may be necessary to disconnect an active connection and access a new server. This process is handled in the plugin and is a server-side request, so there is no need for the client to request a server move.

37.7.1. Checking server redirect

When transfer between servers begins, its status is sent through SessionEventCallback. Below is an event type received through a callback.

public enum SessionEventType
{
    ...
    kRedirectStarted,       // 서버간 이동을 시작합니다.
    kRedirectSucceeded,     // 서버간 이동을 완료했습니다.
    kRedirectFailed         // 서버간 이동에 실패했습니다.
};

When movement between servers begins, a kRedirectStarted event is sent and the current server connection is closed. A new server connection begins. Until movement between servers is finished, session or transport events, excluding redirect events, do not occur. Even if there is an error during connection, only a debug log is output and no events occur. When movement between servers is complete, kRedirectSucceeded or kRedirectFailed events are sent.

37.7.2. Transport options

The number of protocols to close when moving between servers may vary. There is a callback function that can set transport options when a new transport is created in these cases.

public delegate TransportOption TransportOptionHandler (string flavor, TransportProtocol protocol);
public event TransportOptionHandler TransportOptionCallback;

flavor shows the server type. This type is a text string set by the server. When a redirect message is received from the server, this callback function is invoked according to the protocol configuration of the destination server. You can return options for the relevant server type and protocol.

You don’t necessarily need to register this event callback, even if the same protocol is used on the destination server or the options aren’t set up and connection is made with default options.

37.8. Multicasting and chatting

You can use the plugin’s multicasting feature to connect to a desired channel and transmit messages to and from all users on that channel. FunapiSession objects are used to connect and send messages to the server handling multicasting. You can create FunapiMulticastClient objects while sending FunapiSession objects to use multicasting.

Use FunapiMulticastClient to send messages to all users on the same channel. If _bounce is true, the message can also be received by the sender.

FunapiChatClient classes especially for chatting are derived classes that inherit FunapiMulticastClient. FunapiChatClient is good for chatting between players using classes that only transmit text.

Multicasting uses TCP, and encoding follows the method used in the connected FunapiSession. You can use JSON and Protobuf.

Important

You can only connect to one object per session in multicasting. If you connect to two or more multicasting objects, it is impossible to tell which object the message must be sent to when a multicast message is received. FunapiMulticastClient and FunapiChatClient classes cannot both be used simultaneously in the same session.

37.8.1. Multicasting interface

The following interfaces are in FunapiMulticastClient.

// FunapiMulticastClient 생성자입니다.
// FunapiSession와 주고 받을 메시지의 Encoding을 입력받습니다.
public FunapiMulticastClient (FunapiSession session, FunEncoding encoding)

// 내 id(name)입니다.
// sender를 지정하면 채널 입/퇴장이나 메시지를 보낼 때 sender를 함께 전송합니다.
// 나를 포함한 채널의 모든 유저가 해당 메시지를 보낸 이를 확인할 수 있습니다.
public string sender { set; }

// 서버와 연결되어 있는지 확인하기 위한 property 입니다.
// 서버와 연결이 되어 있다면 true를 반환합니다.
public bool Connected;

// 채널 목록을 요청하는 함수입니다.
// 채널 목록과 함께 채널에 있는 유저 수도 함께 전달됩니다.
// 요청에 성공하면 ChannelListCallback에 등록한 함수가 호출됩니다.
public void RequestChannelList ();

// channel_id 가 입장한 채널의 id 인지 확인하는 property 입니다.
// 입장한 채널이면 true를 반환합니다.
public bool InChannel (string channel_id);

// 채널에 입장할 때 사용하는 함수입니다.
// channel_id 는 입장할 channel id 입니다. 존재하지 않는 채널이면 서버에서 생성합니다.
// handler는 메시지를 전달받으면 호출되는 함수입니다.
// 이미 channel_id에 해당하는 채널에 입장했거나 서버와 연결되어 있지 않으면 false 를 반환합니다.
public bool JoinChannel (string channel_id, ChannelMessage handler);

// 채널을 나갈 때 사용하는 함수입니다.
// channel_id는 나갈 channel id 입니다.
// 입장한 채널이 없거나 서버와 연결되어 있지 않으면 false 를 반환합니다.
public bool LeaveChannel (string channel_id);

// 채널에 메시지를 전송할 때 사용하는 함수입니다.
// 이 함수는 protobuf로 메시지를 전송할 때 사용합니다.
public bool SendToChannel (FunMulticastMessage mcast_msg);

// 이 함수는 json으로 메시지를 전송할 때 사용합니다.
public bool SendToChannel (object json_msg);

// 채널에 입/퇴장을 알려주는 Handler의 delegate 입니다.
// 채널의 입/퇴장 알림을 받고 싶다면 ChannelNotify의 event 객체인
// JoinedCallback이나 LeftCallback을 등록하면 됩니다.
public delegate void ChannelNotify(string channel_id, string sender);

// 채널로부터 메시지를 받으면 호출되는 Handler의 delegate 입니다.
// 이 delegate와 동일한 타입으로 함수를 만들고 JoinChannel() 함수의 두 번째 인자로 전달합니다.
// channel_id 는 메시지를 전달받은 채널 id 입니다.
// body는 전달받은 메시지이며 Encoding에 따라서 JSON이나 Protobuf인 FunMulticastMessage가 전달됩니다.
public delegate void ChannelMessage(string channel_id, string sender, object body);

37.8.2. Example of multicasting

To prepare in advance to handle multicasting with FunapiMulticastClient, first create a FunapiSession and connect using TCP.

string kServerIp = "127.0.0.1";

// 우선 FunapiSession 을 만듭니다.
// 멀티캐스팅은 session reliability 와 상관없이 동작합니다.
// 예제의 단순함을 위해 여기서는 이 기능을 사용하지 않겠습니다.
session = FunapiSession.Create(kServerIp, false);

// Transport의 이벤트를 받기 위한 콜백함수를 등록합니다.
session.TransportEventCallback += onTransportEvent;

// 서버에 접속합니다.
// Transport의 옵션 설정은 생략하겠습니다.
session.Connect(TransportProtocol.kTcp, FunEncoding.kJson, 8012);

FunapiMulticastClient will then be made to send messages.

You can register a callback function to notify of channel entry or exit. Notifies the coming or going of all users on the channel, including you.

JoinedCallback When a user joins the channel, the function invoked is
LeftCallback When a user leaves a channel, the function invoked is
ErrorCallback When an error appears, the function sends the error code
// FunapiMulticastClient를 만듭니다.
// 위에서 만든 session과 session과 같은 encoding 타입을 전달합니다.
FunapiMulticastClient multicast = new FunapiMulticastClient(session, FunEncoding.kJson);

// 내 아이디를 입력합니다.
multicast_.sender = "my name";

// 채널 목록을 받았을 때 호출되는 콜백입니다.
multicast_.ChannelListCallback += delegate (object channel_list) {
    onMulticastChannelList(encoding, channel_list);
};

// Player가 채널에 입장하면 호출되는 콜백입니다.
multicast_.JoinedCallback += delegate(string channel_id, string sender) {
    DebugUtils.Log("JoinedCallback called. player:{0}", sender);
};

// Player가 채널에서 퇴장하면 호출되는 콜백입니다.
multicast_.LeftCallback += delegate(string channel_id, string sender) {
    DebugUtils.Log("LeftCallback called. player:{0}", sender);
};

// 에러가 발생했을 때 알림을 받는 콜백입니다.
// 에러 종류는 enum FunMulticastMessage.ErrorCode 타입을 참고해주세요.
multicast_.ErrorCallback += delegate(FunMulticastMessage.ErrorCode code) {
    // error
};

Invokes the JoinChannel() function to join a channel. When a message is sent from the channel, the onMulticastChannelReceived function is invoked.

To join several channels at once, invoke the JoinChannel() function for each channel you wish to join.

// 입장할 채널 id 를 입력합니다.
// 채널이 존재하지 않으면 JoinChannel() 함수를 호출할 때 채널이 생성됩니다.
string channel_id = "test_channel";

// 채널에 입장하기 위해 JoinChannel() 함수를 호출합니다.
// 해당 채널로부터 메시지가 전송되면 onMulticastChannelReceived 함수가 호출됩니다.
multicast.JoinChannel(channel_id, onMulticastChannelReceived);

// 채널에 메시지 전송
PbufHelloMessage hello_msg = new PbufHelloMessage();
hello_msg.message = "multicasting message";

PbufHelloMessage is in the server’s {project}_messages.proto file. When the first project is created with funapi_initiator, this is added by default. PbufHelloMessage is created for the example, and proto message names and fields on the MulticastServer are unlimited. When you create a proto message with the name you need and define the fields to use, iFun Engine recognizes these and sends them to the client. This is the same with JSON.

Default data is in the following server’s {project}_messages.proto file.

message PbufAnotherMessage {
  optional string msg = 1;
}

extend FunMessage {
  optional PbufEchoMessage pbuf_echo = 16;
  optional PbufAnotherMessage pbuf_another = 17;
}


import "funapi/service/multicast_message.proto";

message PbufHelloMessage
{
  optional string message = 1;
}
extend FunMulticastMessage
{
  optional PbufHelloMessage pbuf_hello = 9;
}

In the following onMulticastChannelReceived Protobuf sample code, the message called MulticastMessageType.pbuf_hello is not added to the plugin by default. The proto file is also built when you build the server.

At that time the PBufHelloMessage field names and extended numbers entered into extend FunMulticastMessage are automatically

recognized and enum is created. For more on this, please refer to Using Protobuf in Unity.

private void onMulticastChannelReceived (string channel_id, string sender, object body)
{
    if (multicast_.encoding == FunEncoding.kJson)
    {
        // 채널이 맞는지 확인합니다.
        string channel = FunapiMessage.JsonHelper.GetStringField(body, "_channel");
        FunDebug.Assert(channel != null && channel == channel_id);

        // 메시지를 전송할 때 "message" 필드에 전송할 메시지를 담았습니다.
        // 따라서 수신한 메시지는 mcast_msg["message"] 에 있습니다.
        string message = FunapiMessage.JsonHelper.GetStringField(body, "_message");

        // 여기서는 간단하게 로그를 출력하겠습니다.
        DebugUtils.Log("Received a multicast message from the '{0}' channel.\nMessage: {1}",
                       channel_id, message);
    }
    else
    {
        // 수신한 메시지를 Protobuf인 FunMulticastMessage로 처리합니다.
        FunMulticastMessage mcast_msg = body as FunMulticastMessage;

        // MulticastMessageType 은 서버를 빌드할 때 proto 파일도 함께 빌드하면 자동 생성됩니다.
        // 이 예제에서는 PbufHelloMessage를 사용하므로 이것의 field 이름인 pbuf_hello를 사용합니다.
        object obj = FunapiMessage.GetMulticastMessage(mcast_msg, MulticastMessageType.pbuf_hello);
        PbufHelloMessage hello_msg = obj as PbufHelloMessage;
        if (hello_msg == null)
            return;

        // 메시지를 전송할 때 PbufHelloMessage의 message field에 메시지를 저장합니다.
        // 따라서 hello_msg.message 에 전달된 메시지가 담겨 있습니다.
        DebugUtils.Log("Received a multicast message from the '{0}' channel.\nMessage: {1}",
                       channel_id, hello_msg.message);
    }
}

To send a message, invoke the SendToChannel() function.

// channel id를 입력합니다.
string channel_id = "test_channel";

if (multicast_.encoding == FunEncoding.kJson)
{
    // JSON으로 메시지를 전송합니다.
    Dictionary<string, object> mcast_msg = new Dictionary<string, object>();

    // _channel 필드에 channel id를 입력합니다.
    mcast_msg["_channel"] = channel_id;

    // 보낸 메시지를 나도 받고 싶다면 _bounce 필드값을 true로 설정합니다.
    mcast_msg["_bounce"] = true;

    // 메시지를 mcast_msg["message"] 에 담겠습니다.
    // 이는 자유롭게 변경하셔도 되며 필요하다면 다른 필드를 추가하셔도 됩니다.
    mcast_msg["message"] = "multicast test message";

    // 다른 필드 추가
    // mcast_msg["something"] = "...";

    // 멀티캐스팅 역할을 하는 서버에 메시지를 전송합니다.
    multicast.SendToChannel(mcast_msg);
}
else
{
    // protobuf로 메시지를 전송합니다.
    // PbufHelloMessage 파일은 서버의 {project}_messages.proto 파일에 정의 되어 있습니다.
    // 기본적으로 포함된 proto message인데 자유롭게 이름과 field 등을 정의하셔도 됩니다.
    PbufHelloMessage hello_msg = new PbufHelloMessage();

    // message field에 전송할 메시지를 담겠습니다.
    hello_msg.message = "multicast test message";

    // 멀티캐스팅 메시지를 전송하기 위해서 FunMulticastMessage 를 생성합니다.
    // 이 부분은 멀티캐스팅 역할을 하는 서버에서 필요한 proto message 이므로 반드시 필요한 부분입니다.
    FunMulticastMessage mcast_msg = new FunMulticastMessage();
    // channel field 에 channel id 를 입력합니다.
    mcast_msg.channel = channel_id;

    // 보낸 메시지를 나도 받고 싶다면 bounce 필드 값을 true로 설정합니다.
    mcast_msg.bounce = true;

    // protobuf 에서 제공하는 Extensible 인터페이스를 이용하여 PbufHelloMessage를 추가합니다.
    Extensible.AppendValue(mcast_msg, (int)MulticastMessageType.pbuf_hello, hello_msg);

    // 멀티캐스팅 역할을 하는 서버에 메시지를 전송합니다.
    multicast_.SendToChannel(mcast_msg);
}

When leaving the channel, the LeaveChannel() function is invoked.

// 채널 나가기
string channel_id = "test_channel";
multicast.LeaveChannel(channel_id);

37.8.3. Chatting interface

The following interfaces are available in FunapiChatClient, and the difference from FunapiMulticastClient is that messages are not received as object objects and only string are received.

// FunapiChatClient 생성자입니다.
// FunapiSession과 주고 받을 메시지의 Encoding을 입력받습니다.
public FunapiChatClient (FunapiSession session, FunEncoding encoding);

// 채널에 입장할 때 사용하는 함수입니다.
// chat_channel은 입장할 channel id 입니다. 존재하지 않는 채널이면 서버에서 생성합니다.
// my_name은 채팅에서 사용할 나의 이름입니다. FunapiMulticastClient의 sender에 값을 저장하게 됩니다.
// handler는 메시지를 전달받으면 호출되는 함수입니다.
// 이미 channel_id 에 해당하는 채널에 입장했거나 서버와 연결되어 있지 않으면 false를 반환합니다.
public bool JoinChannel (string chat_channel, string my_name, OnChatMessage handler);

// my_name 파라미터가 없는 채널에 입장할 때 사용하는 함수입니다.
// FunapiMulticastClient의 sender 값을 지정했다면 채널에 입장할 때 이 함수를 사용해도 좋습니다.
public bool JoinChannel (string channel_id, OnChatMessage handler);

// 채널을 나갈 때 사용하는 함수입니다.
// 채널에서 나갈 때는 FunapiMulticastClient의 LeaveChannel 함수를 호출하면 됩니다.
public bool LeaveChannel (string channel_id);

// 채널에 메시지를 전송할 때 사용하는 함수입니다.
// chat_channel은 메시지를 전송할 channel 의 id 입니다.
// text는 전송할 메시지입니다.
public bool SendText (string chat_channel, string text);

// 채널로부터 메시지를 받으면 호출되는 Handler의 delegate 입니다.
// 이 delegate와 동일한 타입으로 함수를 만들고 JoinChannel() 함수의 인자로 전달합니다.
public delegate void OnChatMessage(string channel_id, string sender, string text);

37.8.4. Example of chatting

FunapiChatClient uses the multicasting function, and the classes created are used very similarly to those in FunapiMulticastClient. Therefore, we will acquaint you here with basic use of FunapiChatClient.

Json is used for encoding in the example.

// FunapiSession을 생성합니다.
FunapiSession session = FunapiSession.Create(kServerIp, false);
session.TransportEventCallback += onTransportEvent;

// 서버에 연결합니다.
session.Connect(TransportProtocol.kTcp, FunEncoding.kJson, 8012);

...

// FunapiChatClient 객체를 생성합니다.
FunapiChatClient chat = new FunapiChatClient(session, FunEncoding.kJson);

// 입장하는 플레이어의 이름을 입력합니다.
chat_.sender = "my name";

// 채널 입장
string channel_id = "test_channel";
chat.JoinChannel(channel_id, onMulticastChannelReceived);

When messages are received, the onMulticastChannelReceived() function is invoked.

// 메시지를 전달받으면 호출됩니다.
// FunapiMulticastClient 와는 달리 encoding에 따라 message를 처리하지 않아도 됩니다.
// 메시지 보낸 사람인 sender와 전송된 메시지인 text를 처리하시면 됩니다.
private void onMulticastChannelReceived (string chat_channel, string sender, string text)
{
    // 여기서는 간단하게 로그를 출력하겠습니다.
    DebugUtils.Log("Received a chat channel message.\nChannel={0}, sender={1}, text={2}",
                   chat_channel, sender, text);
}

Use the SendText() function to send messages to the channel.

// 채널에 메시지 전송
string channel_id = "test_channel";
string message = "Hello World";
chat.SendText(channel_id, message);

When leaving the channel, the LeaveChannel() function is invoked.

// 채널 나가기
string channel_id = "test_channel";
chat.LeaveChannel(channel_id);

37.9. Checking announcements

If you use the announcement server provided by the Engine, you can get announcement lists from the announcement server through a client plugin and update them whenever you want.

You can request announcement lists from the server as the FunapiAnnouncement class. The following shows sample code to request announcements from the server using the FunapiAnnouncement class and parse the announcement list in the callback function. The message type is JSON.

FunapiAnnouncement announcement = new FunapiAnnouncement();

void Init ()
{
    // 공지사항은 웹 서비스입니다.
    // 서버에서 공지사항 용도로 열어 둔 ip와 port를 주소로 요청합니다.
    announcement.Init("http://127.0.0.1:8080");
    announcement.ResultCallback += OnAnnouncementResult;

    // 이 함수를 호출하면 목록을 가져오거나 이미 있을경우 갱신해줍니다.
    announcement.UpdateList();
}

void OnAnnouncementResult (AnnounceResult result)
{
    // 결과가 Success가 아닐경우
    if (result != AnnounceResult.kSuccess)
        return;

    // 메시지는 JSON 타입이며 MiniJSON을 사용합니다.
    if (announcement.ListCount > 0)
    {
        for (int i = 0; i < announcement.ListCount; ++i)
        {
            // 각각의 목록에 대해 정보를 가져올 수 있습니다.
            Dictionary<string, object> list = announcement.GetAnnouncement(i);
            string buffer = "";

            foreach (var item in list)
                buffer += item.Key + ": " + item.Value + "\n";

            Debug.Log("announcement >> " + buffer);
        }
    }
}

There are no strict rules about what can be included in an announcement. The server administrator can define and use whatever items are needed. Note that if fields in use are changed, the plugin code may also need to be modified, so if you need to change existing fields, contact Funapi support.

If images are included in announcements, they are downloaded in order after all announcements are received, and files in image_url are saved in the FunapiUtils.GetLocalDataPath/ + "announce/" path. When all downloads are complete, the OnAnnouncementResult callback function is invoked. If there are a lot of images to download, it may take some time before the callback function is invoked.

37.10. Server maintenance messages

During server maintenance, messages sent by clients are ignored and the server sends a notification to the client informing it of server maintenance each time a message from it is received. If no messages are sent to the server after server connection, maintenance notifications sent by the server are not received.

Maintenance notifications are sent using FunapiSession’s MaintenanceCallback function rather than the usual message receipt callback. To handle server maintenance notifications, you can register the MaintenanceCallback function and conduct the required tasks in the callback function.

// 서버로부터 점검 안내 메시지를 받았을 때 이 콜백함수가 호출됩니다.
session.MaintenanceCallback += onMaintenanceMessage;

You can parse and use server maintenance messages as follows. Message data includes the maintenance start time, end time, and a text string message.

date_start string Server maintenance start time
date_end string Server maintenance end time
messages string Messages
if (encoding == FunEncoding.kJson)
{
  JsonAccessor json_helper = FunapiMessage.JsonHelper;
  FunDebug.Log("Maintenance message\nstart: {0}\nend: {1}\nmessage: {2}",
               json_helper.GetStringField(message, "date_start"),
               json_helper.GetStringField(message, "date_end"),
               json_helper.GetStringField(message, "messages"));
}
else if (encoding == FunEncoding.kProtobuf)
{
  FunMessage msg = message as FunMessage;
  object obj = FunapiMessage.GetMessage(msg, MessageType.pbuf_maintenance);
  if (obj == null)
    return;

  MaintenanceMessage maintenance = obj as MaintenanceMessage;
  FunDebug.Log("Maintenance message\nstart: {0}\nend: {1}\nmessage: {2}",
               maintenance.date_start, maintenance.date_end, maintenance.messages);
}

All code can be found in the Tester.Session.cs file.

37.11. Downloading resource files

If the server uses a client resource service, client resources can be updated with no other binary update on the client end.

You need to use the FunapiHttpDownloader class to use this function. The following sample code illustrates creation of FunapiHttpDownloader and registration of the callback function. All code can be found in the Tester.Download.cs file.

// FunapiHttpDownloader 객체를 생성합니다.
FunapiHttpDownloader downloader = new FunapiHttpDownloader();

// 필요한 콜백을 등록합니다.
// ReadyCallback과 FinishedCallback은 필수로 등록해야 합니다.
downloader.VerifyCallback += OnDownloadVerify;
downloader.ReadyCallback += OnDownloadReady;
downloader.UpdateCallback += OnDownloadUpdate;
downloader.FinishedCallback += OnDownloadFinished;

// 다운로드할 파일의 목록을 요청합니다.
// 파라미터로 서버 주소(포트를 포함한 URL)와 파일이 저장될 경로를 넘겨줍니다.
downloader.GetDownloadList("http://127.0.0.1:8020", "target/path", "file/path");

ReadyCallback and FinishedCallback are essential callback functions, while the others can be registered and used as needed.

Download begins when the file list is received. Invoke the GetDownloadList function to get the resource list and see local files. Set the server URL and folder path to save the file in the GetDownloadList function parameters. file/path is the file path including the list file name. Only the last part needs to be sent while leaving out the first parameter, the server URL address. These values don’t need to be set if requested from the game server. However, resource list files must be set if requested from a CDN.

VerifyCallback Invoked when verification of each file ends.
ReadyCallback Invoked when download preparations are complete after the resource list is received and files have been verified.
UpdateCallback Notifies of file download progress. Should be used for UI updates.
FinishedCallback Invoked when resources have been downloaded.

When the download is ready after the GetDownloadList function is invoked, ReadyCallback is invoked. After the ReadyCallback function is invoked, StartDownload is invoked and the download can begin. When the download finishes, the FinishedCallback function is invoked.

downloader.StartDownload();

If there are no new files to download, ReadyCallback is not invoked and only FinishedCallback is invoked.

There are a few things to check before starting the download in the FunapiHttpDownloader class.

TotalDownloadFileCount Total number of files to download
TotalDownloadFileSize Total download file size (bytes)
CurrentDownloadFileCount Number of files finished downloading
CurDownloadFileSize Size of downloaded files (bytes)

These properties must be used after ReadyCallback has been invoked.

37.12. Social network plugins

You can use Funapi Social plugins to easily login and post with accounts on Facebook, Twitter, etc. The installation package is in the additional-plugins/Packages folder. Scripts is the folder to extract files added during package import. If the package has been installed, there is no need to copy files from the Scripts folder. The sample source is installed in the Tester folder.

Tip

Social network plugins are currently only available in the Unity version.

37.12.1. Interface classes

Shared interface classes are used in the Funapi Social Plugin. A class called SocialNetwork has the following interfaces.

public abstract class SocialNetwork : MonoBehaviour
{
  // 친구 정보를 담고 있는 클래스입니다.
  public class UserInfo
  {
      public string id;         // 계정 아이디
      public string name;       // 계정 이름
      public string url;        // 프로필 사진 url
      public Texture2D picture; // 프로필 사진 이미지
  }

  // 초기화 함수입니다.
  // 페이스북의 경우 파라미터가 필요 없지만 트위터는 consumer key 와 consumer secret 값을 전달해야 합니다.
  public abstract void Init (params object[] param);

  // 메시지를 포스팅하는 함수입니다.
  public virtual void Post (string message)

  // 메시지와 이미지를 함께 포스팅하는 함수입니다.
  public virtual void PostWithImage (string message, byte[] image)

  // 메시지와 함수가 호출되는 시점의 화면을 함께 포스팅하는 함수입니다.
  public virtual void PostWithScreenshot (string message)

  // 친구 목록에서 해당하는 아이디의 친구 정보를 반환합니다.
  public UserInfo FindFriendInfo (string id)

  // 친구 목록에서 해당하는 인덱스의 친구 정보를 반환합니다. (인덱스는 0부터 시작)
  public UserInfo FindFriendInfo (int index)

  // 초대 목록에서 해당하는 아이디의 친구 정보를 반환합니다.
  public UserInfo FindInviteFriendInfo (string id)

  // 초대 목록에서 해당하는 인덱스의 친구 정보를 반환합니다. (인덱스는 0부터 시작)
  public UserInfo FindInviteFriendInfo (int index)

  // 내 계정의 아이디를 반환합니다.
  public string my_id;

  // 내 계정의 이름을 반환합니다.
  public string my_name;

  // 내 계정의 프로필 사진 Texture2D를 반환합니다.
  public Texture2D my_picture;

  // 친구 목록의 전체 개수를 반환합니다.
  public int friend_list_count;

  // 초대 목록의 전체 개수를 반환합니다.
  public int invite_list_count;

  // 이벤트 함수 원형
  public delegate void EventHandler (SNResultCode code);
  public delegate void PictureDownloaded (UserInfo user);

  // 초기화, 로그인, 친구 목록 가져오기, 포스팅 등의 응답에 대한 이벤트 함수입니다.
  // 콜백 호출시 파라미터로 enum SNResultCode 값이 전달됩니다.
  // 요청에 실패할 경우 SNResultCode.kError가 전달됩니다.
  public event EventHandler OnEventCallback;

  // 프로필 사진을 받으면 호출되는 이벤트 함수입니다.
  // 해당 프로필 사진의 UserInfo가 호출과 함께 전달됩니다.
  public event PictureDownloaded OnPictureDownloaded;
}

You can use the FacebookConnector class and TwitterConnector class made by inheriting this SocialNetwork class.

Note

These social apps must be made before plugin connection. Make the Facebook app for Facebook, and the Twitter app for Twitter. You need to install Facebook SDK for Unity as well before installing the facebook-plugin we have provided for Facebook.

37.12.2. Facebook

Plugin functions

The Facebook SDK feature is wrapped and only a few functions are provided. You can request functions you wish to use beyond those provided below from Funapi support.

  • Log in with a Facebook account
  • Request data and profile photo
  • Request friends list and profile photo
  • Request friends list to send game invitations
  • Write on wall

FacebookConnector is a MonoBehaviour object and thus must be preregistered on the scene. The following code imports registered FacebookConnector objects to reset them.

// FacebookConnector 컴포넌트를 가져옵니다.
FacebookConnector facebook = GameObject.Find("object name").GetComponent<FacebookConnector>();

// 초기화, 로그인, 포스팅 등의 요청에 대한 응답을 알려주는 이벤트 핸들러를 등록합니다.
facebook.OnEventCallback += OnEventHandler;

// 프로필 사진을 다운받으면 호출되는 이벤트 핸들러를 등록합니다.
facebook.OnPictureDownloaded += delegate(SocialNetwork.UserInfo user) {
    // do something with profile picture
};

// 초기화 함수를 호출합니다.
facebook.Init();

Facebook login

There are two types of login function. To login with only read permissions, use LogInWithRead. To login with write permissions as well, you need to login with LogInWithPublish.

A list of permissions needs to be sent as a login function parameter. These permissions can be viewed on the Facebook app’s Status & Review page. Several functions are provided by default, and most functions, including posting, can be requested in the app after authentication from Facebook. To test in Unity Editor, you can get an access token for testing from Graph API Explorer.

// 담벼락에 글을 쓰기 위해 publish 권한을 사용할 수 있는 LogInWithPublish 함수로 로그인합니다.
facebook.LogInWithPublish(new List<string>() {
    "public_profile", "email", "user_friends", "publish_actions"});

// 친구 목록을 요청합니다. 최대 100명의 친구 목록을 가져옵니다.
facebook.RequestFriendList(100);

// 게임에 초대 가능한 친구 목록을 요청합니다. 최대 100명의 초대 목록을 가져옵니다.
facebook.RequestInviteList(100);

// 내 담벼락에 현재 화면과 함께 메시지를 포스팅합니다.
facebook.PostWithScreenshot("plugin test post.");

Photos are automatically requested as well when requesting a friends list, but if not all friends’ photos are needed right away, you have the option of whether or not to download them.

// 이 옵션을 false로 주면 친구 목록을 요청할 때 프로필 사진을 같이 요청하지 않습니다.
// 이 값은 친구 목록을 요청하기 전에 설정해야 합니다. FacebookConnector 객체를 초기화 할 때 설정하는 것이 좋습니다.
facebook.auto_request_picture = false;

// 친구 목록에 있는 유저들의 프로필 사진을 요청합니다.
// 시작 인덱스 값과 최대 개수를 전달합니다.
facebook.RequestFriendPictures(0, 20);

// 초대 목록에 있는 유저들의 프로필 사진을 요청합니다.
// 시작 인덱스 값과 최대 개수를 전달합니다.
facebook.RequestInvitePictures(0, 20);

If you request photos, they are downloaded asynchronously and sequentially. When the profile photo download is finished, the PictureCallback event function is invoked. If you want to receive a notification when photo download is finished, you need to register a callback function in PictureCallback events when initializing FacebookConnector objects.

37.12.3. Twitter

Plugin functions

The Twitter plugin provides the following features. If there are other features you want to use, you can request them from Funapi support.

  • Log in with a Twitter account
  • Request data and profile photo
  • Request friends list and profile photo
  • Upload text to your Twitter feed

TwitterConnector is a MonoBehaviour object and thus must be preregistered on the scene. The following code imports registered TwitterConnector objects to reset them.

// TwitterConnector 컴포넌트를 가져옵니다.
TwitterConnector twitter = GameObject.Find("object name").GetComponent<TwitterConnector>();

// 초기화, 로그인, 포스팅 등의 요청에 대한 응답을 알려주는 이벤트 핸들러를 등록합니다.
twitter.OnEventCallback += OnEventHandler;

// 프로필 사진을 다운받으면 호출되는 이벤트 핸들러를 등록합니다.
twitter.OnPictureDownloaded += delegate(SocialNetwork.UserInfo user) {
    // do something with profile picture
};

// 초기화 함수를 호출합니다.
// 트위터 앱의 Consumer Key와 Consumer Secret 값을 전달합니다.
twitter.Init("4RnU4YDXmu8vmwKW5Lgpej3Xc",
             "voDDAoaTNXj8VjuWRDhfrnCpa9pnVgpRhBJuKwjJpkg62dtEhd");

When Twitter is initialized, Consumer Key and Consumer Secret must be transmission parameters in order to call the Init() function. You can view these values in the Twitter app [Keys and Access Tokens] tab.

Twitter login

Logging into Twitter is relatively complicated, but once you’ve logged in the Access Token received when first authenticating the app stays in use so that you only need to deal with this complicated process once.

// 로그인 함수를 호출해 앱을 승인하고 PIN Code를 얻습니다.
// 웹을 통해서 얻은 PIN Code를 입력받는 UI는 클라이언트에서 구현해야 합니다.
twitter.Login();

// Access Token을 요청합니다.
// 로그인 함수를 통해 얻은 PIN Code를 파라미터로 전달합니다.
twitter.RequestAccess("pin code");

The Access Token obtained by invoking the RequestAccess() function can be used for permanent authentication, so with this value alone, you can post to Twitter at any time. After logging in once, this value is encrypted and saved by the plugin , then confirmed the next time Init() is invoked. Login is notified through the callback function with no other authentication process.

Once logged in, you can post tweets. Sent messages are posted to your feed as follows:

twitter.Post("Funapi plugin test message~");

37.13. Unity plugins

Download the Unity client plugin from GitHub. If you need a version that includes encryption, you need to request it from Funapi support.

You can copy the Funapi and Plugins folders from among the files in the Assets folder in the source code received from GitHub and paste them in the Assets folder of the project where you will use the plugin. If using HTTPS, you should also copy the Editor and Resources folders. Sample plugin files are in the Tester folder.

37.13.1. Using Protobuf in Unity

Due to iOS/Android limitations, you cannot directly use C# code made with protobuf-net. If you use C# files made with protobuf.NET, error messages like the following will be left in some_protobuf_message.GetExtension(...) code and libmono‘s JIT compiler will crash.

F/mono  (1021): * Assertion at mini-arm.c:2595, condition `pdata.found == 1' not met
F/libc  (1021): Fatal signal 11 (SIGSEGV) at 0x0000600d (code=-6), thread 1033 (UnityMain)

Therefore, you need to change the message serialization/deserialization portion to .dll built by AOT, not C# code.

Setting up .dll builds for Protobuf

You can see the following setting called GENERATE_UNITY_PROTOBUF_DLL in CMakeLists.txt in the game server’s top-level directory. If this value is true, the necessary .dll file is created in the build directory.

# Source managed by git can append a build number from git commit id.
set(PACKAGE_WITH_BUILD_NUMBER_FROM_GIT false)

# Source managed by svn can append a build number from svn info.
set(PACKAGE_WITH_BUILD_NUMBER_FROM_SVN false)

# Generate .DLL for Unity Engine
set(GENERATE_UNITY_PROTOBUF_DLL true)

Copying created .dll files

For example, suppose that you built the project named hello with debug configuration. The following .dll files would be generated in the hello-build/debug/unity_dll directory.

  • protobuf-net.dll: protobuf-net file for Unity
  • messages.dll: Protobuf message definition created by the engine
  • FunMessageSerializer.dll: Protobuf reading/writing routine created by the engine

You can copy this file to the client’s Assets directory and see it in Unity Editor. In Windows, it is easiest to use the program provided by https://winscp.net/eng.

Tip

Individual messages and source code for messages’ enum is created as messages.cs and messages_enum.cs files in the unity_cs directory under the build directory.

37.13.2. C# Runtime Test Code

There is C# Runtime test code to test plugin bots inside the plugins folder. Since Unity limits the maximum number of sockets that can be open at once, we recommend writing a bot program to test. More details can be found in Approach 2: Leveraging the client plug-in.

37.13.3. Viewing Unity plugin logs

Plugin logs are only visible in Unity Editor or while C# Runtime is running. Since even leaving logs can affect a game’s performance, logs are not left other than when running in the editor. If you always want to view plugin logs for testing, you set ENABLE_LOG at the top of the Funapi/DebugUtils.cs file. If you want to see more detailed logs than the default, activate ENABLE_DEBUG.

//#define ENABLE_DEBUG    // 디버그 로그 표시

// 유니티 에디터 or C# Runtime일 경우 로그 표시
#if ENABLE_DEBUG || UNITY_EDITOR || NO_UNITY
#define ENABLE_LOG
#endif

With ENABLE_DEBUG defined, notes in the Funapi/DebugUtils.cs file can be read and the define symbol can be added in Unity’s build settings. In the Other Settings tab in build settings, add ENABLE_DEBUG to Scripting Define Symbols.

37.13.4. Saving logs

You can save plugin logs as text strings or files. To use this feature, define ENABLE_SAVE_LOG in the Funapi/DebugUtils.cs file.

The following functions and properties have to do with log storage in the FunDebug class. Sample code for this is in the Tester/Tester.cs file.

// 이 옵션을 활성화하면 로그가 버퍼에 저장됩니다.
#define ENABLE_SAVE_LOG

public class FunDebug
{
  ...

  // 버퍼에 저장된 내용을 파일로 저장합니다. 버퍼의 크기는 1MBytes 입니다.
  // 버퍼가 꽉 차면 자동으로 파일로 저장되고 버퍼가 초기화됩니다.
  // 파일은 로컬 저장 경로의 'Data/Logs/' 폴더에 저장됩니다.
  public static void SaveLogs();

  // 저장된 로그의 크기를 반환합니다.
  public static int GetLogLength();

  // 저장된 전체 로그를 반환합니다.
  public static string GetLogString();

  // 버퍼를 초기화합니다.
  public static void ClearLogBuffer();

  // 저장된 모든 로그 파일을 삭제합니다.
  public static void RemoveAllLogFiles();
}

37.13.5. Creating a debug log file

If there is deemed to be a problem in the Unity plugin during development, please save a log as follows to send us the file and let us know the steps to reproduce the problem.

Define the following in the Funapi/DebugUtils.cs file.

#define ENABLE_DEBUG
#define ENABLE_SAVE_LOG

Invoke the following function to save a log when the game closes or immediately after the reproducible event occurs.

// 로그를 파일로 저장
FunDebug.SaveLogs();

If running in the editor, these saved logs can be found in the Data/Logs folder in the same path as the project folder where Assets is located. If testing on a mobile device, access to the app’s save folder is limited on the device.

37.14. Unreal Engine 4 plugins

Download the Unreal Engine 4 plugin from GitHub. If you need a version that includes encryption, you need to request it from Funapi support.

37.14.1. Executing in the editor

Simple sample code to test FunapiSession is included in the plugin. Objects in this actor class must be displayed in the Unreal Editor for the tester sample map to play without errors. When the project is first run, funapi_tester objects may not be displayed in the editor. If this occurs, compile the project to make class objects. If they aren’t displayed in the editor even after compiling, close the editor, then reload the project. If funapi_tester objects appear in the editor’s content browser (C++ Class > funapi_plugin_ue4), load the tester map, then press the play button to execute the sample program.

37.14.2. Compiling external libraries

Use the following external libraries with the Unreal plugin.

libcurl Uses necessary functions for HTTP communication.
libcrypto Encryption library included in the OpenSSL library. Uses MD5.
libprotobuf The Google protocol buffer library.
libsodium ChaCha20 The Library for AES-128 encryption.

Script for compiling the libraries above is distributed along with compiled library files. Build scripts and library files inside the ThirdParty folder within the plugin folder are differentiated by platform.

ThirdParty
  ㄴ build     // 라이브러리 빌드 스크립트 파일들
  ㄴ include   // 헤더 파일들
  ㄴ lib       // 라이브러리 파일들
  ㄴ proto     // .proto 파일과 proto 파일용 컴파일 스크립트

You can build and use libraries as distributed scripts for compiling, but if there is no particular reason to rebuild a library, you can use distributed library files as-is.

37.14.3. Building Protobuf files

To build .proto files, execute ThirdParty/proto/make-win.bat or make-mac.sh files. To add .proto files to the build list, open the batch files and add them to the file list at the bottom. If you run batch files, the output file is added/modified in the funapi folder inside the Source folder.

37.15. Cocos2d-x plugins

Download the Cocos2d-x client plugin from GitHub. If you need a version that includes encryption, you need to request it from Funapi support.

37.16. Bug reports

Email Funapi support with bug reports or comments about client plugins.