> For the complete documentation index, see [llms.txt](https://docs.proudnet.com/proudnet.cn/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.proudnet.com/proudnet.cn/proudnet/using_pn/rmi/using_rmi.md).

# 如何使用RMI

## 為每個 RMI 函數指定單獨的 ID

<mark style="color:orange;">global {}</mark> 中的 RMI 函數被指派了序列值 RMI ID。

若要取得所需的 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方法。 它可以防止程式設計師錯誤地建立發送/接收例程而犯錯，使以後的開發更加方便。

但是，如果您確實需要 RMI 以外的舊傳輸/接收處理方法，我們提供以下替代方法。

### - 無需 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);

// 接收訊息時，Proud.ByteArray 物件作為開發人員實作的 RMI 函數內的 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);

// 接收訊息時，Proud.ByteArray 物件作為開發人員實作的 RMI 函數內的 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" %} <mark style="color:orange;">BeforeRmiInvocation</mark> 在 RMI 執行即將到來之前調用，<mark style="color:orange;">AfterRmiInvocation</mark> 在執行結束時調用。\
這可以幫助您找到導致處理時間較長的伺服器效能問題的 RMI。
{% endhint %}

{% hint style="success" %}
**如何列印從存根接收的RMI函數的所有參數**\
將Stub實例的成員變數<mark style="color:orange;">m\_enableNotifyCallFromStub</mark>設為**true**並覆寫<mark style="color:orange;">NotifyCallFromStub</mark>。\
此方法接收轉換為字串的參數，因此您可以在此處留下日誌。\
然而，由於RMI處理性能較低，建議僅在絕對必要時使用它。
{% endhint %}

{% hint style="info" %}
在 C# 中，您可以使用定義的委託函數，而無需單獨重寫它。

同樣可以在定義的 Stub 物件中找到變數。
{% endhint %}

## 隱藏 RMI 名稱

為了在<mark style="color:orange;">BeforeRmiInvocation</mark>等顯示主機傳送和接收的RMI的名稱，可以將所有RMI的名稱儲存在一個執行檔中。

但是，如果您出於安全原因想要隱藏它，請按照以下步驟操作。

在將 <mark style="color:orange;">...\_proxy.cpp</mark> include在 PIDL 編譯結果中之前，將其定義如下。

```
#define HIDE_RMI_NAME_STRING
```

{% hint style="danger" %}
從版本 <mark style="color:orange;">1.7.36365</mark> 開始，出於安全原因，RMI 函數名稱預設不會出現在 <mark style="color:orange;">IRmiStub.BeforeRmiInvocation</mark> 函數中。

為了顯示PIDL編譯結果的代理，在編譯cpp原始檔之前，\
它的定義如下。

```
#define USE_RMI_NAME_STRING
```

{% endhint %}

***

## :arrow\_left: [**返回**](/proudnet.cn/proudnet/using_pn/rmi.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.proudnet.com/proudnet.cn/proudnet/using_pn/rmi/using_rmi.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
