# Diarkis Module のカスタマイズ

Diarkis クライアントではコールバック関数をカスタマイズすることで Diarkis サーバーに接続完了した時や Room に参加した時など様々なイベントに合わせて任意の処理を実行することができます。

ここでは、クイックスタート で作成した TutorialSubsystem クラスを変更し、ConnectUdp 関数から ConnectUdpAsync 関数に変更し、コールバック関数を使って接続状態を判断するように変更する方法について解説します。

#### ConnectUdpAsync 関数への変更

`ConnectUdp` 関数を実行するとサーバーに接続できない場合、タイムアウトするまで動作が停止します。そこで応答性を高めるために `ConnectUdpAsync` 関数に変更します。

```cpp
// TutorialSubsystem.cpp
	
	...
	// UDP サーバに接続
	// Connect to the UDP server
	UE_LOG(LogTemp, Log, TEXT("Connecting to the UDP server..."));
	bool result = DiarkisInterfaceInstance->ConnectUdpAsync(endpoint);
```

また、実際にサーバーへの接続処理が完了する前に `ConnectUdpAsync` 関数の次の処理が実行されるため、下記のログの処理を削除します。

```cpp
UE_LOG(LogTemp, Log, TEXT("Connected to the UDP server")); // <-- 削除
```

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

1. DiarkisUdpBase クラスを継承したクラスを作成します。

   ```cpp
   // TutorialSubsystem.h

   ...
   #include "DiarkisUdpBase.h"

   class DiarkisUdpTutorial;
   ...
   // Diarkis Module の Udp 機能をアプリ側でカスタマイズするために DiarkisUdpBase を継承したクラスを実装します。
   // Implement a class that inherits from DiarkisUdpBase to customize the Udp functionality of the Diarkis Module on the application side.
   class DiarkisUdpTutorial : public Diarkis::DiarkisUdpBase
   {
   private:
       void OnConnect(const DiarkisConnectionEventArgs& args) override;
   };
   ```

   ```cpp
   // TutorialSubsystem.cpp

   ...
   void DiarkisUdpTutorial::OnConnect(const DiarkisConnectionEventArgs& args)
   {
   	if (args.GetStatus() == Diarkis::DiarkisConnectStatus::DCS_Timeout)
     {
   		UE_LOG(LogTemp, Error, TEXT("Timeout occurred while connecting to the UDP server"));
   		return;
   	}
       UE_LOG(LogTemp, Log, TEXT("Connected to the UDP server"));
   }
   ```
2. DiarkisIntefaceBase クラスを継承したクラスを作成します。

   ```cpp
   // TutorialSubsystem.h
    
   ...
   #include "DiarkisUdpBase.h"
   #include "DiarkisInterfaceBase.h"
   ...
   class DiarkisUdpTutorial;
   class DiarkisInterfaceTutorial;
   ...
   // DiarkisInterface が内部で管理する各機能のモジュールをアプリ固有のものに置き換えるために DiarkisInterfaceBase を継承したクラスを実装します。
   // Implement a class that inherits from DiarkisInterfaceBase to replace each functional module managed internally by DiarkisInterface with application-specific ones.
   class DiarkisInterfaceTutorial : public Diarkis::DiarkisInterfaceBase
   {
   public:
       DiarkisInterfaceTutorial(const std::string& uid) : Diarkis::DiarkisInterfaceBase(uid) {};

       bool SetupUdp() override;
   };
   ```

   ```cpp
   // TutorialSubsystem.cpp

   bool DiarkisInterfaceTutorial::SetupUdp()
   {
       // アプリ側でカスタマイズした DiarkisUdpTutorial クラスを作成します
       // Create a DiarkisUdpTutorial class customized on the application side
       if (udpBase_ == nullptr)
       {
           udpBase_ = Diarkis::DiarkisAllocShared<DiarkisUdpTutorial>();
       }

       DiarkisInterfaceBase::SetupUdp();

       return true;
   }
   ```
3. DiarkisInterfaceBase クラスを作成していた部分を書き換えます。

   ```cpp
   // TutorialSubsystem.h

   UCLASS()
   class TUTORIAL_API UTutorialSubsystem : public UGameInstanceSubsystem
   {
   ...
   private:
       std::shared_ptr<DiarkisInterfaceTutorial> DiarkisInterfaceInstance;
   ```

   ```cpp
   // TutorialSubsystem.cpp 

   void UTutorialSubsystem::Initialize(FSubsystemCollectionBase& Collection)
   {
       ...
       // DiarkisInterfaceインスタンスの作成
       // Create DiarkisInterface instance
       DiarkisInterfaceInstance = Diarkis::DiarkisAllocShared<DiarkisInterfaceTutorial>("AAAA");
   ```

最終的なコードは下記のようになります。

```cpp
// TutorialSubsystem.h

#pragma once

#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "DiarkisUdpBase.h"
#include "DiarkisInterfaceBase.h"
#include "TutorialSubsystem.generated.h"

class DiarkisUdpTutorial;
class DiarkisInterfaceTutorial;

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

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

private:
	std::shared_ptr<DiarkisInterfaceTutorial> DiarkisInterfaceInstance;
};

// Diarkis Module の Udp 機能をアプリ側でカスタマイズするために DiarkisUdpBase を継承したクラスを実装します。
// Implement a class that inherits from DiarkisUdpBase to customize the Udp functionality of the Diarkis Module on the application side.
class DiarkisUdpTutorial : public Diarkis::DiarkisUdpBase
{
private:
    void OnConnect(const DiarkisConnectionEventArgs& args) override;
};

// DiarkisInterface が内部で管理する各機能のモジュールをアプリ固有のものに置き換えるために DiarkisInterfaceBase を継承したクラスを実装します。
// Implement a class that inherits from DiarkisInterfaceBase to replace each functional module managed internally by DiarkisInterface with application-specific ones.
class DiarkisInterfaceTutorial : public Diarkis::DiarkisInterfaceBase
{
public:
	DiarkisInterfaceTutorial(const std::string& uid) : Diarkis::DiarkisInterfaceBase(uid) {};

    bool SetupUdp() override;
};

```

```cpp
// TutorialSubsystem.cpp


#include "TutorialSubsystem.h"
#include "Kismet/KismetSystemLibrary.h"

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

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

	// Diarkis ライブラリの初期化
	// Initialize Diarkis library
	UE_LOG(LogTemp, Log, TEXT("UTutorialSubsystem: Initialize - Calling DiarkisInit"));
	Diarkis::DiarkisInterfaceBase::DiarkisInit("logs", Diarkis::LogOutType::FILE_OUT, true);

	// DiarkisInterfaceインスタンスの作成
	// Create DiarkisInterface instance
	DiarkisInterfaceInstance = Diarkis::DiarkisAllocShared<DiarkisInterfaceTutorial>("AAAA");
	if (!DiarkisInterfaceInstance)
	{
		UE_LOG(LogTemp, Error, TEXT("DiarkisInterfaceInstance creation failed"));
		Diarkis::DiarkisInterfaceBase::DiarkisDestroy();

		UKismetSystemLibrary::QuitGame(GetWorld(), nullptr, EQuitPreference::Quit, false);
	}

	// Diarkis UDP クラスを初期化
	// Initialize Diarkis UDP class
	DiarkisInterfaceInstance->SetupUdp();

	// Http サーバから UDP サーバの Endpoint を取得
	// Get the endpoint of the UDP server from the HTTP server
	char endpoint[256];
	if (!DiarkisInterfaceInstance->GetEndpoint("127.0.0.1:7000", "", "UDP", endpoint, 256))
	{
		UE_LOG(LogTemp, Error, TEXT("Failed to get UDP endpoint"));

		Diarkis::DiarkisInterfaceBase::DiarkisDestroy();
		UKismetSystemLibrary::QuitGame(GetWorld(), nullptr, EQuitPreference::Quit, false);
	}

	UE_LOG(LogTemp, Log, TEXT("Endpoint: %s"), UTF8_TO_TCHAR(endpoint));

	// UDP サーバに接続
	// Connect to the UDP server
	UE_LOG(LogTemp, Log, TEXT("Connecting to the UDP server..."));
	bool result = DiarkisInterfaceInstance->ConnectUdpAsync(endpoint);

	if (!result)
	{
		UE_LOG(LogTemp, Error, TEXT("Failed to connect to the UDP server"));
		Diarkis::DiarkisInterfaceBase::DiarkisDestroy();
		UKismetSystemLibrary::QuitGame(GetWorld(), nullptr, EQuitPreference::Quit, false);
	}

	UE_LOG(LogTemp, Log, TEXT("Connected to the UDP server"));
}

void UTutorialSubsystem::Deinitialize()
{
	if (!DiarkisInterfaceInstance)
	{
		UE_LOG(LogTemp, Warning, TEXT("DiarkisInterfaceInstance 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..."));
	DiarkisInterfaceInstance->Disconnect();
	UE_LOG(LogTemp, Log, TEXT("Disconnected from the UDP server."));

	DiarkisInterfaceInstance.reset();

	UE_LOG(LogTemp, Log, TEXT("UTutorialSubsystem: Deinitialize - Calling DiarkisDestroy"));
	Diarkis::DiarkisInterfaceBase::DiarkisDestroy();

	Super::Deinitialize();
}

void DiarkisUdpTutorial::OnConnect(const DiarkisConnectionEventArgs& args)
{
	if (args.GetStatus() == Diarkis::DiarkisConnectStatus::DCS_Timeout)
	{
		UE_LOG(LogTemp, Error, TEXT("Timeout occurred while connecting to the UDP server"));
		return;
	}
	UE_LOG(LogTemp, Log, TEXT("Connected to the UDP server"));
}

bool DiarkisInterfaceTutorial::SetupUdp()
{
	// アプリ側でカスタマイズした DiarkisUdpTutorial クラスを作成します
	// Create a DiarkisUdpTutorial class customized on the application side
	if (udpBase_ == nullptr)
	{
		udpBase_ = Diarkis::DiarkisAllocShared<DiarkisUdpTutorial>();
	}

	DiarkisInterfaceBase::SetupUdp();

	return true;
}

```

処理を追加した後は、再度ビルドし、ゲーム起動時に `"Connected to the UDP server"` というログが表示され、ゲーム終了時に `"Disconnected from the UDP server."` というログが表示されることを確認します。

ここまでで UE Plugin を使い、Diarkis サーバーへの非同期接続とコールバック関数を使って接続状態を判断することができました。

今回は、コールバック関数の中でスレッドセーフな UE\_LOG 関数しか実行していませんが、実際のアプリケーションではコールバック内で様々な処理を実行することが想定されます。しかし、コールバックは Diarkis が管理するイベントスレッドで発火され、ゲームスレッドで実行されるわけではないため、このコールバック内で `SpawnActor` 関数の実行などの UE 関連の機能を使用することができません。\
これらの課題に対応するために DiarkisPluginSample では様々なイベント処理が実装されています。

次のページで DiarkisPluginSample に含まれる DiarkisExtension プラグインを使用して、ゲームスレッドでコールバック関数を実行する方法を説明します。


---

# 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/diarkis-module-nokasutamaizu.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.
