# 詞彙表

## :books: Burst Time

Burst time主要分爲<mark style="color:orange;">CPU burst time</mark>和<mark style="color:orange;">Device burst time</mark>。

### **- CPU burst time**

運行特定例程只運算 CPU 的時間 。 <mark style="color:orange;">CPU burst time</mark> 線程的 CPU 核心佔用率爲 100 。

### **- Device burst time**

這是CPU在執行特定例程時等待其他處理完成的時間。<mark style="color:orange;">Device burst time</mark>中執行緒的CPU核心使用率為0。\
CPU 等待其他處理完成的典型情況包括讀取/寫入檔案、執行資料庫查詢或等待其他主機的服務回應。

## :books: C++ Singleton

Singleton和全局變量在C++語言中存在差異。 全局變量在運行 <mark style="color:orange;">WinMain()</mark> 或 <mark style="color:orange;">main()</mark> 時不會在即將退出時被破壞， 而會在子調用函數中被破壞 。 全局變量的破壞順序只能在一個C++文件的編譯結果中保證,不同C++文件的編譯結果之間的破壞順序不能保證。<br>

但是 C++ singleton 在 <mark style="color:orange;">WinMain()</mark> 或 <mark style="color:orange;">main()</mark> 返回之前被調用 。 此外，實例的創建者會在第一次訪問 singleton 的瞬間被調用，破壞的順序也會被調用者反向調用。 因此，它確保了比全局變量更安全的生成/破壞規則。

<details>

<summary><span data-gb-custom-inline data-tag="emoji" data-code="27a1">➡️</span> C++ singleton 實現示例</summary>

```cpp
class A
{
    A(){}
public:
    static A& Instance()
    {
        static A inst;
        return inst;
    }
    void Goo() {}
};
 
 
 
void Foo()
{
    A::Instance().Goo();
}
```

</details>

{% hint style="warning" %}
上述實現存在如下風險:如果在短時間內同時從多個線程訪問singleton，則生成器可能會有2次以上的呼叫。

在解決這些問題的同時，我們建議使用無critical section負載的類<mark style="color:orange;">Proud.CSingleton</mark>訪問。
{% endhint %}

## :books: DB Constraints

要進入字段的值如果不滿足特定條件，就指不讓進入。

PrimaryKey Unique Index 其他等等(>,<,=,!=)，Constraints由Unique Index, Trigger 等實現。

## :books: Fast Heap

ProudNet的Fastheap雖然比[**Lookaside allocator**](https://docs.proudnet.com/proudnet.cn/proudnet/pn_utility#lookaside-allocator)稍微慢一些，但比OS環境下的存儲器分配/解除速度更快，可以分配/解除各種大小的存儲器塊。

ProudNet 的 <mark style="color:orange;">Fast heap</mark> 的實現類是 <mark style="color:orange;">Proud.CFastHeap</mark>。 <mark style="color:orange;">Proud.CFastHeap</mark> 和 <mark style="color:orange;">Lookaside allocator</mark> 一樣， 只有在所有存儲塊被破壞之後才能移除 <mark style="color:orange;">Proud.CFastHeap</mark> 對象。

<mark style="color:orange;">Fast heap</mark>的使用方法如下。

> * 首先以<mark style="color:orange;">Proud.CFastHeap.New</mark>方法生成fastheap對象。 創建全局對象也可以。
> * 用 <mark style="color:orange;">Proud.CFastHeap.Alloc</mark> 方法分配內存塊 。
> * 解除爲<mark style="color:orange;">Proud.CFastHeap.Free</mark>。 可以用<mark style="color:orange;">CFastHeap.Realloc</mark>重新分配內存塊。
> * 解除所有存儲塊後，破壞 <mark style="color:orange;">Proud.CFastHeap</mark> 對象。

## :books: P2P 組

如果兩個客戶端A和B要進行P2P通信，則A和B必須屬於至少一個P2P組。 多人可以在一個聊天視窗中互相聊天，也可以透過建立多個聊天視窗來進行多人聊天。 但是，您無法在您不在的聊天視窗中聊天。

ProudNet在網絡聊天工具中，每個聊天窗口對應P2P組。

但是，創建聊天窗口或進入其他聊天窗口的權限只有服務器纔有。 P2P 組標識符也是 <mark style="color:orange;">Proud.HostID</mark> 類型。

## :books: PIDL

PIDL是爲RMI自制的編譯器。

在特定文件中定義協議後設置，會自動創建對象文件。\
此時生成的客體與Server和Client一起使用，因此生成Common(公用)Project進行管理非常方便。

## :books: Reliable 訊息

Reliable消息（或reliable send）是指發送方發送的消息內容和順序始終由接收方統一接收。 例如,發送信息A、B、C、D、E後,接收方也會按照A、B、C、D、E的順序無損失地接收數據。

雖然具有可信度(reliablility)的優點,但偶爾接收的時間可能比[**Unreliable 信息**](#unreliable)慢。

## :books: RMI

<mark style="color:orange;">RMI(Remote Method Invocation, 遠程方法呼叫)</mark>是指呼叫其他網絡或其他程序中的函數,通過機器代替人進行定義收發例程和信息(Message)結構的編碼工作,起到將讓開發者感到吃力的編程工作(信息結構定義、發送函數、接收函數製作)簡化爲函數呼叫形式的作用。

發送消息時，調用 RMI 函數的一方稱爲 Proxy，調用方稱爲 Stub。

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/4Wl3RASyIbhyzIjZyu44/rmi_2.png" alt=""><figcaption><p>RMI概念</p></figcaption></figure>

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/p9wwSoY2wgSVeQlDAjYs/rmi_3.png" alt=""><figcaption><p>RMI概念2</p></figcaption></figure>

### - 不使用 RMI 時

```cpp
// Message header ID definitions
#define Message_Knight_Move_ID 12
#define Message_Knight_Attack_ID 13
 
// Message format definitions
struct Message
{
    int m_msgID;
};
struct Message_Knight_Move:public Message
{
    int m_id;
    float m_x,m_y,m_z;
};
struct Message_Knight_Attack:public Message
{
    int m_id;
    int m_target;
    int m_damage;
};
 
// A function which send a formatted message
void Knight_Move(int id,float x,float y,float z)
{
    Message_Knight_Move msg;
    msg.m_msgID=Message_Knight_Move_ID;
 
    msg.m_id=id;
    msg.m_x=x;
    msg.m_y=y;
    msg.m_z=z;
 
    Send(msg);
}
 
// A function which send a formatted message
void Knight_Attack(int id,int target,int damage)
{
    Message_Knight_Attack msg;
    msg.m_msgID=Message_Knight_Attack_ID;
 
    msg.m_id=id;
    msg.m_target=target;
    msg.m_damage=damage;
 
    Send(msg);
}
 
// Identified a received message 
// and call an appropriate function for message handling
void DoReceivedMessage(Message* msg)
{
    switch(msg->m_msgID)
    {
    case Message_Knight_Move_ID:
    {
        Message_Knight_Move* msg2=
            (Message_Knight_Move*)msg;
 
        Do_Knight_Move(
            msg2->m_id,
            msg2->m_x,
            msg2->m_y,
            msg2->m_z);
    }
    break;
    // ... cases for other message types
    case Message_Knight_Attack_ID:
    {
        Message_Knight_Attack* msg2=
            (Message_Knight_Attack*)msg;
 
        Do_Knight_Attack(
            msg2->m_id,
            msg2->m_target,
            msg2->m_damage);
    }
    break;
    // ... cases for other message types
    }
}c
```

但是使用RMI可以整理成以下短代碼。

```cpp
Knight_Move([in] int id,[in] float x,[in] float y,[in] float z);
Knight_Attack([in] int id,[in] int target,[in] int damage);
```

以上格式爲<mark style="color:orange;">IDL(Interface Description Language)</mark>格式，編譯後生成C++或C#源。 創建的源文件由消息結構聲明、發送函數、接收處理函數等組成，即使開發者不直接創建網絡處理程序，PIDL編譯器也會自動生成。

在創建的文件中，將函數調用轉換爲消息發送到網絡的模塊稱爲proxy，分析通過網絡接收到的消息並調用用戶函數的模塊稱爲stub。

當從主機A調用RMI函數X時，實際上調用X的proxy，proxy將其切換爲網絡消息並將其發送到主機B。 然後主機B接收消息併發送到stub，stub分析後調用用戶創建的函數X。

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/jNba3Vex8UNE8irYwdrM/rmi.png" alt=""><figcaption><p>RMI 處理順序</p></figcaption></figure>

因爲看起來具有與主機A呼叫主機B上的函數相似的形狀,所以具有<mark style="color:orange;">Remote Method Invocation(遠程方法呼叫)</mark>的意義。

## :books: Stored Procedure

<mark style="color:orange;">Stored Procedure</mark>是由DBMS本身可放入的SQL語句製作的程序函數。\
訪問數據庫時，查詢語句字符串由應用程序直接創建並扔出，但如果可能，提前創建<mark style="color:orange;">Stored Procedure</mark>保存查詢語句例程，應用程序直接調用<mark style="color:orange;">Stored Procedure</mark>在性能和穩定性(數據庫鎖定策略等)上更有效。

<br>

## :books: **Unreliable** 訊息

<mark style="color:orange;">Unreliable 信息</mark>(或unreliable send)可以根據通信線的長度和狀態,接收發送方發送的消息內容和順序不同。

例如，發送消息A、B、C、D、E時，接收方有時會收到A、B、C、D、E，但可能會收到兩次相同的信息（A，B，B，C，C，D和E），或者消息在中間丟失（A,B,D），或者消息會按順序不同到達。(A,C,B,E,D)

但是消息內部的數據不會被破壞。 雖然Unreliable消息具有這些缺點，但送達時間比[**Reliable 信息**](#reliable)快。

## :books: UUID 或 GUID

Unique or global universal identifier (<mark style="color:orange;">UUID</mark>或<mark style="color:orange;">GUID</mark>)是16字節大小的數據塊,生成的GUID在概率上是全世界唯一的。

ProudNet DB將UUID分配給每個Gamer、Hero、WorldObject。 雖然因爲UUID是16bytes,所以可以認爲大小比較大,但是UUID具有在地球上不重複的優點,所以在幾個重要案例中很有用。 最具代表性的是服務器整合、玩家賬號移動、玩家ID變更等。

{% hint style="success" %}
**參考**\
[**全域唯一識別碼**](https://ko.wikipedia.org/wiki/%EC%A0%84%EC%97%AD_%EA%B3%A0%EC%9C%A0_%EC%8B%9D%EB%B3%84%EC%9E%90)&#x20;
{% endhint %}

## :books: 競爭狀態 (Race Condition)

在工學領域,<mark style="color:orange;">競爭狀態(race condition)</mark>是指同時進行兩個以上輸入或操作的狀態。 在這種情況下,有無法得出正常結果的危險,這被稱爲<mark style="color:orange;">競爭危險</mark>。

在計算機科學中，<mark style="color:orange;">競爭狀態</mark>是指多個過程試圖同時獲取共享資源的狀態，當同時獲取時，可能會出現破壞數據一致性的結果。 爲了防止這種情況的發生，需要過程協作技術。

{% hint style="success" %}
**參考**

[**競爭狀態 - Wiki**](https://ko.wikipedia.org/wiki/%EA%B2%BD%EC%9F%81_%EC%83%81%ED%83%9C)
{% endhint %}

## :books: 數據量化

當通過網絡交換浮動小數點單位的大值時，可能會出現減少數據包量的詭計。\
例如，如果角色位置x值僅在100到200之間確定，小數點後兩位數的精度可以忽略不計， 此值可轉換爲 100 \* 100 = 10000 或更少的值， 並節省 double (8 字節) 爲 word (2 字節) 。

這個技巧稱為<mark style="color:orange;">量化(quantization)</mark>。

該類提供了量化和量化的相反給付功能。

```cpp
Proud::CQuantizer q(-10000,10000,65535); // - 將10000~10000之間的值以65535等分精度進行量化的功能
double a = 3423.38274f;
int b = q.Quantize(a);      // 量子化
double c= q.Dequantize(b);  // 從量子化值中恢復實際值
```

## :books: Listening Port

由<mark style="color:orange;">主機地址</mark>和<mark style="color:orange;">端口</mark>組成，服務器必須有一個<mark style="color:orange;">listening port</mark>才能接收客戶端的訪問。

<mark style="color:orange;">主機地址</mark>是互聯網上的地址，由111.222.111.222或mycomputer.mydomain.com 格式組成。 <mark style="color:orange;">Port</mark>的值介於1，000到65500之間。\
只要兩個或兩個以上的程序不使用相同的<mark style="color:orange;">listening port</mark>,就可以任意指定。

## :books: Marshaling

將RMI呼叫轉換爲信息或從信息中提取用於呼叫RMI的值稱爲<mark style="color:orange;">marshaling</mark>。\
ProudNet提供int或float等基本類型的Marshaling功能。

<br>

## :books: Multicast

將一條信息一次性傳遞給多個主播的<mark style="color:orange;">multicast</mark>，\
將消息只傳遞給一個主機叫做<mark style="color:orange;">unicast</mark>。

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/UCftt9mM0MuRiS2Kua7i/multicast.png" alt=""><figcaption></figcaption></figure>

## :books: Thread Pool

生成1條線程後，清除的過程會產生大量的通量，因此，如果運行中的線程較多，操作系統可能會超負荷運行。

因此，要保持儘可能少的線程，儘量減少線程的去除和生成過程，爲此，需要設置由一定數量組成的1個線程集合，只在必要時導入使用，必要時返回集合。 這個過程叫做<mark style="color:orange;">thread pool</mark>。

即，<mark style="color:orange;">thread pool</mark>是指由<mark style="color:orange;">多個線程預先準備好的多個線程</mark>組成的<mark style="color:orange;">一個集合</mark>。

### - 例子

> 客戶端A、B、C被服務器容納的狀態下，分別因RMI或事件在<mark style="color:orange;">Proud.CNetServer</mark>內的queue中等待的狀態。<br>
>
> A1,A2,A3 -> 客戶端 A 的事件或 RMI\
> B1,B2,B3 -> 客戶端B的事件或RMI線程池共有兩個線程。\
> \
> \
> 這時根據規則執行如下。<br>
>
> * A1、A2、A3不能同時運行。
> * B1、B2、B3和C1、C2、C3同樣不是同時運行的。
> * A1、A2、A3之一和B1、B2、B3之一、C1、C2、C3之一可以同時運行。
> * 由於線程池中只有2個線程，A、B、C中的2個線程被篩選並回饋，但回饋程序首先完成線程的線程對未篩選的客戶端進行RMI或事件回饋。

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/3w9ImAfgs7Qr0I8q6237/ex_thread_pool.png" alt=""><figcaption><p>線程池實例</p></figcaption></figure>

## :books: 推測導航(dead reckoning)

ProudNet爲了表現遊戲角色的流暢位置,提供<mark style="color:orange;">推測導航(dead reckoning)</mark>工具。

<mark style="color:orange;">推測導航</mark>大致以以下方式操作。

> * 將移動角色的位置和速度從主機A傳送到主機B。 此時傳輸週期爲每秒2\~10次。
> * 傳輸週期最好是動態的。 只有當角色的加速度較大時，縮短傳輸週期才能同步更精確的運動。 加速度大的情況是,角色的速度急劇變化時或角色撞到其他物體,移動方向急劇變化時等。 （見下表）
> * 在主機B中，通過pinging從主機A獲得消息到達所需的時間（延遲）。
> * 當主機B接收到角色的位置、速度信息時，用以下公式預測主機A角色的實際位置。\
>   \
>   **P:** 預測位&#x7F6E;**, V:** 接收速&#x5EA6;**, T:** 延&#x9072;**, P0:** 接收位置\ <mark style="color:orange;">**P = (V \* T) + P0**</mark>

到這裏可以預測角色的位置，但租用計算出的位置值會導致角色的位置斷斷續續的問題。\
使用 <mark style="color:orange;">Proud.CPositionFollower</mark> 來解決這個問題。

\ <mark style="color:orange;">Proud.CPositionFollower</mark>的作用是移動追蹤者(follower)使其在規定時間內到達移動目標位置。 特別是製作成可以直線追蹤移動的目標位置,可以減少其他主機角色位置的突出現象。

下面將描述實現<mark style="color:orange;">推測導航</mark>的其餘步驟。

> * 在主機 B 中，將預測的位置和速度值輸入到 <mark style="color:orange;">Proud.CPositionFollower</mark> 對象中。 此時輸入目標位置、速度。
> * 在主機B中，獲取<mark style="color:orange;">Proud.CPositionFollower</mark>客體的示蹤劑位置，對主機A的角色位置進行渲染。

<figure><img src="https://content.gitbook.com/content/hhO5qj4oN2uLfVC5EG5W/blobs/wrg3VRvJCGvsij4REDdI/dead_reckon.png" alt=""><figcaption></figcaption></figure>

### - 導航示例

P（t=0，1，2）是預測點，紅線是<mark style="color:orange;">Proud.CPositionFollower</mark>校正的角色位置。\
通常，最好將主機A中角色的位置發送週期和跟蹤者在<mark style="color:orange;">Proud.CPositionFollower</mark>中到達目標位置的時間限制設定爲相同，角色位置發送週期因情況而異。

**建議示例**

<table data-full-width="true"><thead><tr><th width="341">情況</th><th>發送的數據類型</th><th width="151" align="center">平均發送週期</th><th>快速增加的加速度示例</th></tr></thead><tbody><tr><td>大型戰RPG遊戲(MMORPG)玩家角色</td><td>位置(xyz),速度(xyz),觀望方向(z)</td><td align="center">0.3</td><td>角色的移動方向轉換，站立，圓點，波普</td></tr><tr><td>飛機或車輛</td><td>位置(xyz),速度(xyz),加速度(xyz), 觀望方向(xyz)</td><td align="center">0.3</td><td>障礙物碰撞、急轉彎</td></tr><tr><td>第一人稱射擊遊戲(FPS)玩家角色</td><td>位置(xyz), 觀望方向(xyz)</td><td align="center">0.03</td><td>角色移動方向改變後，狙擊槍發射瞬間</td></tr></tbody></table>

{% hint style="info" %}
發送週期短時，可能會有發送量過大的危險，建議使用[**發送量自動調節功能 (Throttling)**](https://docs.proudnet.com/proudnet.cn/proudnet/using_pn/tong-xun-xun-xi#throttling)。
{% endhint %}

### - 曲線型跟蹤器(Spline based follower)

<mark style="color:orange;">Proud.CPositionFollower</mark>不僅提供直線，還提供曲線追擊目標的follower。 它是一種三階函數形式的<mark style="color:orange;">曲線型跟蹤器(spline based follower)</mark>。 這顯示了它們以比直線型follower更光滑的形式追趕。 但是，並不總是呈現光滑的面貌，所以最好在遊戲玩法上邊測試邊選擇。<br>

爲了獲得<mark style="color:orange;">曲線型跟蹤器</mark>，請使用以下方法。

> * Proud.CPositionFollower.GetSplineFollowerPosition
> * Proud.CPositionFollower.GetSplineFollowerVelocity

角度的校正處理使用以下方法。

> * <mark style="color:orange;">Proud.CPositionFollower</mark>: 位置補正作用
> * <mark style="color:orange;">Proud.CAngleFollower</mark>: 角度的校準作用

## :books: 打孔 (Hole Punching)

路由器還具有路由器的特性,以製作Routing Table的P2P通信爲目的,事先與對方交換數據包,在各自的路由器上製作Routing Table。\
確切的名稱是 <mark style="color:orange;">STUN (Simple Traversal of User Datagram Protocol Through Network Address Translators)</mark>。

打孔方法如下：

* Full Cone NAT
* Restricted Cone
* Port Restricted Cone
* Symmetric Cone
