2025-03-14TIL 채팅서버

별빛에소원을·2025년 3월 14일

TeamSparta-Unreal1기-TIL

목록 보기
57/90
post-thumbnail

UnrealServer

언리얼 서버는 데디케이트와 리슨서버로 구분 되는데
데디케이트는 전용서버를 말하고 리슨서버는 게스트가 호스트가 될 수 있는 서버를 이야기 한다.

채팅창 입력 구현


대충만든 스크룰 박스와
대충만든 입력창을 넣어둔다.

EditableTextBox

해당 입력창은 OnTexCommitted()를 통해서 입력에 대한 이벤트를 여러 함수를 제공한다.

  • Default
  • OnEnter
    엔터를 눌렀을 때 동작을 정의할 수 있다.
    c++로 만들었기 때문에 text가 있을 경우 Server로 전송하도록 했다.
  • OnUserMovedFocus
  • OnCleared

GameMode

게임모드의 기능은 다음과같다.

  • 클라이언트에서 온 메시지를 브로드 캐스트한다.
// 헤더
UCLASS()
class CHATSERVERPRJ_API AChatGameMode : public AGameModeBase
{
	GENERATED_BODY()
public:
	AChatGameMode();
public:
	//	Client to Server
	UFUNCTION(Server, Reliable, WithValidation)
	void ReceiveClientMessage(APlayerController* Sender, const FString& Message);
	//	NetMultiCast는 서버에서 클라이언트에게 브로드캐스트를 하기 위한 지정자
	UFUNCTION(NetMulticast, Reliable)
	void BroadcastMessage(const FString& SenderName, const FString& Message);
protected:
	virtual void BeginPlay() override;
};

UFUNCTION의 지정자
Server는 클라이언트에서 서버로 보내는 데이터일 경우 사용하는 것이다.
Multicast는 서버에서 모든 클라이언트에게 적용될 경우 사용된다.

특이사항으로는 해당 함수를 구현할 별도의 함수를 통해서

Controller

서버와 클라이언트의 게이트웨이는 컨트롤러가 하므로 컨트롤러의 역할은 다음과같다.

  • 서버에서 온 메시지를 받는다.
  • 메시지를 서버에 전달한다.
  • 고유한 플레이어의 아이디를 가진다.
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "ChatController.generated.h"

UCLASS()
class CHATSERVERPRJ_API AChatController : public APlayerController
{
	GENERATED_BODY()
protected:
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TSubclassOf<class UChatWindow> ChatWindowClass;
	//TSoftClassPtr<class UChatWindow> ChatWindowClass;
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TSoftObjectPtr<class UChatWindow> ChatWindowInstance;
	UPROPERTY(VisibleAnywhere,BlueprintReadOnly)
	FString ControllerID;
public:
	UFUNCTION(Client, Reliable)
	void ReceiveMessage(const FString& SenderName,const FString& Message);
	UFUNCTION(Server, Reliable)
	void SendServerMessage(APlayerController* Sender, const FString& Message);
	UFUNCTION(BlueprintCallable,BlueprintPure)
	FString GetUserID() const;
protected:
	virtual void BeginPlay() override;
	bool IsServer();
};

#include "ChatController.h"
#include "Kismet/GameplayStatics.h"
#include "ChatServerPrj/GameUI/ChatWindow.h"
#include "ChatGameMode.h"
#include "ChatGameState.h"

void AChatController::ReceiveMessage_Implementation(const FString& SenderName,const FString& Message)
{
	if (ChatWindowInstance)
	{
		ChatWindowInstance->AddMessage(SenderName, Message);
	}
}

void AChatController::SendServerMessage_Implementation(APlayerController* Sender,const FString& Message)
{	
	if (AChatGameState* ChatGameState = GetWorld()->GetGameState<AChatGameState>())
	{		
		if (AChatController* Cont = Cast<AChatController>(Sender))
		{
			ChatGameState->BroadcastMessage(Cont->GetUserID(), Message);
		}
	}
}

FString AChatController::GetUserID() const
{	
	return ControllerID;
}

void AChatController::BeginPlay()
{
	Super::BeginPlay();

	if (IsServer())
	{
		ControllerID = FString::Printf(TEXT("[Host]"));
	}
	else
	{
		ControllerID = FString::Printf(TEXT("[Guest%d]"),GetUniqueID());
	}
	if (!GetRemoteRole() == ROLE_None)
	{
		//	T딜레이 줘야함
		if (ChatWindowClass)
		{
			ChatWindowInstance = CreateWidget<UChatWindow>(this, ChatWindowClass.Get());
			if (ChatWindowInstance.IsValid())
			{
				ChatWindowInstance->AddToViewport();
				bShowMouseCursor = true;
				SetInputMode(FInputModeGameAndUI());
			}
		}
	}
}

bool AChatController::IsServer()
{
	if (UWorld* World = GetWorld())
	{
		ENetMode NetMode = World->GetNetMode();

		switch (NetMode)
		{
		case NM_Standalone:
			break;
		case NM_DedicatedServer:
			return true;
			break;
		case NM_ListenServer:
			if (IsLocalController())
			{
				return true;
			}
			else
			{
				return false;
			}
			break;
		case NM_Client:
			break;
		case NM_MAX:
			break;
		default:
			break;
		}
	}
	return false;
}

GameState

GameMode는 서버에만 존재하기 때문에 Multicast를 GameMode에 두게 되면 Server에서만 실행되고 클라이언트에서는 실행되지 않을 가능성이 높다.
따라서 Controller나 State에 Multicast를 두는것이 바람직하다.

NetMulticast는 복제된 엑터에만 적용이 가능하다. 그러나 Controller는 클라이언트마다 고유한 객체 하나만 가져야 하기 때문에 복제가 되지 않고 Multicast는 작동하지 않을 수 있다.

Gamestate는 클라이언트에게 복제되므로 동작하는데 문제가 없으므로, GameState에 두는것이 바람직하다.

클래스복제여부multicast적합여부적용 사례
Controller(Host)X클라이언트별 로직, UI-
AGameStateOO게임 전체 브로드캐스트
AGameModeX(서버만)X게임 규칙, 서버로직
profile
취미로 게임하는사람

0개의 댓글