# 通訊訊息

## 發送訊息

當宣告 RMI Foo(int a, float b) 時，如果在 <mark style="color:orange;">Client</mark> 或 <mark style="color:orange;">Server</mark> 上註冊了 RMI 代理，則可以呼叫代理程式的 RMI 來傳送訊息。 訊息傳遞協定可以使用 <mark style="color:orange;">RmiContext</mark> 的 <mark style="color:orange;">ReliableSend</mark> 或 <mark style="color:orange;">UnreliableSend</mark>。 客戶端-伺服器通訊和P2P通訊都可用。<br>

下面是發送訊息的範例。

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

```cpp
Proud::HostID oneHostID = ...; // 1 個發送目標
int manyHostIDListCount = ...;
 
// 向伺服器發送 RMI。
myProxy.Foo(Proud::HostID_Server, Proud::RmiContext::ReliableSend, 3, 4);
 
// RMI 被傳送到一個傳輸目的地。
myProxy.Foo(oneHostID, Proud::RmiContext::UeliableSend, 3, 4);
```

傳輸目的地可以是客戶端的Host ID、伺服器的Host ID（<mark style="color:orange;">Proud::HostID\_Server</mark>）或是你自己，如果輸入P2P群組ID，則會組播到相關的P2P群組。\
您也可以一次多播到多個目的地。 輸入指向 HostID 陣列的指標和 HostID 陣列的大小作為第一個參數，而不是 HostID。

```cpp
// 發送目標數組
Proud::HostID sendTo[2];
sendTo[0] = ...;
sendTo[1] = ...;
 
// 一次發送到多個目的地。
myProxy.Foo(sendTo, 2, Proud::RmiContext::ReliableSend, 3, 4);
```

此時，如果清單中的發送目的地重複，則不會重複發送，僅發送一次，清單中不僅可以指定一台主機，還可以指定一個P2P群組作為發送目的地。
{% endtab %}

{% tab title="C#" %}

<pre class="language-csharp"><code class="lang-csharp"><strong>Nettetion.Proud.HostID oneHostID = ...; // 1 個發送目標
</strong>int manyHostIDListCount = ...;

// 向伺服器發送 RMI。
myProxy.Foo(Nettention.Proud.HostID.HostID_Server, Nettention.Proud.RmiContext.ReliableSend, 3, 4);

// RMI 被傳送到一個傳輸目的地。
myProxy.Foo(oneHostID, Nettention.Proud.RmiContext.UnreliableSend, 3, 4);
</code></pre>

傳輸目的地可以是客戶端的Host ID、伺服器的Host ID（<mark style="color:orange;">Nettention.Proud.HostID.HostID\_Server</mark>），也可以是您自己，如果輸入P2P群組ID，則會組播到相關的P2P群組。\
您也可以一次多播到多個目的地。 只需輸入 HostID 數組而不是 HostID 作為第一個參數。

```csharp
// 發送目標數組
HostID[] sendTo = new HostID[2];
sendTo[0] = ...;
sendTo[1] = ...;

// 一次發送到多個目的地。
myProxy.Foo(sendTo, Nettention.Proud.RmiContext.ReliableSend, 3, 4);
```

{% endtab %}
{% endtabs %}

## 通訊訊息大小限制

ProudNet可以靈活設定通訊訊息的大小限制，這是為了防止被駭客攻擊的客戶端向伺服器發送錯誤訊號，表示它們無意中發送了大訊息。

因此，不建議在遊戲用戶端和伺服器之間通訊時發送或接收大於約 64KB 的大訊息。

可以發送和接收的訊息的初始最大大小約為 64 KB。 \
然而，伺服器間通訊不受這些駭客問題的影響，伺服器間通訊線路非常高速，有時需要發送和接收非常大的訊息。 因此，需要控制伺服器間通訊的最大訊息大小。

為此，可以透過<mark style="color:orange;">SetMessageMaxLength</mark>進行調整。 如果用戶端嘗試傳送大於指定大小的訊息，則用戶端的相關傳輸點將會發生Exception。

| C++                                  | C#                                             |
| ------------------------------------ | ---------------------------------------------- |
| Proud.CNetServer.SetMessageMaxLength | Nettention.Proud.NetServer.SetMessageMaxLength |

<br>

## 發送量自動調節功能 (Throttling)

ProudNet具有<mark style="color:orange;">發送量自動調節功能 (throttling)</mark>,在通信速度較慢的環境下也能減少通信障礙,在通信速度較快的環境下也能實現高品質的網絡。

### - 訊息傳輸優先功能

ProudNet提供訊息傳輸優先權功能。

如果傳輸緩衝區中有尚未透過網路線路傳輸的訊息等待，則優先順序 2 的訊息只有在優先順序 1 的訊息傳送完畢後才會被傳送。\
同樣，優先順序3的訊息只有在優先順序2的訊息發送完畢後才開始發送。

### - 利用訊息傳輸優先權功能

* **語音聊天**

語音聊天需要大量訊息傳遞。 由於語音聊天流量可能會破壞遊戲玩法，因此我們建議將遊戲相關訊息置於高優先級，而將語音聊天置於較低優先級，以改善這種情況。<br>

* **即時內容下載遊戲**

快速安裝後，您可以開發一個遊戲，在玩遊戲時下載必要的媒體資料以啟動遊戲。 您可以透過將與遊戲相關的訊息設定為高優先級並將與下載資料相關的訊息設定為低優先級來提高效能。<br>

* **同步大量角色位置**

與其他不直接互動的角色相比，與玩家角色直接互動的角色的位置同步非常重要。 因此，透過以高優先級發送與玩家角色戰鬥或靠近攝影機的角色的位置，並以低優先級發送其他訊息，可以提供更好的遊戲環境。

呼叫RMI時，輸入參數<mark style="color:orange;">RmiContext</mark>有一個成員變數<mark style="color:orange;">priority</mark>。 只需在此變數中輸入訊息傳輸優先權即可。 訊息發送優先權是<mark style="color:orange;">Proud.MessagePriority</mark>的值之一。

<mark style="color:orange;">Proud.RmiContext.ReliableSend</mark>, <mark style="color:orange;">Proud.RmiContext.UnreliableSend</mark> 是全域變數。 請不要直接修改它們的優先級，而是建立並使用單獨的 <mark style="color:orange;">RmiContext</mark> 物件。

### - 僅發送最終訊息

只發送最終訊息的功能是發送與傳輸佇列中累積但尚未發送的訊息相同的訊息的功能，而是僅發送要發送的最終訊息並取消剩餘累積的訊息。&#x20;

當您想要將角色的位置傳送給線上遊戲中的另一台主機時，此功能非常有用。

<figure><img src="/files/cx2UpZAUdBQVfnzkD1UC" alt=""><figcaption><p>只發送最後信件的功能</p></figcaption></figure>

當「Time」為1時，角色的位置（Pos）已經透過通訊線路傳輸出去。 \
而當時間為2或3時，字元位置尚未傳輸並載入到主機記憶體中。如果嘗試發送最新的時間4位置，則無需傳輸時間2或3的字元位置，仍在等待，因此已發送。最好在時間4 時發送位置，而不這樣做。

#### 如何使用

可以將呼叫RMI時參數<mark style="color:orange;">RmiContext</mark>輸入的成員變數<mark style="color:orange;">uniqueID</mark>指定為0以外的值。\
此時，在透過 RMI 呼叫將該訊息累積到傳輸佇列之前，如果存在具有相同 <mark style="color:orange;">uniqueID</mark> 的訊息，則將舊訊息刪除並用新訊息取代。

{% hint style="warning" %}
由於<mark style="color:orange;">UnreliableSend</mark>用在很多地方，所以最好建立一個單獨的<mark style="color:orange;">RmiContext</mark>物件並指定一個<mark style="color:orange;">uniqueID</mark>，而不是直接使用它。
{% endhint %}

#### 用法範例

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

```cpp
// Proud.RmiContext.m_uniqueID 用法範例
// 取得預設不可靠傳送的 RmiContext 物件的副本。
Proud::RmiContext rmi = Proud::RmiContext::UnreliableSend;
 
// 在m_uniqueID中輸入本機主機控制的角色的識別碼。
rmi.m_uniqueID = MyPlayerPetID;
 
// 透過 RMI 發送訊息。
C2CProxy.MyRmiFunction(PeerHostID, rmi, blahblah);
```

{% endtab %}

{% tab title="C#" %}

```csharp
// Proud.RmiContext.m_uniqueID 用法範例
// 取得預設不可靠傳送的 RmiContext 物件的副本。
Nettention.Proud.RmiContext rmi = Nettention.Proud.RmiContext.UnreliableSend;
 
// 在uniqueID中輸入本機主機控制的角色的識別碼。
rmi.uniqueID = MyPlayerPetID;
 
// 透過 RMI 發送訊息。
C2CProxy.MyRmiFunction(PeerHostID, rmi, blahblah);
```

{% endtab %}
{% endtabs %}

## 檢測傳輸過載

### - 偵測每台主機上的過度傳輸

ProudNet 有一個內部<mark style="color:orange;">發送隊列(send queue)</mark>。\
發送隊列是指網絡線路想要發送的數據量超過可承受的發送速度時,在存儲器中等待發送完成爲止的"發送數據"

隨著傳輸量的增加，傳輸佇列不斷增加，因此透過測量傳輸量來預防是一個很好的方法。<br>

#### 發送隊列測量方法

<table data-full-width="true"><thead><tr><th>C++</th><th>C#</th><th>註釋</th></tr></thead><tbody><tr><td>Proud.CNetServer.GetClientInfo</td><td>Nettention.Proud.NetServer.GetClientInfo</td><td>獲取與該客體連接的1個peer的信息。</td></tr><tr><td>Proud.CNetClient.GetPeerInfo</td><td>Nettention.Proud.NetClient.GetPeerInfo</td><td>獲取與該客體連接的1個peer的信息。</td></tr><tr><td>Proud.CNetPeerInfo.m_sendQueuedAmountInBytes</td><td>Nettention.Proud.NetPeerInfo.sendQueuedAmountInBytes</td><td><p>要發送到 Peer 的信件總量( Byte)</p><ul><li>從 Peer 導入到服務器時: 客戶端傳輸的總量</li><li>從 Peer 導入到客戶端時: 客戶端傳送的總量<br>（但已relay消息除外）</li></ul></td></tr></tbody></table>

{% hint style="info" %}
當傳輸量較大時，可以透過[**發送量自動調節功能 (Throttling)**](#throttling)來緩解。
{% endhint %}

### - 偵測每種 RMI 類型的過度傳輸

每個 RMI 呼叫發生的事件回呼 <mark style="color:orange;">Proud.IRmiProxy.NotifySendByProxy</mark> 都有一個參數 <mark style="color:orange;">Proud.MessageSummary</mark>，它允許您測量每個 RMI 呼叫的流量。


---

# Agent Instructions: 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:

```
GET https://docs.proudnet.com/proudnet.cn/proudnet/using_pn/tong-xun-xun-xi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
