# PIDL

## 創建和設置

創建一個文件很簡單。\
只需在 Visual Studio 中建立一個 txt 文件，將擴展名更改為 PIDL，然後將其設定為編譯即可。 擴展名為 PIDL 的檔案必須設定為「自訂建置」。

> 1. Visual Studio 解決方案 Viewer
> 2. 右鍵單擊已建立的PIDL文件
> 3. <mark style="color:orange;">屬性</mark> -> <mark style="color:orange;">General</mark> -> <mark style="color:orange;">Item Type: Custom Build Tool</mark>

## PIDL語法

PIDL 具有以下結構。

```
global (namespace) 
         (訊息起始值ID) 
{ 
     函數聲明([in] 函數 Parameter, …) 
}
```

編譯時，會建立一個命名空間，並將存根和代理類別放置在該命名空間內。 \
所有 RMI 函數都有一個唯一的 ID，該值是透過在「訊息 ID 起始值」上加 +1 給出的。\
但是，0 到 1,300 之間的 ID 以及 63,000 之後的 ID 用作 ProudNet 內部訊息，因此必須使用除這些之外的數字。

{% tabs %}
{% tab title="PIDL" %}

```cpp
// 定義需要更改格式才能在 C# 中使用的變數。
rename cs(Proud::String, System.String);

global S2C 1000 
{
     // 定義Protocol。
    Chat([in] Proud::String txt);
}
```

{% endtab %}
{% endtabs %}

## 如何使用生成的Proxy & Stub文件

當您執行 PIDL 時，將會建立以下六個檔案。

* PIDL檔案名稱\_common.Cpp
* PIDL檔案名稱\_common.h
* PIDL檔案名稱\_proxy.Cpp
* PIDL檔案名稱\_proxy.h
* PIDL檔案名稱\_stub.Cpp
* PIDL檔案名稱\_stub.h

對於 C#，將建立以下三個檔案。

* PIDL檔案名稱\_common.cs
* PIDL檔案名稱\_proxy.cs
* PIDL檔案名稱\_stub.cs

頭文件中#include h文件，cpp文件中#include cpp文件，排除普通文件，比較方便。\
您可以在專案中包含 .h 文件和 .cpp 文件，但要小心，因為使用 Custom Build 會導致文件頻繁更改。\
除了您定義的變數之外，還會自動將兩個參數新增至產生的 RMI 函數。

### - 將Proxy連接到客戶端和伺服器

您必須先建立 PIDL 編譯輸出的 Proxy 實例，然後向 <mark style="color:orange;">Proud.CNetClient</mark> 或 <mark style="color:orange;">Proud.CNetServer</mark> 註冊該實例。 客戶端和伺服器都繼承了<mark style="color:orange;">Proud.IRmiHost</mark>，可以透過這裡的<mark style="color:orange;">AttachProxy</mark>方法註冊一個代理程式。\
每個客戶端或伺服器可以有兩個或多個代理程式。 但是，訊息 ID 範圍不得重疊。

{% hint style="info" %}
同樣在 C# 中，您可以在 NetServer 和 NetClient 中使用 <mark style="color:orange;">AttachProxy</mark> 函數。
{% endhint %}

例如，假設有一個 TestA.Proxy 和一個 TestB.Proxy 連接到一個 <mark style="color:orange;">CNetClient</mark>、 \
如果您聲明 TestA 的第一個消息 ID 爲 2,100，TestB 的第一個消息 ID 爲 2,200，那麼如果 TestA 中聲明的 RMI 函數數爲 200，分配給 TestA 的消息 ID 將介於 2,100 和 2,300 之間，從而導致 TestB 的消息 ID 重疊。如果所附代理的消息 ID 重疊，就會產生異常。

### -  將Stub附加到客戶端和伺服器

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

```cpp
// 使用 C++11 時
<exam.pidl>
Func1([in] int a, [in] string b);
 
<exam.cpp>
class Exam
{
    int x = 1;
 
    Exam::StubFunction examStub;
 
    void Main()
    {
        int y = 3;
        // PARAM_Exam_Func1 在 Exam_stub.h 中定義。
        examStub.Func1_Function = [this, y]PARAM_Exam_Func1 {
            x += a;
            y += a;
            return true;
        };
    }
};
```

{% endtab %}

{% tab title="C#" %}

```csharp
<exam.pidl>
Func1([in] int a, [in] string b);
 
<exam.cs>
class Exam
{
    int x = 1;
 
    Exam.Stub examStub = new Exam.Stub();
 
    void Main()
    {
        int y = 3;
        
        examStub.Func1 = (a, b) => {
            x += a;
            y += a;
            
            return true;
        };
    }
}
```

{% endtab %}
{% endtabs %}

PIDL 編譯結果的存根實例具有要透過網路接收的訊息作為虛擬函數執行的 RMI 函數。\
在開發過程中，必須繼承該Stub類別並重寫RMI函數。

{% hint style="info" %}
在 C# 中，您可以直接建立和使用 Stub 對象，而無需覆寫它。
{% endhint %}

對於 C++，PIDL 編譯器產生以下形式的宏觀，其中包裝了 RMI 函數名稱和參數，以方便開發人員。

```
#define DECRMI_S2C_ShowChat bool ShowChat(Proud::HostID remote,Proud::RmiContext &rmiContext,const CString &a,const int &b,const float &c)
#define DEFRMI_S2C_ShowChat(DerivedClass) bool DerivedClass::ShowChat(Proud::HostID remote,Proud::RmiContext &rmiContext,const CString &a,const int &b,const float &c)
```

### - 宏觀使用順序

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

```cpp
// 宏觀使用順序
// 1. 在繼承的Stub類別的類別聲明中加入DECRMI_namespace_method。
// 2. 在繼承的 Stub 類別的方法定義中使用 DEFRMI_namespace_method。

// 用法範例

// PIDL 檔案內容
global LobbyC2S 5000
{
    Foo([in] int a,[in] float b);
}
 
// 繼承LobbyC2S的stub並實作RMI的類
class LobbyC2SStub:public LobbyC2S::Stub
{
    DECRMI_LobbyC2S_Foo;
};
 
// RMI Foo在LobbyC2S中的例程實現
DEFRMI_LobbyC2S_Foo(LobbyC2SStub)
{
    // 這裡給的參數與PIDL的LobbyC2S.Foo的參數相同。
    a++;
    b++;
 
    return true;
}

```

{% endtab %}
{% endtabs %}

用戶端和伺服器繼承 <mark style="color:orange;">Proud.IRmiHost</mark> 並可透過其方法 <mark style="color:orange;">Proud.IRmiHost.AttachStub</mark> 註冊存根。 與Proxy一樣，在訊息ID不重疊的情況下可以附加兩個或多個存根。

### **-** 通訊**Option Class**

使用聲明為內部Static的變數很方便。

<table><thead><tr><th width="202">C++</th><th width="256">C#</th><th>註釋</th></tr></thead><tbody><tr><td>Proud::HostID</td><td>Nettention.Proud.HostID</td><td>要通信的對方 Host 的 ID 值</td></tr><tr><td>Proud::RmiContext</td><td>Nettention.Proud.RmiContext</td><td>發送或接收選項</td></tr></tbody></table>

<table><thead><tr><th width="274">Reliable / Unreliable 通訊名稱</th><th>註釋</th></tr></thead><tbody><tr><td>ReliableSend</td><td>Reliable 通訊</td></tr><tr><td>FastEncryptedReliableSend</td><td>使用快速加密進行可靠通訊（安全性低）</td></tr><tr><td>SecureReliableSend</td><td>加密可靠通信</td></tr><tr><td>UnreliableSend</td><td>Unreliable 通訊</td></tr><tr><td>FastEncryptedUnreliableSend</td><td>使用快速（不太安全）加密方法的不可靠通信</td></tr><tr><td>SecureUnreliableSend</td><td>加密且不可靠的通信</td></tr></tbody></table>

許多常用選項都是預先設定為靜態的。 您可以根據自己的方便自行建立並選擇所需的選項。

> <mark style="color:orange;">m\_reliability</mark>: 選擇可靠且不可靠的通訊方式\ <mark style="color:orange;">m\_encryptMode</mark>: 加密（根據速度和安全性，可以使用三個選項）\ <mark style="color:orange;">m\_compressMode</mark>: 是否壓縮

{% hint style="info" %}
在 C# 中，使用以相同名稱定義的變數。
{% endhint %}

***

## 活用

{% content-ref url="/pages/dIZcRXCNxvzDk80MDuW7" %}
[如何使用PIDL](/proudnet.cn/proudnet/using_pn/pidl/using_pidl.md)
{% endcontent-ref %}


---

# 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/pidl.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.
