TIL

3월 13일자 TIL

오딘.L.스트레인지 2026. 3. 13. 19:54

코드카타-둘만의 암호

#include <string>
#include <vector>
using namespace std;

// 1) skip 여부를 O(1)로 확인하기 위한 테이블 만들기
static vector<bool> BuildSkipTable(const string& skip) {
    vector<bool> isSkip(26, false);
    for (char ch : skip) {
        isSkip[ch - 'a'] = true;
    }
    return isSkip;
}

// 2) 문자 1개를 규칙대로 변환하는 함수
static char ShiftChar(char c, int index, const vector<bool>& isSkip) {
    int moved = 0;

    while (moved < index) {
        if (c == 'z') c = 'a';
        else c = c + 1;

        if (isSkip[c - 'a']) {
        continue; // skip이면 카운트하지 않음
        }
        moved++; // skip이 아닐 때만 카운트
    }

    return c;
}

string solution(string s, string skip, int index) {
    vector<bool> isSkip = BuildSkipTable(skip);

    for (int i = 0; i < (int)s.size(); i++) {
        s[i] = ShiftChar(s[i], index, isSkip);
    }

    return s;
}

 

 

 

현재 채팅 숫자야구 게임 진행도

2-1강과 2-2강을 해봤는데 아직까진 논리 구조가 명확하게 안 떠올라서 조금 더 머리가 깨져야 한다.

더보기

RPC(Remote Procedure Call)

간단히 말해, “호출하는 PC와 실행하는 PC가 달라도 되게끔 해주는 통신 기법”이라고 할 수 있다.

언리얼에선 게임에 큰 영향을 끼치지 않는 일시적인 효과들에, 주로 코스메틱(사운드, 파티클 등)에 사용한다.

Call과 Invoke

Call

  • 정적인 의미를 가진다.
  • 컴파일 타임에 어떤 함수인지, 호출하는 곳과 실행하는 곳이 정해져야 한다.
  • 지금까지 정의했던 일반적인 전역 함수와 멤버 함수가 이에 해당한다. 즉, 직접적으로 함수를 호출하고 실행해야 한다.

Invoke

  • 동적인 의미를 가진다.
  • 런타임에 어떤 함수인지, 호출하는 곳과 실행하는 곳이 어딘지 정해진다.
  • 예를 들어, 함수 포인터, 동적 바인딩, RPC 같은 개념들이 이에 해당한다.
  • 즉, 간접적으로 함수를 호출하고 실행해야 한다. RPC도 마찬가지로 "RPC를 Invoke"라고 표현한다.

Actor Ownership

네트워크 멀티플레이가 적용되려면, 액터는 서버에서 스폰되고 bReplicated가 true여야 한다.

서버에서 스폰된 후 SetOwner(PlayerController) 함수를 호출해야 Client-Owned Actor가 된다.

📌PlayerController가 Local PlayerController와 같다면, Client-Owned Actor 이다.

  • 예: 내 플레이어 캐릭터

📌PlayerController가 Local PlayerController와 다르다면, Owned by different client 이다.

  • 예: 내 화면에 보이는 친구의 플레이어 캐릭터

📌서버에서 스폰되었지만 SetOwner() 함수 호출이 없으면, Server-Owned Actor 이다.

  • 예: 보물상자 액터

NetMulticast, Server, Client 키워드

UFUNCTION() 매크로와 함께 사용되는 키워드들이다.

키워드는 “해당 원격 PC에서 RPC를 실행시켜 달라는 요청”을 뜻한다.

실제로 실행될지는 표를 통해 판단해야 한다.

NetMulticast

  • “서버를 포함한 모든 클라이언트에서 해당 RPC를 실행시켜주세요”라는 요청이다.

Server

  • “서버에서 해당 RPC를 실행시켜주세요.”라는 요청이다.

Client

  • “클라이언트에서 해당 RPC를 실행시켜주세요.”라는 요청이다.

자주 쓰이는 케이스

RPC가 클라이언트에서 호출되고 서버에서 실행되어야 하는 경우에는 Server 키워드를 사용해야 한다.

  • ClientConnection이 소유하고 있는 액터(Client-Owned)에서 RPC가 호출되어야 한다.
  • _Validate() 함수에서 RPC를 실행할지 말지 결정한다.

RPC가 서버와 모든 클라이언트에서 실행되어야 하는 경우에는 NetMulticast 키워드를 사용해야 한다.

  • 서버에서 호출해야 한다.
  • 부하가 심하므로, 빈번하게 호출되게끔 코드를 작성하는 건 권장되지 않는다. ex) Tick() 함수 내에서 NetMulticast RPC 호출.

RPC가 서버에서 호출되고 클라이언트에서 실행되어야 하는 경우에는 Client 키워드를 사용해야 한다.

  • 서버에서 호출해야 한다.

WithValidation

📚UFUNCTION() 매크로와 함께 사용되며, 서버에서 실행되는 RPC의 경우에 해당 키워드를 작성하는 것이 권장된다.

이 키워드가 붙은 RPC를 구현 할때는 _Implementation() 함수와 _Validate() 함수로 나뉘어 진다.

_Validate() 함수는 해당 RPC가 서버에서 실제로 실행할지 말지 결정한다.

서버 실행 로직은 무조건적으로 신뢰되기 때문에, 위변조를 막기 위한 방어막 역할을 한다.

Unreliable VS Reliable

📚UFUNCTION() 매크로와 함께 사용되는 키워드이다.

RPC는 기본적으로 Unreliable 키워드를 기준으로 동작한다. 아무것도 안쓰면 Unreliable이다.

Unreliable은 원격 PC에서 무조건 실행되리라는 보장이 없다.

반면, 원격 PC에서 무조건 실행되어야 하는 로직이라면 Reliable 키워드를 작성한다.

📌Unreliable의 활용 예시

  • 코스메틱(이펙트, 사운드 등)처럼 게임에 큰 영향이 없는 로직.

📌Reliable의 활용 예시

  • 충돌, 데미지, 스폰처럼 게임에 큰 영향을 끼치는 로직.

멀티플레이 채팅 구현

더보기
// CXPlayerController.h

...
class CHATX_API ACXPlayerController : public APlayerController
{
	...

public:
	...

	UFUNCTION(Client, Reliable)
	void ClientRPCPrintChatMessageString(const FString& InChatMessageString);

	UFUNCTION(Server, Reliable)
	void ServerRPCPrintChatMessageString(const FString& InChatMessageString);
	
protected:
	...
	
};
// CXPlayerController.cpp


...
#include "EngineUtils.h"

...

void ACXPlayerController::SetChatMessageString(const FString& InChatMessageString)
{
	ChatMessageString = InChatMessageString;

	//PrintChatMessageString(InChatMessageString);
	if (IsLocalController() == true)
	{
		ServerRPCPrintChatMessageString(InChatMessageString);		
	}
}

...

void ACXPlayerController::ClientRPCPrintChatMessageString_Implementation(const FString& InChatMessageString)
{
	PrintChatMessageString(InChatMessageString);
}

void ACXPlayerController::ServerRPCPrintChatMessageString_Implementation(const FString& InChatMessageString)
{
	for (TActorIterator<ACXPlayerController> It(GetWorld()); It; ++It)
	{
		ACXPlayerController* CXPlayerController = *It;
		if (IsValid(CXPlayerController) == true)
		{
			CXPlayerController->ClientRPCPrintChatMessageString(InChatMessageString);
		}
	}
}

NetMulticast RPC를 통한 서버 접속 알리기

더보기
// CXGameStateBase.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameStateBase.h"
#include "CXGameStateBase.generated.h"

/**
 * 
 */
UCLASS()
class CHATX_API ACXGameStateBase : public AGameStateBase
{
	GENERATED_BODY()

public:
	UFUNCTION(NetMulticast, Reliable)
	void MulticastRPCBroadcastLoginMessage(const FString& InNameString = FString(TEXT("XXXXXXX")));
	
};
// CXGameStateBase.cpp


#include "CXGameStateBase.h"

#include "Kismet/GameplayStatics.h"
#include "Player/CXPlayerController.h"

void ACXGameStateBase::MulticastRPCBroadcastLoginMessage_Implementation(const FString& InNameString)
{
	if (HasAuthority() == false)
	{
		APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0);
		if (IsValid(PC) == true)
		{
			ACXPlayerController* CXPC = Cast<ACXPlayerController>(PC);
			if (IsValid(CXPC) == true)
			{
				FString NotificationString = InNameString + TEXT(" has joined the game.");
				CXPC->PrintChatMessageString(NotificationString);
			}
		}
	}
}
// CXGameModeBase.h

...
class CHATX_API ACXGameModeBase : public AGameModeBase
{
	GENERATED_BODY()

public:
	virtual void OnPostLogin(AController* NewPlayer) override;	
	
};
// CXGameModeBase.cpp


#include "Game/CXGameModeBase.h"

#include "CXGameStateBase.h"

void ACXGameModeBase::OnPostLogin(AController* NewPlayer)
{
	Super::OnPostLogin(NewPlayer);

	ACXGameStateBase* CXGameStateBase =  GetGameState<ACXGameStateBase>();
	if (IsValid(CXGameStateBase) == true)
	{
		CXGameStateBase->MulticastRPCBroadcastLoginMessage(TEXT("XXXXXXX"));
	}
}

그 다음 BP_GameModeBase > Details > Game State Class에 BP_GameStateBase 지정해 준다.

 

 

'TIL' 카테고리의 다른 글

3월 17일자 TIL  (0) 2026.03.17
3/16일자 TIL  (0) 2026.03.16
3/12일자 TIL  (0) 2026.03.12
3/11일자 TIL  (0) 2026.03.11
3/10일자 TIL  (0) 2026.03.10