최근 새로 설치한 팀시티 빌드 에이전트에서 언리얼 엔진 소스를 체크아웃받아 놓은 폴더가 있었는데, 어느 날 확인 해보니 사라져 있었다.

엔진은 리빌드를 하면 너무 오래 걸리기 때문에 수정된 내용만 컴파일되도록 가급적 건드리지 않는 게 좋은데, 이렇게 사라져 버리면 전체 빌드를 하느라 시간이 많이 걸린다. 

원인을 파악해 보니 예전에도 본 것 같은 기능이 있었는데, 팀시티는 여러가지 이유로 체크아웃 폴더를 삭제하고 다시 받도록 할 수 있는데, VCS 설정이 바뀌거나 디스크 용량이 부족해서 삭제를 하는 것은 당연하지만 아무것도 안 했는데 삭제를 하는 것은 기간만료 기능 때문이다. 약 8일 정도 사용을 안 하면 자동으로 삭제를 한다고 한다.

팀시티 문서 링크: https://www.jetbrains.com/help/teamcity/2024.07/build-checkout-directory.html#Checkout+Directory+Expiration

 

Build Checkout Directory | TeamCity On-Premises

 

www.jetbrains.com

 

해당 기능을 끄려면 문서에도 나와 있듯이 두가지 방법 중 하나를 사용하면 된다.

빌드 에이전트 머신으로 들어가서 buildAgent.properties 파일을 찾아서 다음의 속성을 추가한다.

teamcity.agent.build.checkoutDir.expireHours=never

이 방법은 에이전트 머신마다 설정하는 방법이고 해당 에이전트에서 빌드하는 모든 프로젝트에 적용된다.

 

혹은, 팀시티 프로젝트에서 다음의 속성을 추가한다. (이 방법 추천)

system.teamcity.build.checkoutDir.expireHours

이 방법이 더 나은 거 같다. 원하는 프로젝트나 설정에만 적용할 수 있기 때문이다. 물론 전체 적용을 원한다면 Root project에 한 번만 설정하면 된다.

'개발 > 팀시티' 카테고리의 다른 글

팀시티 2018 설치하기  (0) 2019.03.12
팀시티 2018에서 사용할 MySQL 설치하기  (0) 2019.03.12

언리얼 엔진 최신 버전(5.4.2)을 비주얼 스튜디오 최신 버전 (2022)으로 컴파일하려고 하니 다음과 같은 경고가 뜬다.

Visual Studio 2022 compiler version 14.40.33811 is not a preferred version. Please use the latest preferred version 14.38.33130

 

그리고, 이상한 곳에서 컴파일 에러도 뜬다.

...Engine\Source\Runtime\Core\Public\Templates\SharedPointer.h(1092) : error C4702: unreachable code

 

검색을 해 보니, 언리얼 엔진 최신 버전과 비주얼 스튜디오 최신 버전간 호환성 문제가 있다고 한다.

비주얼 스튜디오 인스톨러에서 14.38에 해당되는 툴체인을 설치하면 해결된다.

정확히 뭐가 필요한 지 몰라서 14.38로 검색되는 걸 일단 다 설치해 보니 문제가 해결되었다.

일반적으로 많이 사용하는 Actor나 ActorComponent가 아니라, 간혹 Object를 바로 상속받거나 LocalPlayerSubsystem 같은 클래스를 상속받은 BP 클래스를 만들어서 사용해야 할 때가 있다. 그런데, 그렇게 상속받은 클래스에서 BlueprintFunctionLibrary에 정의된 함수를 호출하려고 하면 잘 안된다. 에디터에서 마우스 오른쪽을 눌러 다른 BP 함수 라이브러리의 함수를 찾아도 아예 보이지가 않는다.

 

정확한 원인은 잘 모르겠지만, BlueprintFunctionLibrary 함수를 호출할 때는 호출하려는 클래스의 GetWorld 함수 접근이 필요한데, Actor나 ActorComponent의 경우 해당 함수가 에디터에서도 접근할 수 있게 노출되어 있지만, UObject나 Subsystem 계통의 클래스들은 그게 안되어 있어서 생긴 문제로 보인다.

 

그래서, BP 클래스를 만들기 전에 부모 클래스를 c++로 만들고, Blueprintable 속성을 넣어주고, GetWorld 함수를 오버라이드하면 해결이 된다.

// MyGameSoundSubsystem.h
UCLASS(Blueprintable)
class MYGAME_API UMyGameSoundSubsystem : public ULocalPlayerSubsystem
{
    GENERATED_BODY()

public:
    virtual UWorld* GetWorld() const override;
//...
};
  
  
// MyGameSoundSubsystem.cpp
UWorld* UMyGameSoundSubsystem::GetWorld() const
{
#if WITH_EDITOR
    if (IsTemplate())
    {
        return nullptr;
    }
#endif

    return Super::GetWorld();
}

가장 간단한 방법은 위의 코드처럼 cpp 함수의 구현부에서 그냥 부모 함수만 호출해도 되긴 하는데, 에디터에서는 뭔가 또 다르게 작동하는 게 있어서 예외처리는 필요하다.

 

항상 위의 코드처럼 작성해야 하는 건 아니고 경우에 따라서는 부모 클래스나 Outer 클래스를 참조해야 할 필요도 있고, WorldContext를 명시적으로 노출해서 사용해야 하는 경우도 있다.

 

코드 출처: https://forums.unrealengine.com/t/can-you-use-a-blueprint-function-library-in-an-object-class/350918/31

 

ps. 일반적으로 Actor나 ActorComponent는 현재 맵(Level)의 월드에 귀속되지만, UObject를 상속받은 클래스 혹은 Subsystem 클래스들은 싱글턴처럼 쓰이는 경우가 많고, 현재 월드가 아닌 다른 월드(엔진 기본 월드?)에 귀속되어 있을 수 있다.

UGameInstanceSubsystem을 상속받은 c++ 클래스는 싱글턴처럼 하나만 생성되는 게 원칙이다.

UCLASS()
class MYGAME_API UMySubsystem : public UGameInstanceSubsystem
{
	GENERATED_BODY()
	...
};

 

서브 시스템을 가져다 쓸 때는 클래스 타입으로 검색해서 사용한다.

GetGameInstance()->GetSubsystem<UMySubsystem>()

 

그런데, 만약 c++ 클래스를 상속 받은 블루프린트 클래스를 만들어서 그걸 사용하고 싶다면 어떻게 해야 하는지 궁금해졌다. 검색해서 알아낸 방법은 다음과 같다.

 

먼저, c++의 서브시스템을 블루프린트에서 상속 받을 수 있게 Blueprintable 속성을 추가하고, ShouldCreateSubsystem 함수를 오버라이드해서 c++ 클래스의 생성은 막고 블루프린트 클래스만 생성할 수 있게 한다.

// MySubsystem.h
UCLASS(Blueprintable)
class MYGAME_API UMySubsystem : public UGameInstanceSubsystem
{
	GENERATED_BODY()
	
public:
	virtual bool ShouldCreateSubsystem(UObject* Outer) const override;
    ...
};

// MySubsystem.cpp
bool UMySubsystem::ShouldCreateSubsystem(UObject* Outer) const
{
    TArray<UClass*> ChildClasses;
    GetDerivedClasses(GetClass(), ChildClasses, false);
    return (ChildClasses.Num() == 0); // 최종 자식 클래스만 생성
}

 

에디터를 실행해서 MySubsystem을 상속 받는 BP_MySubsystem이라는 블루프린트 클래스를 생성한다.

 

일단 이렇게만 해도 에디터 플레이에서는 잘 작동한다.

 

하지만, 패키징 단계에서 해당 블루프린트 클래스에 대한 참조가 없으면 누락될 수 있으니, GameInstance에서 클래스 참조를 넣어 두자.

UCLASS(BlueprintType, Blueprintable)
class MYGAME_API UMyGameInstance : public UGameInstance
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere, BlueprintReadOnly) 
	TSubclassOf<UMySubsystem> MySubsystemClass;

변수 선언은 위와 같이 하고, UMyGameInstance를 상속받은 블루프린트 클래스(예: BP_MyGameInstance)에서 MySubsystemClass 변수값에 BP_MySubsystem를 지정해 두도록 하자.

TortoiseSVN에서 잘못된 메시지로 커밋을 했을 때, Log에서 마우스 오른쪽 버튼을 누르면 로그 메시지 편집 기능이 있다.

하지만, 해당 메뉴를 클릭하면 관리자에게 문의하라는 에러가 뜬다.

While handling the 'svn:log' property on '/svn/MyProject/!svn/bln/999':
Repository has not been enabled to accept revision propchanges;
ask the administrator to create a pre-revprop-change hook

 

단발성 수정 방법

다음의 콘솔 명령어를 이용하면 해당 로그의 메시지만 수정할 수 있다.

svn propedit -r {리비전번호} --revprop svn:log {SVN 저장소 URL}

다만, 이 명령어는 관리자 권한이 있는 유저만 사용할 수 있다.

 

항상 허용하는 방법

관리자가 한번 설정하면 이후 모든 유저가 로그 메시지를 편집할 수 있게 허용하는 방법이다.

 

VisualSVN 서버 관리 툴을 연다. 저장소를 선택하고, 마우스 오른쪽 버튼을 눌러 속성창을 연다.

Hooks 탭에서 Pre-revision property change hook을 선택하고 하단의 Edit 버튼을 누른다.

창이 열리면 다음의 내용을 붙여 넣고 저장한다.

@ECHO OFF
:: Set all parameters. Even though most are not used, in case you want to add
:: changes that allow, for example, editing of the author or addition of log messages.
set repository=%1
set revision=%2
set userName=%3
set propertyName=%4
set action=%5

:: Only allow the log message to be changed, but not author, etc.
if /I not "%propertyName%" == "svn:log" goto ERROR_PROPNAME

:: Only allow modification of a log message, not addition or deletion.
if /I not "%action%" == "M" goto ERROR_ACTION

:: Make sure that the new svn:log message is not empty.
set bIsEmpty=true
for /f "tokens=*" %%g in ('find /V ""') do (
set bIsEmpty=false
)
if "%bIsEmpty%" == "true" goto ERROR_EMPTY

goto :eof

:ERROR_EMPTY
echo Empty svn:log messages are not allowed. >&2
goto ERROR_EXIT

:ERROR_PROPNAME
echo Only changes to svn:log messages are allowed. >&2
goto ERROR_EXIT

:ERROR_ACTION
echo Only modifications to svn:log revision properties are allowed. >&2
goto ERROR_EXIT

:ERROR_EXIT
exit /b 1

Hook 스크립트를 입력하고, OK를 눌러 속성창을 닫으면 된다.

 

참고#1: https://stackoverflow.com/questions/304383/how-to-edit-log-message-already-committed-in-subversion

 

How to edit log message already committed in Subversion?

Is there a way to edit the log message of a certain revision in Subversion? I accidentally wrote the wrong filename in my commit message which could be confusing later. I've seen How do I edit an

stackoverflow.com

참고#2: https://stackoverflow.com/questions/197224/what-is-a-pre-revprop-change-hook-in-svn-and-how-do-i-create-it

 

What is a pre-revprop-change hook in SVN, and how do I create it?

I wanted to edit a log comment in the repository browser and received an error message that no pre-revprop-change hook exists for the repository. Besides having a scary name, what is a pre-revprop-...

stackoverflow.com

 

윈도우즈 운영체제에서는 윈도우 애플리케이션이 정상 작동 중인지 확인하기 위해서, 지속적으로 윈도우 메시지를 해당 애플리케이션으로 보내는데, 만약 일정 시간이 지나도 응답이 없으면, 해당 애플리케이션이 응답이 없으니 (강제로) 종료하겠느냐는 팝업을 띄운다. 그런데, 게임 등의 애플리케이션을 개발하다 보면 로딩해야 될 리소스가 많아서 시간이 오래 걸릴 경우가 있는데, 그렇게 로딩하는 와중에 이 팝업이 뜨면 유저는 해당 애플리케이션에 문제가 있다고 생각하고 강제 종료를 하게 될 수 있다. 해결 방법은 크게 두 가지이다.

 

1. 로딩 처리를 시간이 많이 드는 부분, 특히 파일 로딩을 비동기로 처리해서 메인 스레드에서 윈도우 메시지 처리가 끊기지 않게 하기.

 

2. 응답 없음 처리(고스팅 처리) 기능을 그냥 끄는 방법

 

1번이 바람직한 방법이긴 하지만, 프로그램 구조를 바꿔야 하고, 바꾸는 와중에 버그가 발생하기도 하고, 암튼 좀 어려울 수 있다. 2번 방식은 그냥 다음의 함수를 한번 호출해 주면 된다.

DisableProcessWindowsGhosting();

링크: https://learn.microsoft.com/ko-kr/windows/win32/api/winuser/nf-winuser-disableprocesswindowsghosting

 

DisableProcessWindowsGhosting 함수(winuser.h) - Win32 apps

호출 GUI 프로세스에 대한 창 고스팅 기능을 사용하지 않도록 설정합니다. 창 고스팅은 사용자가 응답하지 않는 애플리케이션의 주 창을 최소화, 이동 또는 닫을 수 있는 Windows 관리자 기능입니

learn.microsoft.com

참고로, 해당 기능을 다시 켜는 함수는 없다.

UE4Game.Target.cs 파일에 다음의 코드를 추가한다.

if (Target.Configuration == UnrealTargetConfiguration.Shipping || Target.Configuration == UnrealTargetConfiguration.Test)
{
  bAllowGeneratedIniWhenCooked = false;
  bAllowNonUFSIniWhenCooked = false;
}

엔진을 빌드한다.

이후, 게임 프로젝트의 *.Target.cs 파일에도 동일한 코드를 넣는다.

 

위의 코드를 넣어주면, 게임 구동시 [게임명]/Saved/Config/[빌드설정]/Config 폴더에 ini 파일을 쓰지도 않고, 읽지도 않는다. 단, GameUserSettings.ini 파일은 여전히 읽고 쓴다.

 

[부작용]

게임 옵션에서 인풋액션 설정을 Input.ini 파일을 통해서 저장하고 있었다면, 해당 기능을 더 이상 쓸 수 없다.

그런 경우, GameUserSettings.ini 파일을 이용하도록 코드를 수정하면 된다.

혹은, 엔진 수정을 통해 Input.ini 파일도 예외로 추가할 수도 있겠다.

제목 설명 장점 단점
BASE64 바이너리 데이터를 텍스트 포맷으로 변경해 줌. 이메일이나 URL 표시등에 사용. 텍스트 포맷이라 다루기 편함 데이터 사이즈가 늘어남. 2배.
SHA256 단방향 해쉬 알고리즘. 해쉬 키 길이는 32바이트(256비트). 짧은 데이터는 늘어나고, 긴 데이터는 줄어든다. MD5와 유사. 원본 데이터 보관은 어려우나 검증이 필요할 때 좋음. 즉, 비밀번호 검증 등에 효과적. 원본 데이터 복원이 안됨.
AES256 암복호화 알고리즘. 패킷 등의 데이터를 암호화 해서 주고 받을 때 좋음. 32바이트 단위로 처리하므로 패딩이 필요할 수 있음. 즉, 원본 데이터의 사이즈는 따로 보내야 함. 패딩이 없다면, 암호화 한 데이터의 사이즈가 늘어나지 않음. 작은 사이즈의 데이터를 보낼 땐 패딩 사이즈만큼 부하가 커짐.
RSA 개인키 공개키를 이용한 암호화 알고리즘. 강력한 보안이 필요할 때 사용함. 보안 처리가 강력함. 중요한 데이터(예: AES에 사용할 키)를 전송하는 데 사용하면 좋음. 원본 데이터 보다 암호화한 데이터가 훨씬 큼. 사용 방법이 까다로움. 

 

몬티홀 딜레마는 미국에서 약 40여 년간 방영한 미국의 TV쇼에서 유래한 확률 문제임.

사회자의 이름인 몬티 홀(Monty Hall)의 이름을 따서 몬티홀 딜레마 혹은 몬티홀 문제(Monty Hall Problem)이라고 불림.

 

A, B, C 세 개 문 중 하나의 문 뒤에 자동차가 있고, 나머지 문 뒤에는 염소가 있다.

참가자가 하나를 고르면, 정답을 아는 사회자가 나머지 두 개의 문 중 염소가 있는 문을 하나 연다.

이후 참가자에게 답을 바꿀 기회를 준다. 이때, 답을 바꾸는 게 좋을까 아니면 그대로 있는 게 좋을까?

 

얼핏 보면 최종적으로는 2개 중 하나이니까 50% 확률이라고 생각하기 쉽다.

하지만, 처음에 정답을 골랐을 확률은 1/3이고, 오답이 제거된 상황에서 처음 답을 유지하면 여전히 1/3 확률로 정답이지만, 선택을 바꿀 경우 1 - 1/3 = 2/3로 정답을 맞힐 확률이 올라간다.

 

좀 더 이해하기 쉽게 처음에 선택지가 10개가 있었다고 가정하자.

처음에 정답을 맞힐 확률은 10%밖에 안된다. 이후 사회자가 나머지 8개의 오답을 모두 제거하고 이제 딱 2개만 남았다고 생각해보자. 이런 경우엔 당연히 본인이 처음에 선택한 문보다는 사회자가 열지 않은 마지막 남은 문에 자연스럽게 눈길이 가게 될 것이다. 사회자가 남긴 문이 정답일 확률이 90%인 것이다.

 

이 문제를 코드로 작성해서 확인해 보자.

// Online C# Editor for free
// Write, Edit and Run your C# code using C# Online Compiler

using System;

public class HelloWorld
{
    static Random rand = new Random();
    
    static float TestMontyHall(int totalItemCount, bool changeFirstSelection, int testCount)
    {
        int successCount = 0;

        for (int i = 0; i < testCount; ++i)
        {
            int correctIndex = rand.Next(totalItemCount);
            int firstAnswerIndex = rand.Next(totalItemCount);
            
            // 처음 선택을 바꾼 경우
            if (changeFirstSelection)
            {
                // 첫번째 선택이 답이 아닌라면, 바꾼 후에 성공!
                if (correctIndex != firstAnswerIndex)
                    ++successCount;
            }
            else // 선택을 바꾸지 않은 경우
            {
                // 첫번째 선택이 정답인 경우에만, 성공
                if (correctIndex == firstAnswerIndex)
                    ++successCount;
            }
        }
        
        return (float)successCount / testCount;
    }
    
    public static void Main(string[] args)
    {
        Console.WriteLine ("Hello Monty Hall Problem World");
        
        Console.WriteLine ("If you change the first selection, success ratio is " + TestMontyHall(3, true, 10000));

        Console.WriteLine ("If you don't change the first selection, success ratio is " + TestMontyHall(3, false, 10000));
    }
}

결과는 아래와 같다.

Hello Monty Hall Problem World
If you change the first selection, success ratio is 0.664
If you don't change the first selection, success ratio is 0.3301

몬티홀 문제를 풀게 될 경우가 생긴다면, 처음 선택을 고집하지 말고 답을 바꾸자!

잡을 복사했는데 "빌드" 버튼이 보이지 않는다.

이럴 경우 "구성"으로 들어가서..

"저장"을 한번 해 주면 된다.

끝.

+ Recent posts