# RMI 활용법

## RMI 함수별로 ID 개별 지정하기

<mark style="color:orange;">global {}</mark> 안에 있는 RMI 함수들은 일련 값의 RMI ID 가 배정됩니다.&#x20;

원하는 RMI ID 값을 가지게 하기 위해서는 아래의\[id=xxx] 구문을 참고합니다.

```cpp
global MedivalWorld 10000
{
    Foo([in] int x);                   // id=10001 자동 지정
    [id=13000] id=Foo2([in] int y);    // id=13000으로 강제 지정
}
```

## 과거 송수신 처리 방식 유지하기

과거의 송수신 처리 방식으로 만들어진 프로그램에 ProudNet을 도입할 때, RMI 방식으로 바꾸는 것을 권장합니다. 프로그래머가 송수신 루틴을 잘못 만드는 실수를 방지하고 추후 개발이 더욱 편리합니다.&#x20;

하지만 RMI가 아닌 과거의 송수신 처리 방식이 꼭 필요하다면 다음과 같은 대안을 제시해 드립니다.&#x20;

### - RMI 없이 사용자 정의 메시지를 주고 받기

<mark style="color:orange;">Remote Method Invocation(원격 메서드 호출)</mark> 를 쓰지않고 사용자가 정의한 메시지를 송신하기 위해 다음과 같은 함수를 이용합니다.

{% tabs %}
{% tab title="C++" %}

<table><thead><tr><th width="346">송신</th><th>수신 콜백</th></tr></thead><tbody><tr><td>Proud.CNetClient.SendUserMessage</td><td>Proud.INetClientEvent.OnReceiveUserMessage</td></tr><tr><td>Proud.CNetClient.SendUserMessage</td><td>Proud.INetServerEvent.OnReceiveUserMessage</td></tr><tr><td>Proud.CLanClient.SendUserMessage</td><td>Proud.ILanClientEvent.OnReceiveUserMessage</td></tr><tr><td>Proud.CLanServer.SendUserMessage</td><td>Proud.ILanServerEvent.OnReceiveUserMessage</td></tr></tbody></table>
{% endtab %}

{% tab title="C#" %}

| 송신                                         | 수신 콜백                                                 |
| ------------------------------------------ | ----------------------------------------------------- |
| Nettention.Proud.NetClient.SendUserMessage | Nettention.Proud.NetClient.ReceivedUserMessageHandler |
| Nettention.Proud.NetServer.SendUserMessage | Nettention.Proud.NetServer.ReceiveUserMessageHandler  |
| {% endtab %}                               |                                                       |
| {% endtabs %}                              |                                                       |

### - RMI의 파라미터로 사용자 정의 메시지를 주고 받기

{% tabs %}
{% tab title="C++" %}

```cpp
// ProudNet에서는 Proud.ByteArray 타입을 
// RMI의 파라미터로 다음과 같이 사용할 수 있습니다.
Foo([in] Proud::ByteArray something);

// --- 여기까지 PIDL

// 이를 활용해서 과거의 송수신 처리 루틴을 사용할 수 있습니다. 
// 메시지 송신 시 과거의 메시지 송신 루틴에서 만들어진 버퍼 객체를 Proud.ByteArray 객체에 넣습니다. 
// 이후 Proud.ByteArray 객체를 RMI의 파라미터로 전송합니다.

// 메시지 객체 생성
Proud::CMessage msg;
 
/* 사용자가 직접 만든 msg 객체는 아직 어떤 버퍼를 써야 할지 지정되지 않았습니다.
이러한 경우 UseInternalBuffer를 호출해야만 << 연산자가 작동합니다.
UseInternalBuffer는 msg 객체에 아무것도 버퍼 사용이 지정되지 않음을 전제합니다.
따라서 이미 버퍼 사용이 지정된 경우 이 메서드를 호출해서는 안됩니다.
자세한 것은 Proud.CMessage.UseInternalBuffer 도움말을 참고하십시오. */
msg.UseInternalBuffer();
 
msg << a << b;
 
Proud::ByteArray block;
block.SetCount(msg.GetLength());
memcpy(block.GetData(), msg.GetData(), block.Count);
 
Foo(Proud::HostID_Server, Proud::RmiContext::ReliableSend, block);

// 메시지 수신 시 개발자가 구현하는 RMI 함수 내부에서 Proud.ByteArray객체를 RMI 파라미터로 받고, 
// 필요한 데이터를 추출합니다.

DEFRMI_MyPIDL_Foo(MyClass)
{
    // Parameter 'block' and the others are is given
    Proud::CMessage msg;
    msg.UseExternalBuffer(block.GetData(), block.Count);
    msg.SetLength(block.Count);
    msg >> a >> b;
    ...
}
```

{% endtab %}

{% tab title="C#" %}

<pre class="language-csharp"><code class="lang-csharp">// C#에서 ByteArray를 사용하려면 PIDL에서 아래와 같이 설정해주어야 합니다.
rename cs(Proud::ByteArray, Nettention.Proud.ByteArray);

// ProudNet에서는 Proud.ByteArray 타입을 
// RMI의 파라미터로 다음과 같이 사용할 수 있습니다.
Foo([in] Proud::ByteArray something);

<strong>// --- 여기까지 PIDL
</strong>
// 메시지 송신 예시
string msg = "Welcome";
byte[] msgBytes = Encoding.UTF8.GetBytes(msg);
Nettention.Proud.ByteArray ar = new Nettention.Proud.ByteArray(msgBytes);

simpleProxy.Foo(Nettention.Proud.HostID.HostID_Server, Nettention.Proud.RmiContext.ReliableSend, ar);

// 메시지 수신 시 개발자가 구현하는 RMI 함수 내부에서 Proud.ByteArray객체를 RMI 파라미터로 받고, 
// 필요한 데이터를 추출합니다.

simpleStub.Foo = (remote, rmi, somthing) => {
    string msg = Convert.ToBase64String(somthing.data);
};
</code></pre>

{% endtab %}
{% endtabs %}

## 모든 RMI 호출 시점을 접근하기

ProudNet에서는 RMI의 호출 시점을 접근할 수 있는 장치가 있습니다.

> * 호출되는 모든 RMI의 로그를 남기기
> * 각 RMI별로 실행되는 시간을 측정하여 게임 서버 성능 최적화

### - 송신측(Proxy) 호출 시점 접근

> 1\. 먼저, PIDL 컴파일러 결과물의 Proxy 파생 클래스를 만듭니다.\
> 2\. <mark style="color:orange;">NotifySendByProxy</mark> 를 오버라이드 합니다

이렇게 하면 매 송신마다 오버라이드한 메서드가 호출됩니다.

기본적으로 <mark style="color:orange;">NotifySendByProxy()</mark> 는 호출되도록 설정되어 있습니다만, \
성능을 더 높이기 위해 <mark style="color:orange;">NotifySendByProxy()</mark> 호출 자체를 막고 싶을 땐 <mark style="color:orange;">m\_enableNotifySendByProxy</mark> 를 **false**로 설정해주면 <mark style="color:orange;">NotifySendByProxy()</mark>의 호출이 더 이상 되지 않습니다.

### - 수신측(Stub) 호출 시점 접근

> 1\. Stub 인스턴스의 멤버 변수 <mark style="color:orange;">m\_enableStubProfiling(enableStubProfiling)</mark> 를 **true**로 세팅합니다.\
> 2\. PIDL 컴파일러 결과물의 Stub 파생 클래스의 <mark style="color:orange;">BeforeRmiInvocation</mark> 과 <mark style="color:orange;">AfterRmiInvocation</mark>를 오버라이드 합니다.

이렇게 하면 매 수신마다 오버라이드한 메서드가 호출됩니다.

{% hint style="info" %}
도착한 RMI 실행 직전에 <mark style="color:orange;">BeforeRmiInvocation</mark> 가 호출되고 실행이 끝나면 <mark style="color:orange;">AfterRmiInvocation</mark> 가 호출됩니다.\
이를 활용하면 긴 처리 시간으로 서버 성능에 문제를 일으키는 RMI를 찾는데 도움이 됩니다.
{% endhint %}

{% hint style="success" %}
**Stub에서 수신되는 RMI 함수의 파라미터를 모두 출력하는 방법**\
Stub 인스턴스의 멤버 변수 <mark style="color:orange;">m\_enableNotifyCallFromStub</mark> 를 **true**로 세팅하고 <mark style="color:orange;">NotifyCallFromStub</mark> 를 오버라이드합니다. \
이 메서드는 파라미터를 문자열로 변환한 형태로 받으므로 여기에 로그를 남기면 됩니다.\
다만, RMI 처리 성능이 떨어지니 반드시 필요할 때만 사용할 것을 권장합니다.
{% endhint %}

{% hint style="info" %}
C#의 경우 따로 오버라이드 할 필요 없이 정의된 delegate 함수를 사용하면 됩니다.

변수도 마찬가지로 정의한 Stub 객체에서 찾을 수 있습니다.
{% endhint %}

## RMI 이름 감추기

호스트가 주고받는 RMI의 이름을 <mark style="color:orange;">BeforeRmiInvocation</mark> 등에서 나타나게 하기 위해 모든 RMI의 이름을 실행 파일에 보관할 수 있습니다.&#x20;

하지만 보안 상 감추고 싶다면 아래와 같이 진행합니다.

PIDL의 컴파일 결과물 중 <mark style="color:orange;">...\_proxy.cpp</mark>를 include 하기 전에 다음과 같이 정의합니다.

```
#define HIDE_RMI_NAME_STRING
```

{% hint style="danger" %} <mark style="color:orange;">1.7.36365</mark> 이후 버전부터 보안 상의 이유로 <mark style="color:orange;">IRmiStub.BeforeRmiInvocation</mark> 함수에서는`RMI` 함수 이름이 기본적으로 나타나지 않습니다.&#x20;

나타나게 하기 위해서는 PIDL 컴파일 결과물의 proxy, cpp 소스 파일이 컴파일 되기 전,\
다음과 같이 정의합니다.

```
#define USE_RMI_NAME_STRING
```

{% endhint %}

***

## :arrow\_left: [**뒤로**](https://docs.proudnet.com/proudnet/proudnet/using_pn/rmi)
