# packet\_manipulator\_custom\_filter

## packet\_manipulator\_custom\_filter サンプル

<figure><img src="https://669307705-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FlFJ89PMX2ike3NyauXNM%2Fuploads%2FQorW1PKeHuIA6mw8kPUh%2Fimage.png?alt=media&#x26;token=c2f6e87a-2481-4078-84b5-654ca3a47592" alt=""><figcaption></figcaption></figure>

### 概要

packet\_manipulator\_custom\_filter サンプルは Packet Manipulator で使用する、Custom Filter の実装方法と使用方法のサンプルとなっています。\
サンプルコードでは Packet Manipulator の実装コードのフォーカスしているため、Diarkis Runtime の基本的な実装については関数化して説明等は記載していません。 Diarkis Runtime の基本的な実装について知りたい場合は [room\_broadcast サンプル](https://help.diarkis.io/diarkis-client/samples/cpp/broken-reference) や [Diarkis モジュール利用の全体的な流れ](https://help.diarkis.io/diarkis-module#diarkis-module-noi) を参照してください。 また、Packet Manipulator 全般については [Packet Manipulator](https://help.diarkis.io/diarkis-client/runtime-library/packet-manipulator) を参照してください。

**本サンプルはデバッグ機能のサンプル実装のため Release ビルドはできません。**

### ローカル環境でサーバーを起動する

サンプルで使用するサーバーを起動するためのチュートリアルを実施して、ローカル環境で Diarkis サーバーを起動します。

[Diarkis サーバをローカルで起動する](https://help.diarkis.io/getting-started/tutorial/setup-local-server)

### サンプルの引数

サンプルの起動時には以下の３つのパラメータを指定してください。

`packet_manipulator_custom_filter.exe serverAddr UID clientKey`

| 引数         | 説明                            |
| ---------- | ----------------------------- |
| serverAddr | Diarkis サーバのエンドポイントを指定してください  |
| UID        | 接続するユーザーの ID を任意の文字列で指定してください |
| clientKey  | クライアントキーを任意の文字列で指定してください      |

#### 起動例：

`packet_manipulator_custom_filter.exe 192.168.1.123:7000 1111 AAAA`

### 起動方法

本サンプルは起動すると Room に接続し、Room のメンバー数が 2 人になるまで待機します。 2 ユーザーが Room に接続後、Packet Manipulator のフィルタ設定を Room と P2P で通信を行い Packet Filter の適用結果を表示しています。

## packet\_manipulator\_custom\_filter サンプル解説

本セクションではサンプルの実装ポイントについて解説します。 Packet Manipulator の使用方法の基本については [packet\_manipulator\_simple サンプル](https://help.diarkis.io/diarkis-client/samples/cpp/packet_manipulator_simple) をご参照ください。

### Custom Filter の実装の基本

本サンプルでは Room と P2P それぞれのパケットに対してのみフィルタを適用する Custom Filter を実装しています。\
ここでは P2P 用 Custom Filter を例にとり実装方法を解説します。\
以下が P2P メッセージパケット用の Custom Filter の全体となります。

```
/**
* @brief Custom Packet Loss Filter for P2P packets.
* @details A user can implement custom packet filter for P2P packets by inheriting from IP2PMessageReceiveFilterBase.
*          You can access the packet data and related information from the argument passed to the Apply function.
*/
class P2PPacketLossFilter : public Diarkis::System::PacketManipulator::IP2PMessageReceiveFilterBase
{
public:
    P2PPacketLossFilter() : randomGenerator_(12345) {}
    ~P2PPacketLossFilter() override {}

    /**
    * @brief Apply the filter to a P2P packet.
    * @details This method will be called for each received P2P packet.
    */
    Diarkis::System::PacketManipulator::FilterPostProcess Apply(Diarkis::System::PacketManipulator::IP2PMessageReceiveFilterArgument& arg) override
    {
        // In this sample, we drop 30% of the received P2P packets randomly.
        if (randomGenerator_() % 100 < 10)
        {
            DiarkisUtils::Print("Drop P2P packet from %s(%s:%d)", arg.GetPeerUID().c_str(), arg.GetPeerResolvedAddress().c_str(), arg.GetPeerPort());
            // The packet filter will stop processing the packet and drop it when returning Skip.
            return Diarkis::System::PacketManipulator::FilterPostProcess::Skip;
        }
        return Diarkis::System::PacketManipulator::FilterPostProcess::ApplyNextFilter;
    }
private:
    std::mt19937 randomGenerator_;
};
```

[Packet Manipulator の User Custom Filter](https://help.diarkis.io/runtime-library/packet-manipulator#user-custom-filter) にあるように、基本的には各 Apply Point 専用の Filter Base を継承して Custom Filter を実装します。今回は `P2PMessageReceive` Apply Point のため `IP2PMessageReceiveFilterBase` を継承して実装しています。 Packet Filter の実装ではフィルタ適用時に実行される `Apply` メソッドをオーバーライドすることで処理をカスタマイズします。この Filter Base では `IP2PMessageReceiveFilterArgument` を受け取ることのできる `Apply` メソッドが純粋仮想関数として宣言されており、P2P 専用の情報を受け取って `Apply` 処理を実装することが可能となっています。\
また、`Apply` メソッドの返り値でその後のパケットの扱いをコントロールすることができます。`FilterPostProcess::Skip` を返すことでその後、Diarkis Runtime 内ではそのパケットが無かったものとして扱われます。`FilterPostProcess::ApplyNextFilter` を返すと通常通りの処理を継続します。 サンプル実装では単純に確率でパケロスさせるように `Apply` メソッドの返り値をコントロールしています。

### 複雑な Custom Filter 実装

`RoomPacketDelayFilter` では Room の push/response パケットに対して遅延を発生させる仕組みを実装しています。

`RoomPacketDelayFilter::Apply` で Packet Filter 内に受け取ったパケットの情報を保存し、そのタイミングではパケットを無視するように返り値を返します。 `RoomPacketDelayFilter::Apply` に渡されるパケットの情報は一時的なもののため、`IPacketFilterArgument::DeepCopy` を使用してパケットデータのコピーを保存します。

```
/**
* @brief Apply the filter to a room packet.
* @details This method will be called for each received room push and response packet.
*/
Diarkis::System::PacketManipulator::FilterPostProcess Apply(Diarkis::System::PacketManipulator::IRoomPushAndResponseFilterArgument& arg) override
{
    // This method and Update method will be called from the various thread in the Diarkis runtime and the user thread.
    // So we need to protect the internal data with mutex.
    std::lock_guard<std::mutex> lock(mutex_);

    if (randomGenerator_() % 100 < 10)
    {
        ...
        // Store the packet argument into the buffer for delay processing.
        // You can deep copy the argument to hold it for later processing.
        delayPackets_.emplace_back(arg.DeepCopy());

        return Diarkis::System::PacketManipulator::FilterPostProcess::Skip;
    }

    return Diarkis::System::PacketManipulator::FilterPostProcess::ApplyNextFilter;
}

```

その後、`RoomPacketDelayFilter::Update` で時間を計測し、遅延分の時間が経過した後に通常の受信時の処理を行うようになっています。 `RoomPacketDelayFilter::Update` の引数として実際にパケットを処理する関数が渡されるため、その関数を呼び出すことで通常通りのパケットの処理を行います。

```
/**
* @brief Update function called periodically to process any pending operations.
* @details This method will be called periodically from the Packet Manipulator update loop.
*/
void Update(std::function<Result(Diarkis::System::PacketManipulator::IPacketFilterArgument*)> packetHandler) override
{
    // This method and Apply method will be called from the various thread in the Diarkis runtime and the user thread.
    // So we need to protect the internal data with mutex.
    std::lock_guard<std::mutex> lock(mutex_);

    // Process all delayed packets in the buffer.
    auto iter = delayPackets_.begin();
    uint32_t deliveredCount = deliveredPacketIndex_;
    while (iter != delayPackets_.end())
    {
        ...
        // This method expects to be passed the packet filter argument that was passed to the Apply method.
        packetHandler((*iter).get());
    }
    ...
}

```
