일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- CTF
- 언리얼엔진
- Aegis
- 유니티
- photon fusion2
- unity
- network object pooling
- Replication
- os
- local prediction
- 게임개발
- stride
- gameplay tag
- gameplay ability system
- map design
- 보안
- animation
- nanite
- gameplay effect
- rpc
- UI
- MAC
- listen server
- gas
- Multiplay
- 언리얼 엔진
- 게임 개발
- Unreal Engine
- ability task
- attribute
Archives
- Today
- Total
Replicated
[Drag Down] 자체 매치메이킹 연동 성공 (Unreal <-> Spring Boot) 본문
USTRUCT(BlueprintType)
struct FRoomSummary
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly) FString RoomId;
UPROPERTY(EditAnywhere, BlueprintReadOnly) FString RoomName;
UPROPERTY(EditAnywhere, BlueprintReadOnly) FString HostUserName;
UPROPERTY(EditAnywhere, BlueprintReadOnly) int32 CurrentPlayerCount;
UPROPERTY(EditAnywhere, BlueprintReadOnly) int32 MaxPlayers;
UPROPERTY(EditAnywhere, BlueprintReadOnly) bool bIsGameStarted;
};
USTRUCT(BlueprintType)
struct FRoomDetails
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly) FString RoomId;
UPROPERTY(EditAnywhere, BlueprintReadOnly) FString RoomName;
UPROPERTY(EditAnywhere, BlueprintReadOnly) FString HostUserName;
UPROPERTY(EditAnywhere, BlueprintReadOnly) FString HostIPAddress;
UPROPERTY(EditAnywhere, BlueprintReadOnly) int32 HostPort;
UPROPERTY(EditAnywhere, BlueprintReadOnly) int32 MaxPlayers;
UPROPERTY(EditAnywhere, BlueprintReadOnly) TArray<FString> Players;
UPROPERTY(EditAnywhere, BlueprintReadOnly) TMap<FString, FString> PlayerIPs; // all users' username -> ipAddress mapped
UPROPERTY(EditAnywhere, BlueprintReadOnly) bool bIsGameStarted;
};
일단 DTO
룸 생성 -> FRoomSummary가 리스폰스로 옴
룸 리스트업 -> FRoomDetails가 리스폰스로 옴
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnListRoomsCompleted, const TArray<FRoomSummary>&, Rooms);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnJoinRoomCompleted, const TArray<FRoomDetails>&, RoomDetails);
리스트업, 조인 델리게이트
void UDDHttpApiSubsystem::SendCreateRoomRequest(const FString& RoomName)
{
FString Url = TEXT("http://localhost:8080/api/MatchRooms");
TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject());
JsonObject->SetStringField(TEXT("roomName"), RoomName);
JsonObject->SetStringField(TEXT("ipAddress"), IP);
JsonObject->SetStringField(TEXT("port"), Port);
FString OutputString;
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&OutputString);
FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer);
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = FHttpModule::Get().CreateRequest();
Request->SetURL(Url);
Request->SetVerb(TEXT("POST"));
Request->SetHeader("Content-Type", "application/json");
UE_LOG(LogDD, Log, TEXT("Token: %s"), *GetGameInstance()->GetSubsystem<UDDUserAuthSubsystem>()->GetToken());
FString RawToken = GetGameInstance()->GetSubsystem<UDDUserAuthSubsystem>()->GetToken();
FString Bearer = FString::Printf(TEXT("Bearer %s"), *RawToken);
Request->SetHeader("Authorization", Bearer);
Request->SetContentAsString(OutputString);
Request->OnProcessRequestComplete().BindUObject(this, &UDDHttpApiSubsystem::OnCreateRoomResponseReceived);
Request->ProcessRequest();
}
void UDDHttpApiSubsystem::OnCreateRoomResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
FResponseStruct ResponseStruct;
ResponseStruct.bWasSuccessful = bWasSuccessful;
if (bWasSuccessful && Response.IsValid())
{
int32 ResponseCode = Response->GetResponseCode();
FString ResponseContent = Response->GetContentAsString();
ResponseStruct.ResponseCode = ResponseCode;
ResponseStruct.ResponseContent = ResponseContent;
UE_LOG(LogDD, Log, TEXT("ResponseCode: %d"), ResponseCode);
UE_LOG(LogDD, Log, TEXT("ResponseContent: %s"), *ResponseContent);
GetWorld()->ServerTravel(TEXT("/Game/02_Level/L_Map_Game?game=/Game/01_Blueprint/Game/BP_DDGameMode.BP_DDGameMode_C?listen"));
}
else
{
FString Error = Response.IsValid() ? Response->GetContentAsString() : TEXT("Invalid Response");
ResponseStruct.ErrorContent = Error;
UE_LOG(LogDD, Error, TEXT("Request Failed: %s"), *Error);
}
// UnBind는 바인딩하는 쪽에서 알아서
OnRequestCompleted.Broadcast(ResponseStruct);
}
룸 생성
일단 테스트용으로 룸 생성 성공하면 바로 게임룸으로 이동하는데, 추후 대기방으로 변경예정
룸 이름 입력하면 그에 맞는 입력을 서버로 보냄
void UDDHttpApiSubsystem::SendListRoomsRequest()
{
FString Url = TEXT("http://localhost:8080/api/MatchRooms");
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = FHttpModule::Get().CreateRequest();
Request->SetURL(Url);
Request->SetVerb(TEXT("GET"));
Request->SetHeader("Content-Type", "application/json");
FString RawToken = GetGameInstance()->GetSubsystem<UDDUserAuthSubsystem>()->GetToken();
FString Bearer = FString::Printf(TEXT("Bearer %s"), *RawToken);
Request->SetHeader("Authorization", Bearer);
Request->OnProcessRequestComplete().BindUObject(this, &UDDHttpApiSubsystem::OnListRoomsResponseReceived);
Request->ProcessRequest();
}
void UDDHttpApiSubsystem::OnListRoomsResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
TArray<FRoomSummary> RoomSummaries;
if (bWasSuccessful && Response.IsValid() && Response->GetResponseCode() == 200)
{
UE_LOG(LogDD, Log, TEXT("%s"), *Response->GetContentAsString());
FString JsonString = Response->GetContentAsString();
TSharedPtr<FJsonValue> JsonValue;
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);
if ( FJsonSerializer::Deserialize(Reader, JsonValue) && JsonValue.IsValid() && JsonValue->AsArray().Num() > 0)
{
const TArray<TSharedPtr<FJsonValue>>& Arr = JsonValue->AsArray();
for (auto& Entry : Arr)
{
const TSharedPtr<FJsonObject>& Obj = Entry->AsObject();
if (!Obj.IsValid()) return;
FRoomSummary Info;
Info.RoomId = Obj->GetStringField(TEXT("roomId"));
Info.RoomName = Obj->GetStringField(TEXT("roomName"));
Info.HostUserName = Obj->GetStringField(TEXT("hostUsername"));
Info.CurrentPlayerCount = Obj->GetIntegerField(TEXT("currentPlayerCount"));
Info.MaxPlayers = Obj->GetIntegerField(TEXT("maxPlayers"));
Info.bIsGameStarted = Obj->GetBoolField(TEXT("gameStarted"));
RoomSummaries.Emplace(Info);
}
}
}
else
{
UE_LOG(LogDD, Log, TEXT("List Rooms Failed: %s"), *Response->GetContentAsString());
}
OnListRoomsCompleted.Broadcast(RoomSummaries);
}
룸 리스트업
룸 리스트를 얻어낸다
OnListRoomsCompleted에 FRoomSummary 배열을 인자로 브로드캐스트
void UDDHttpApiSubsystem::SendJoinRoomRequest(const FString& RoomID, const FString& InIPAddress, int32 InPort)
{
FString Url = FString::Printf(TEXT("http://localhost:8080/api/MatchRooms/%s/join"), *RoomID);
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = FHttpModule::Get().CreateRequest();
Request->SetURL(Url);
Request->SetVerb(TEXT("POST"));
Request->SetHeader("Content-Type", "application/json");
FString RawToken = GetGameInstance()->GetSubsystem<UDDUserAuthSubsystem>()->GetToken();
FString Bearer = FString::Printf(TEXT("Bearer %s"), *RawToken);
Request->SetHeader("Authorization", Bearer);
TSharedPtr<FJsonObject> Json = MakeShareable(new FJsonObject());
Json->SetStringField(TEXT("ipAddress"), InIPAddress);
Json->SetNumberField(TEXT("port"), InPort);
FString Body;
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&Body);
FJsonSerializer::Serialize(Json.ToSharedRef(), Writer);
Request->SetContentAsString(Body);
Request->OnProcessRequestComplete().BindUObject(this, &UDDHttpApiSubsystem::OnJoinRoomResponseReceived);
Request->ProcessRequest();
}
void UDDHttpApiSubsystem::OnJoinRoomResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
FRoomDetails RoomDetails;
if ( bWasSuccessful && Response.IsValid() && Response->GetResponseCode() == 200 )
{
FString JsonString = Response->GetContentAsString();
TSharedPtr<FJsonObject> RoomJson;
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);
if ( FJsonSerializer::Deserialize(Reader, RoomJson) && RoomJson.IsValid() )
{
RoomDetails.RoomId = RoomJson->GetStringField(TEXT("roomId"));
RoomDetails.RoomName = RoomJson->GetStringField(TEXT("roomName"));
RoomDetails.HostUserName = RoomJson->GetStringField(TEXT("hostUsername"));
RoomDetails.HostIPAddress = RoomJson->GetStringField(TEXT("hostIpAddress"));
RoomDetails.HostPort = RoomJson->GetIntegerField(TEXT("hostPort"));
RoomDetails.MaxPlayers = RoomJson->GetIntegerField(TEXT("maxPlayers"));
RoomDetails.bIsGameStarted = RoomJson->GetBoolField(TEXT("gameStarted"));
const TArray<TSharedPtr<FJsonValue>>& Arr = RoomJson->GetArrayField(TEXT("players"));
for ( auto& Player : Arr )
{
RoomDetails.Players.Emplace(Player->AsString());
}
TSharedPtr<FJsonObject> IPObjs = RoomJson->GetObjectField(TEXT("playerIps"));
for (auto& IPPair : IPObjs->Values)
{
RoomDetails.PlayerIPs.Emplace(IPPair.Key, IPPair.Value->AsString());
}
FString Path = FString::Printf(TEXT("%s:%d"), *RoomDetails.HostIPAddress, RoomDetails.HostPort);
UE_LOG(LogDD, Log, TEXT("Path: %s"), *Path);
GetWorld()->GetFirstPlayerController()->ClientTravel(Path, TRAVEL_Absolute);
}
}
else
{
UE_LOG(LogDD, Log, TEXT("Join Room Failed"));
}
}
룸 이름과 플레이어 ID, Port를 보내면 성공 시 ClientTravel로 리슨 서버에 참여
아직 UI는 제대로 안만들고 Find하면 바로 첫번째 방에 Join되게 만들어놔서 이제 그거 수정 필요
'언리얼 엔진 > Drag Down' 카테고리의 다른 글
[Drag Down] 플레이어 UI 기획 (각 플레이어 Z축 위치, 이름 표기) (0) | 2025.05.12 |
---|---|
[Drag Down] 매치메이킹 UI (0) | 2025.05.12 |
[Drag Down] 메시 변경 UI, USceneCaptureComponent2D (0) | 2025.05.07 |
** [Drag Down] 스켈레탈 메시 병합 및 붙이기 (Skeletal Mesh Merging & Attach) ** (0) | 2025.05.03 |
[Drag Down] 데이터 애셋 기반 메시 병합 설계 (0) | 2025.05.03 |