강좌/Minecraft Mod 제작2015. 10. 6. 20:08

강의 환경 : forge-1.7.10-10.13.0.1160-1.7.10

강의 목표 :

1. 모드의 기반 클래스를 작성할 있다.

2. Mod Name, ID, Version 같은 모드 정보를 이해한다.

3. PreInit, Init, PostInit이벤트 메소드를 이용할 있다.

 

이 강좌에서는 모드의 기반이 되는 클래스를 만들어 보도록 하겠습니다.

 

우선, 프로젝트를 열고 Package Explorer 탭을 봐 주세요.

 

src/main/java 디렉토리를 펼쳐주시면, 다음 사진과 같이 examplemod가 나올 겁니다.

 

 

저 ExampleMod는 일종의 예시 모드인데,

 

기본적인 모드는 어떻게 구성되어야 하는지를 보여주고 있습니다.

 

이 모드는 아무 일도 하지 않습니다. 오직 모드의 기반만 포함하고 있는 예제이기 때문이죠.

    

그렇기에 이번 강좌의 목적에 잘 부합하기도 합니다.

 

우선 이 모드를 구성하는 유일한 소스 파일인 ExampleMod.java를 살펴보면서,

 

모드를 어떻게 구성해 나가야 하는지 살펴보도록 합시다.

 

 

위 사진은 ExampleMod.java를 열었을 때 나오는 화면입니다.

 

그렇게 많은 내용이 있지는 않지만, 일반적인 자바 프로그래밍을 해오신 분들이라면 잘 이해가 가지 않는 부분이 몇 군데 있을 겁니다.

 

그리고 그 이유는 아마 포지가 @Mod나 @EventHandler와 같은 Annotation을 중요하게 많이 활용하기 때문일 것입니다.

 

(잘 모르시는 분은 다음 사진까지 넘어가셔도 됩니다)

 

일반적으로는 Annotation은 드물게 활용되는 편이며 중심적인 역할을 맡는 경우는 드물고, 그나마 주석과 비슷한 용도로만 사용되는 경우가 흔한데,

 

포지에서는 모드의 기반이 되는 클래스에는 물론 이제는 포지의 핵심이 된 이벤트 체계도 Annotation을 중심으로 구성되어 있습니다.

 

사실 저도 왜 저렇게 되었는지 이해가 잘 가지는 않습니다만..

 

아마 모드 제작자에게 인터페이스 등을 상속하지 않을 자유를 주기 위한 것이라 생각됩니다. 사실 그러한 상속 작업이 번거롭기는 하지요.

 

한편 부작용으로, 처음 모드를 제작하는 분들은 어떻게 시작을 해야 하는지 알기가 어렵고,

 

모드 버전을 업데이트할 때에도 빨간 줄의 오류만 보게 되며 실제로 어떤 부분을 바꾸어야 하는지를 알기 어렵게 됩니다.

 

사실 그런 이유로 ExampleMod.java라는 파일이 등장한 것으로 보입니다만, 여기서 사설은 그만두겠습니다.

 

 

어쨌든, 위 사진의 내용으로 다시 돌아가서 살펴보겠습니다.

 

위 코드를 분석해 보면, 중요한 몇 부분을 발견할 수 있습니다.

 

1. @Mod

이 어노테이션은 모드 (기반) 클래스를 표시해주는 역할을 하며,

 

주로 @Mod(modid = "examplemod", name="ExampleMod", version="1.0.0") 이런 식으로 작성하게 됩니다.

 

여기서 modid, name, version과 같은 인자들은 필수적으로 지정해 주어야 합니다.

이들은 다음 표와 같은 의미를 갖습니다.

인자 이름

자료형

의미 및 용법

modid

String

모드의 ID(식별자) 입니다. 모드마다 각기 달라야 합니다.

name

String

Mods창을 열면 표시되는 모드의 이름입니다.

version

String

모드의 버전입니다. 업데이트할 때마다 바꾸어주어야 합니다.

 

이 외에도 다음과 같은 인자들이 있습니다. (필수적인 것은 아닙니다)

인자 이름

자료형

의미 및 용법

dependencies

String

어떤 모드에 필요한 다른 모드들을 지정하거나, 모드들의 로딩 순서를 지정합니다.

useMetadata

boolean

true로 지정될 경우, @Mod의 modid, name, version 등의 정보 대신 mcmod.info 에 지정된 모드 정보를 사용합니다.

acceptedMinecraftVersions

String

받아들일 마인크래프트 버전을 의미합니다.

acceptableRemoteVersions

String

클라이언트가 서버에 연결했을 때 받아들일 버전의 범위를 의미합니다.

acceptableSaveVersions

String

세이브 파일 형식이 호환되는 버전의 범위를의미합니다.

bukkitPlugin

String

이 모드를 위해 로딩할 플러그인을 지정합니다. 플러그인-모드 연계에 쓰이는 것으로 보입니다.

certificateFingerprint

String

이 모드 파일에 대한 지문을 지정합니다. 이를 통해 무단 변경된 모드의 이용을 막을 수 있습니다.

modLanguage

String

언어를 지정합니다. (적절한 ILanguageAdapter만 할당해 주면, 다른 언어로 작성하는 것도 가능합니다)

canBeDeactivated

boolean

모드가 disable될 수 있는지를 의미합니다만, 현재로서는 용도가 없는 것으로 보입니다.

guiFactory

String

Config Gui에 사용되는 IGuiFactory구현 클래스의 위치를 지정합니다.

customProperty

CustomProperty

커스텀 속성을 지정할 수 있습니다.

 

 

 

2. @EventHandler

이 어노테이션은 모드 클래스로 하여금 기본적인 이벤트들을 받을 수 있도록 해줍니다.

 

위의 예를 들어 설명하자면,

 

@EventHandler

public void init(FMLInitializationEvent event)

 

자바의 기본 원리대로라면, 당연히 저 init 함수는 절대로 호출될 수 없습니다.

 

하지만 실제로는 호출됩니다. 어떻게 그럴 수 있을까요?

 

사실 모드를 로딩할 때, 포지는 이미 모드 클래스들을 조사하고 그 인스턴스도 만들어 둔 상태입니다.

 

이때 FMLInitializationEvent가 발생하면, 포지는 각각의 모드 인스턴스에 대해 저 init 함수를 호출하는 것이지요.

 

특히 저 부분은 중요한 역할을 하는데, 바로 모드의 초기화 시점에서 호출되기 때문입니다.

 

즉 처음 모드가 실행될 때의 초기화는 모두 저 init 함수에서 하게 되죠.

 

저 예제 모드에서는, 모드 로딩 시에 콘솔 창에 흙 블록의 현지화되지 않은 이름을 출력하게 됩니다.

 

물론 아무리 예제 모드를 넣고 마크를 실행시켜도, 어떤 것도 발견하기 어려울 겁니다. 콘솔 창에만 기록되니까요.

 

* 기억해 두셔야 할 점이 하나 있는데, init 함수의 이름은 마음대로 바꿔도 되지만,

@EventHandler를 달고 FMLInitializationEvent를 인자로 가져야 한다는 규칙은 엄격히 지켜야 한다는 것입니다.

 

 

모드 기반 클래스를 제작하는 데에는 저 2개면 충분하다고 보시면 됩니다.

 

이제 우리는 새로운 모드를 창조해 보도록 합시다.

 

우선 작업할 패키지를 만들어야겠죠? 저는 abr.tutorial로 정했습니다.

 

 

여기서 자신만의 패키지를 만들어 주세요, 모드의 대부분의 구조가 그 안에 자리잡게 될 것입니다.

 

이를 제작하였다면, 이제 모드 기반 클래스를 만들 차례입니다.

 

저 패키지 안에 새로운 클래스를 만들어 주세요. 여러분이 만들 모드의 이름을 쓰는 것이 좋습니다. 꼭 그럴 필요는 없습니다만-

 

그래서 저는 다음과 같이 만들었습니다.

 

 

이제 모드 기반 클래스로서 제 구실을 하도록 만들어야겠죠? 아까 보았던 예제대로 구성을 해봅시다.

 

우선 @Mod를 붙여 주세요. 그럼 다음과 같이 됩니다.

 

 

(어노테이션의 인자에는 클래스에서 public static final로 선언한 변수의 값을 사용할 수 있습니다.)

 

위와 같이 모드 클래스에 public static final로 modid, name, version을 미리 선언해 둔 후, @Mod에서 참조하는 것을 추천합니다.

 

참고로 modid는 따로 쓸 일이 많기 때문에, 클래스에 public static final로 선언해 두는 것이 정말 도움이 됩니다.

 

 

그렇다면 이제 모드의 초기 구성을 위한 초기화 부분을 넣을 차례입니다.

 

아까 예제에서는 FMLInitializationEvent 하나밖에 없었지만,

 

일반적으로는 FMLPreInitializationEvent과 FMLPostInitializationEvent를 같이 사용합니다.

 

이 둘은 각각 FMLInitializationEvent 의 전과 후에 호출되며 각기 중요한 역할을 맡게 됩니다.

 

즉 우선 각각의 모드에 대해 FMLPreInitializationEvent가 모두 호출된 후, 역시 각 모드에 대해 FMLInitializationEvent가 호출되며, 이후 FMLPostInitializationEvent가 호출됩니다.

 

- FMLPreInitializationEvent에서는 주로 모드가 추가하는 블록과 아이템 등을 포지에 등록하게 됩니다.

* 모드 컨피그도 이곳에서 제공되지만, 그렇다고 이곳에서만 접근할 수 있는 것은 아닙니다.

- FMLInitializationEvent에서는 주로 모드 기본 세팅을 합니다. 이곳에서 레시피들을 등록하게 됩니다.

- FMLPostInitializationEvent에서는 주로 모드간의 호환과 관련된 일이 일어나게 됩니다.

 

이들에 대한 자세한 설명 역시 나중에 하도록 하겠습니다.

 

일반적으로는, 아래와 같이 작성해 주시면 됩니다.

 

 

여기까지 하셨다면, 이제 포지가 제공하는 기본 예제는 전혀 필요가 없습니다. 과감히 지워주도록 합시다.

 

(혹시라도 지금 안 지웠다가 여러분의 모드를 배포할 때까지 그대로 놓아두게 된다면,

나중에 사용자가 여러분의 모드를 받았을 때 ExampleMod라는 정체불명의(?) 모드가 같이 깔려있게 되는 불상사가 생길 수도 있습니다!)

 

여기까지 따라하셨다면, 이제 여러분은 모드 (기반) 클래스의 골격을 완성하신 겁니다.

 

… 프록시라는 한 가지 중요한 요소를 제외하고는 말이죠.

 

다음 강좌에서는 프록시에 대해 다뤄보도록 하겠습니다.

Posted by Abastro