# DiarkisExtension を使用したゲームスレッドでのコールバック

ここでは、DiarkisPluginSample に含まれる DiarkisExtension の `DiarkisDispatch` クラスを使用して、ゲームスレッドでコールバック関数を実行する方法を説明します。

`DiarkisDispatch` クラスを使用したイベント処理については、[イベント処理の注意点](/diarkis-client/game-engine-integration/ue/tips-for-event-processing.md#diarkisdispatch-kurasuwoshitami) を参照してください。

{% hint style="info" %}
「DiarkisExtension」では様々な機能が実装されていますが、「UE Plugin」と異なりこれらはあくまでサンプルコードとなりますので、将来的に互換性が無い仕様変更が発生したり、不具合が存在する可能性があります。 ここではチュートリアルのため、「DiarkisExtension」に含まれる NetworkManager を使用し、ゲームスレッドでコールバック関数を実行しておりますが、コールバック処理を受け取る仕組みについては自前で実装されることをご検討ください。
{% endhint %}

#### DiarkisExtension のインストール

1. DiarkisExtension のコピー

   * 解凍した DiarkisPluginSample の Source フォルダ内の DiarkisExtension フォルダをチュートリアルプロジェクトの Source フォルダにコピーします。

   ```txt
   .
   ├── Config
   ├── Content
   ├── Source
   │   └── DiarkisExtension ← (ここに配置)
   ├── Plugins
   └── Tutorial.uproject
   ```
2. DiarkisExtension をプロジェクトの依存関係に追加

   * Source/Tutorial/Tutorial.Build.cs を開き、 以下のように DiarkisExtension を依存関係に追加します。

   ```csharp
   // Tutorial.Build.cs

   public class Tutorial : ModuleRules
   {
       public Tutorial(ReadOnlyTargetRules Target) : base(Target)
       {
           PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
       
           PublicDependencyModuleNames.AddRange(new string[] {
               "Core",
               "CoreUObject",
               "Engine",
               "InputCore",
               "EnhancedInput",
               "Diarkis",
               "DiarkisExtension" // <-- 追加
           });
   ```
3. プロジェクトを再ビルド
   * DiarkisExtension を追加した後、プロジェクトを再ビルドします。

#### DiarkisUdpDelegate の子クラスの作成

DiarkisUdpBase クラスのコールバックを受け取るために、 `IDiarkisUdpDelegate` の子クラスを作成します。

Tools -> New C++ Class をクリックし、親クラスに None を指定します。&#x20;

<figure><img src="/files/BBTpKIHMJSB5f50pMmrr" alt=""><figcaption></figcaption></figure>

DiarkisUdpTutorialDelegate という名前でクラスを作成します。&#x20;

<figure><img src="/files/j49u3oiWpc7V0V7CE6iq" alt=""><figcaption></figcaption></figure>

`IDiarkisUdpDelegate` クラスを継承します。

```cpp
// DiarkisUdpTutorialDelegate.h

#pragma once

#include "CoreMinimal.h"
#include "DiarkisExtension/Public/Delegate/DiarkisUdpDelegate.h"

class TUTORIAL_API DiarkisUdpTutorialDelegate : public IDiarkisUdpDelegate
{
public:
	DiarkisUdpTutorialDelegate();
	~DiarkisUdpTutorialDelegate();
};
```

#### コールバックの実装

接続が完了したときのコールバックを実装します。

```cpp
// DiarkisUdpTutorialDelegate.h

...
private:
    void OnConnect(DiarkisUdpBase& udp, const ConnectArgs& args) override;
};
```

```cpp
// DiarkisUdpTutorialDelegate.cpp

...
void DiarkisUdpTutorialDelegate::OnConnect(DiarkisUdpBase& udp, const ConnectArgs& args)
{
    if (!IsInGameThread())
    {
        UE_LOG(LogTemp, Warning, TEXT("OnConnect called outside of game thread"));
        return;
    }
    
    if (!GEngine)
    {
        UE_LOG(LogTemp, Warning, TEXT("GEngine is null"));
        return;
    }

    if (args.connectStatus == DiarkisConnectStatus::DCS_Timeout)
    {
        GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Red, TEXT("Timeout occurred while connecting to the UDP server"));
        return;
    }

    GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Green, TEXT("Connected to the UDP server"));
    UE_LOG(LogTemp, Log, TEXT("Connected to the UDP server"));
}
```

Delegate クラスを継承したこのクラスでは、ゲームスレッドでコールバック関数が実行されるため、`AddOnScreenDebugMessage` 関数などの UE 関連の機能を安全に使用することができます。

#### DiarkisNetworkManager を使用した Diarkis サーバーへの接続

DiarkisDispatch クラスを使用したイベント処理を行うためには、DiarkisNetworkManager クラスを使用して DiarkisInterface インスタンスを管理する必要があります。そこで、TutorialSubsystem クラスを書き換え、DiarkisNetworkManager クラスの `ConnectAsync` 関数を使います。 `DiarkisInterfaceBase::DiarkisInit` 関数の呼び出しは DiarkisNetworkManager 内で行われるため、TutorialSubsystem クラス内で呼び出す必要はありません。詳細については、DiarkisNetworkManager クラスの実装を参照してください。

```cpp
// TutorialSubsystem.h

#pragma once

#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "DiarkisExtension/Public/DiarkisNetworkManager.h"
#include "TutorialSubsystem.generated.h"

UCLASS()
class TUTORIAL_API UTutorialSubsystem : public UGameInstanceSubsystem
{
	GENERATED_BODY()

public:
	UTutorialSubsystem();

	void Initialize(FSubsystemCollectionBase& Collection) override;
	void Deinitialize() override;

private:
	UPROPERTY()
	UDiarkisNetworkManager* NetworkManager;
};

```

```cpp
// TutorialSubsystem.cpp


#include "TutorialSubsystem.h"
#include "Kismet/KismetSystemLibrary.h"
#include "DiarkisExtension/Public/DiarkisNetworkManager.h"

UTutorialSubsystem::UTutorialSubsystem()
{
	UE_LOG(LogTemp, Log, TEXT("UTutorialSubsystem: Constructor"));
}

void UTutorialSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
	Super::Initialize(Collection);

	// UDiarkisNetworkManager インスタンスの取得
	// Get UDiarkisNetworkManager instance
	NetworkManager = UDiarkisNetworkManager::Get(GetWorld());
	if (!NetworkManager)
	{
		UE_LOG(LogTemp, Error, TEXT("Failed to get UDiarkisNetworkManager instance"));
		UKismetSystemLibrary::QuitGame(GetWorld(), nullptr, EQuitPreference::Quit, false);
		return;
	}

	// DiarkisNetworkManager を使用した、UDP サーバへの接続
	// Connect to UDP server using DiarkisNetworkManager
	std::string endpoint = "127.0.0.1:7000";
	std::string clientKey = "";
	std::string uid = "AAAA";
	std::string serverType = "UDP";

	std::shared_ptr<DiarkisInterface> diarkis = NetworkManager->ConnectAsync(endpoint, clientKey, uid, serverType, true);
	if (!diarkis)
	{
		UE_LOG(LogTemp, Error, TEXT("Failed to get endpoint."));
		UKismetSystemLibrary::QuitGame(GetWorld(), nullptr, EQuitPreference::Quit, false);
		return;
	}
}

void UTutorialSubsystem::Deinitialize()
{
	if (!NetworkManager)
	{
		UE_LOG(LogTemp, Warning, TEXT("NetworkManager is null during Deinitialize"));

		DiarkisInterfaceBase::DiarkisDestroy();
		Super::Deinitialize();
		return;
	}

	// UDP サーバから切断
	// Disconnect from the UDP server
	UE_LOG(LogTemp, Log, TEXT("Disconnecting from the UDP server..."));
	NetworkManager->Disconnect();
	UE_LOG(LogTemp, Log, TEXT("Disconnected from the UDP server."));

	Super::Deinitialize();
}

```

#### DiarkisUdpTutorialDelegate クラスの登録

ゲームスレッドでコールバックを実行するには、`DiarkisUdp` クラスの `SetDelegate` 関数を実行し、`IDiarkisUdpDelegate` を継承したクラスを登録する必要があります。

`DiarkisUdpTutorialDelegate` クラスのインスタンスを作成し、DiarkisUdp に登録します。

```cpp
// TutorialSubsystem.h
...
#include "DiarkisExtension/Public/DiarkisNetworkManager.h"
...
private:
    std::shared_ptr<DiarkisUdp> DiarkisUdpInstance_;
    std::shared_ptr<DiarkisUdpTutorialDelegate> UdpDelegate_;
```

```cpp
// TutorialSubsystem.cpp
...
	std::shared_ptr<DiarkisInterface> diarkis = NetworkManager->ConnectAsync(endpoint, clientKey, uid, serverType, true);
	if (!diarkis)
	{
		UE_LOG(LogTemp, Error, TEXT("Failed to get endpoint."));
		UKismetSystemLibrary::QuitGame(GetWorld(), nullptr, EQuitPreference::Quit, false);
		return;
	}

	// Udp モジュール用のDelegateを設定
	DiarkisUdpInstance_ = static_pointer_cast<DiarkisUdp>(diarkis->GetUdpBase());
	if (!DiarkisUdpInstance_)
	{
		UE_LOG(LogTemp, Error, TEXT("Failed to get DiarkisUdp instance."));
		UKismetSystemLibrary::QuitGame(GetWorld(), nullptr, EQuitPreference::Quit, false);
		return;
	}
	UdpDelegate_ = std::make_shared<DiarkisUdpTutorialDelegate>();
	if (UdpDelegate_)
	{
		DiarkisUdpInstance_->SetDelegate(UdpDelegate_.get());
	}
...
```

Deinitialize 関数内で UdpDelegate\_ をリセットします。

```cpp
void UTutorialSubsystem::Deinitialize()
{
	if (!NetworkManager)
	{
		UE_LOG(LogTemp, Warning, TEXT("NetworkManager is null during Deinitialize"));

		DiarkisInterfaceBase::DiarkisDestroy();
		Super::Deinitialize();
		return;
	}

	// UDP モジュールの Delegate をリセット
	if (DiarkisUdpInstance_)
	{
		DiarkisUdpInstance_->SetDelegate(nullptr);
	}
	if (UdpDelegate_)
	{
		UdpDelegate_.reset();
	}

	// UDP サーバから切断
	// Disconnect from the UDP server
	UE_LOG(LogTemp, Log, TEXT("Disconnecting from the UDP server..."));
	NetworkManager->Disconnect();
	UE_LOG(LogTemp, Log, TEXT("Disconnected from the UDP server."));

	Super::Deinitialize();
}
```

#### 動作確認

コールバックを実装した後は、再度ビルドし、ゲーム起動時に 画面上に "Connected to the UDP server" というデバッグメッセージとログが表示され、ゲーム終了時に "Disconnected from the UDP server." というログが表示されることを確認します。&#x20;

<figure><img src="/files/dCQ4sJwX5mnxmwlYsosN" alt=""><figcaption></figcaption></figure>

ここまでで、UDP サーバーに非同期的に接続し、ゲームスレッドでコールバック関数を実行することができました。


---

# 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://help.diarkis.io/diarkis-client/chtoriaru/unreal-engine-chtoriaru/diarkisextension-woshitagmusureddodenokrubakku.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.
