최근 수정 시각 : 2024-10-13 14:35:12

마인크래프트/플러그인/개발

파일:상위 문서 아이콘.svg   상위 문서: 마인크래프트/플러그인
파일:관련 문서 아이콘.svg   관련 문서: 마인크래프트/개발
, 마인크래프트/모드/개발
,
,
,
,


파일:마인크래프트 로고.svg
{{{#!wiki style="margin: -0px -10px -5px; min-height:calc(1.5em + 5px)"
{{{#!folding [ 펼치기 · 접기 ]
{{{#!wiki style="margin: -5px -1px -11px; word-break:keep-all"
<colbgcolor=darkgreen><colcolor=#fff> 기본 플레이
시스템세계 (시드) · 게임 모드 · 난이도 · 게임 규칙 · 엔딩 · 죽음 메시지 · 스플래시 · 명령어 · NBT · 런처
인게임아이템 · (플레이어) · 개체 · 날씨 · 차원 · 생물군계 · 구조물 · 마법 부여 · 상태 효과 · 조작법 · 피해 · 업적
도움말튜토리얼 · (탐험 · 파밍 · 회로) · 재생 가능한 자원 · 브릿징 · PVP · 파쿠르 · 스피드런 · 건축 (맵아트)
시리즈 및 매체
출시 에디션자바 에디션 (업데이트 · 거리 효과) · 베드락 에디션 (업데이트) · 포켓 에디션* · 콘솔 에디션* · 파이 에디션*
파생 게임마인크래프트 던전스* · 마인크래프트 레전드* · 마인크래프트 에듀케이션 · 마인크래프트: 스토리 모드* · 마인크래프트 어스*
미디어OST · 관련 서적 · 레고 · 영화 · 애니메이션 · Minecraft Live · Minecraft Now · Minecraft Monthly
유저 콘텐츠
창작 요소2차 창작 · 망토 · · 모드 (개발 · · 모드팩) · 애드온 · (리소스 팩 · 데이터 팩) · 외부 프로그램 ·
멀티 콘텐츠멀티플레이 · 서버 · 플러그인 · Realms · EULA
개발개발 기초 · 모드 개발 · 플러그인 개발
기타
이야깃거리여담 · 커뮤니티 · 사건 사고 · 문제점 · 용어 · 지원 언어 · 머나먼 땅 · 이미테이션 게임 · 히로빈
관련 문서나무위키 마인크래프트 프로젝트 · 마인크래프트로 분류된 문서 · 마인크래프트의 하위 문서
* 표시는 서비스가 종료되었거나 개발이 중단되었다는 표시이다. }}}}}}}}}


1. 개요2. 편집 지침3. 도입
3.1. 플러그인 이란?3.2. 기초적인 지식
4. 준비
4.1. 통합 개발 환경(IDE) 설치
4.1.1. Eclipse
4.1.1.1. 구버전 Eclipse (Mars 버전 이전; 미설치형)4.1.1.2. 신버전 Eclipse (Mars.1 버전 이후; 미설치형)4.1.1.3. 신버전 Eclipse (Mars.1 버전 이후; 설치형)
4.1.2. IntelliJ IDEA
4.2. Java Development Kit (JDK) 설치
5. 첫 프로젝트 만들기( Eclipse )
5.1. Eclipse
5.1.1. Eclipse 실행5.1.2. 작업공간 (Workspace) 설정5.1.3. 프로젝트 만들기
5.2. Bukkit API 외부 라이브러리 다운로드
5.2.1. Gradle을 사용한 Paper API 다운로드5.2.2. SpigotMC의 BuildTools를 이용한 다운로드
5.3. Bukkit API 외부 라이브러리 적용하기
5.3.1. 패키지와 클래스 생성5.3.2. 코드 작성
6. 첫 프로젝트 시작( intelliJ )
6.1. 프로젝트 시작6.2. Gradle 버전 설정6.3. build.gradle 세팅
7. 코드 작성에 관한 팁8. 기본설정( 메인 클래스 , 콜백함수 설정)
8.1. onLoad()
9. 다양한 기능에 관한 기본설정
9.1. 명령어 제작 및 실행
9.1.1. 명령어 실행부 작성( CommandExecutor, TabCompleter )9.1.2. 명령어를 플러그인에 등록9.1.3. 플러그인 정보에 명령어 등록
10. 제목 표시하기11. Bukkit API
11.1. org.bukkit.plugin.java.JavaPlugin
11.1.1. protected File getFile()11.1.2. public ChunkGenerator getDefaultWorldGenerator(String worldName, String id)11.1.3. public EbeanServer getDatabase()11.1.4. public FileConfiguration getConfig()11.1.5. public InputStream getResource(String filename)11.1.6. protected void installDDL()11.1.7. protected void removeDDL()11.1.8. public PluginCommand getCommand(String name)11.1.9. public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args)11.1.10. public void reloadConfig()11.1.11. public void saveConfig()11.1.12. public void saveDefaultConfig()11.1.13. public void saveResource(String resourcePath, boolean replace)11.1.14. public List<Class<?>> getDatabaseClasses()11.1.15. org.bukkit.entity
11.1.15.1. Player 인터페이스
11.1.15.1.1. setPlayerListName
11.1.15.2. HumanEntity 인터페이스
11.2. org.bukkit.event
12. Craftbukkit 구현부 레퍼런스
12.1. net.minecraft.network.protocol.game
12.1.1. Clientbound 패킷 사용법

1. 개요

본 문서는 마인크래프트 서버에 사용되는 플러그인 개발에 도움을 주고자 생성되었다.

기준은 jdk 1.8.0_91craftbukkit-1.9.4를 기반으로, 코드 및 전반 내용은 Bukkit WikiSpigotMC Wiki, Bukkit API, Spigot API를 기반으로 작성되었다.

만약 마인크래프트 모드 개발을 찾아왔다면 모드 개발 문서로 가야한다.

모드를 만드는 프로그램은 있지만 플러그인을 만드는 프로그램은 없다. 이 때문에 유저들 중 플러그인 개발을 시도하다 포기하는 경우가 많다.[1]

2. 편집 지침

플러그인 코딩에 사용되는 소스 코드는 Color Scripter의 Java 기능을 이용해 작성하시길 바랍니다.

만약 나무위키에서 HTML 지원이 종료될 경우, 테이블(표) 기능을 이용해 대체하면 됩니다.

3. 도입

3.1. 플러그인 이란?

플러그인(Plug-in; PIN)은 호스트 프로그램과 상호작용하는 컴퓨터 소프트웨어이다.
위에서 정의한 내용이 "플러그인"의 원래 정의이며, 마인크래프트에서 정의하는 플러그인은
서버에 부가 기능을 추가하여 커맨드, 이벤트, 내부 연산 등을 통해 게임 내부에 새로운 요소를 추가하는 것
을 의미한다.

플러그인을 활용한 예로는 게임 화폐 추가, 미니게임 개발, 새로운 커맨드 추가 등이 있다.

게임 클라이언트와 서버에 범용적으로 적용되는 Universal 모드와는 다르게, 바닐라 서버의 동작을 조작하는 것이 플러그인이다. 즉, 서버 측 모드라고 볼 수 있다.

Universal로 분류된 모드와 비교하면, 서버 관리자가 플러그인을 추가해도 접속하는 유저들이 덩달아 같은 플러그인을 설치할 필요가 없는 이점이 있지만, 싱글 플레이나 LAN 서버, Realms 서버에서 사용할 수 없다는 단점이 있다.

3.2. 기초적인 지식

4. 준비

4.1. 통합 개발 환경(IDE) 설치

이 문서는 IntelliJ IDEA와 Eclipse IDE에 관한 설명으로 나누었다.
Eclipse IDE: 수동 개발 환경이지만, IntelliJ보다 프로그램 규모가 작아서 개발하기 유용하다. 그러나, 편의 기능 제공등에 제약이 많이 따르기에, 이 부분은 유념해야한다.

Intellij IDEA: 1.12+ 기준으로, Minecraft Developer Plugin이 IntelliJ에서 기본으로 제공 되므로, 상위 버전 개발에 매우 유용하다. 특히, 일일이 설정할 필요없이, 기본적인것들을 모두 세팅해 주므로 매우 편리하다.

4.1.1. Eclipse

4.1.1.1. 구버전 Eclipse (Mars 버전 이전; 미설치형)
자동 설치기가 있고 구버전을 사용할 이유도 없기 때문에 이 방법은 권장하지 않는다.
4.1.1.2. 신버전 Eclipse (Mars.1 버전 이후; 미설치형)
이클립스 Mars 이후에도 미설치형으로 사용하는 것이 가능하다.
1. 우선 이클립스 다운로드 파일이 있는 페이지로 가야한다. #
2. 왼쪽에서 다운 받을 버전을 선택한다.
3. 다운로드 가능한 zip 파일이 뜬다. 필요한 것을 다운로드 하자. (플러그인 개발에는 Java와 Java EE중 하나를 하면 된다.)
4.1.1.3. 신버전 Eclipse (Mars.1 버전 이후; 설치형)
빠르게 이클립스를 설치하는 등의 이유가 있을 때는 최신버전도 미설치형으로 사용 가능하다.

4.1.2. IntelliJ IDEA

IntelliJ IDEA는 Eclipse보다 무겁지만 훨씬 더 많은 편의 기능을 제공한다. 이곳에서 다운로드할 수 있다.[4]


IntelliJ는 마인크래프트 개발용으로 제작된 Minecraft Developement Plugin이라는 Intellij Plugin을 제공한다. 이를 우선 다운로드 해야한다.
파일:intelliJ_mcplugin_download.png
이 Plugin Download로 이동하여 Install을 클릭하여, 마인크래프트 개발용 플러그인을 다운로드 한다. 이후에 Restart IDE라는 버튼이 나오는데, 플러그인을 정상적으로 작동시키기 위해서는 반드시 해주어야 한다.

4.2. Java Development Kit (JDK) 설치

마인크래프트 1.16까지의 경우 JDK의 버전은 최소 8이여야 한다.
흔히 쓰는 JDK로는 JDK 8, JDK 11과 JDK 16이 있다.

주의할 점으로, 1.17부터는 JDK 16, 1.18부터는 JDK 17을 사용해야 한다.

JDK에 관해서는 주의할 점이 있는데, JDK 1.8버전은 Oracle에서 다운로드 하기 위해서는 Oracle에 로그인을 해야한다. 이를 위해 미리 Oracle에 가입하는 것이 좋다.

5. 첫 프로젝트 만들기( Eclipse )

5.1. Eclipse

5.1.1. Eclipse 실행

파일:플러그인 개발 - 이클립스 폴더.png
위의 사진처럼 eclipse.exe를 더블 클릭하여 실행한다.

5.1.2. 작업공간 (Workspace) 설정

파일:플러그인 개발 - 이클립스 Workspace 설정.png
후에 생성할 프로젝트의 저장 공간을 설정하면 된다.

5.1.3. 프로젝트 만들기

이 문단에서는 Java EE 개발 환경을 Java SE 개발 환경으로 변경하는 과정을 생략했다. 이는 우측 상단 Open Perspective 버튼을 눌러 Java를 선택한 다음 OK를 눌러주면 된다. 이 과정은 제일 중요하고 제일 기초적인 부분이며, 공통적으로 사용되기 때문에 각 과정에서 생략한다.

파일:플러그인 개발 - 새 프로젝트 만들기.png
프로젝트는 File - New - Java Project 또는 New (추가 아이콘) - Java Project를 통해 생성할 수 있다.

파일:플러그인 개발 - 프로젝트 선택.png
이는 Java EE 환경에서 일반 프로젝트를 생성했을 때 또는 Java SE 환경에서 New 아이콘만 클릭했을 때 설정한다.

파일:플러그인 개발 - 프로젝트 설정.png
Project Name은 한글로 해도 상관은 없으나, 패키지명에 프로젝트 명을 포함시킬 생각[5]이라면 띄어쓰기를 하지 않는 것이 좋다.

5.2. Bukkit API 외부 라이브러리 다운로드

서버를 실행할 때 사용되는 "Craftbukkit-1.8.9-R0.2.jar"와 같은 파일을 외부 라이브러리라고 한다. 이 파일을 Eclipse를 통해 플러그인에 적용시켜야 정상적인 코딩이 가능하고, 서버에 적용시킬 수 있다.

다운로드 방법은 크게 2가지로 나뉜다. 첫번째로, 권장되는 방법은 Gradle을 사용하여 Paper API를 다운로드하는 것이다. 두번째로는 SpigotMC의 BuildTools를 이용하여 Spigot과 Craftbukkit을 동시에 다운로드하는 방법[6]이 있다.

각 방법은 하위 문단에서 자세히 설명한다.

5.2.1. Gradle을 사용한 Paper API 다운로드

5.2.2. SpigotMC의 BuildTools를 이용한 다운로드

본 문서의 내용 일부는 SpigotMC Wiki의 BuildTools 항목에서 가져왔음을 알립니다.
파일:BuildTools GUI.png

과거의 CraftBukkit DMCA Takedown 사건 이후로 CraftBukkit 기반의 프로그램을 직접 배포하는 것은 불법이 되었다. SpigotMC에서는 이를 우회하기 위해 BuildTools라는 프로그램을 만들었다. BuildTools를 이용해 파일을 다운로드하기 위해 다음의 파일들을 다운로드하자.

준비물의 준비가 모두 끝났으면 아래와 같은 순서를 통해 진행하자.
  1. BuildTools를 다운로드하여 폴더 경로에 띄어쓰기나 특수문자가 없는 폴더[9]에 저장 또는 옮긴다.
  2. 해당 폴더로 이동하여 빈 공간에 커서를 옮기고 Shift + 우클릭을 이용해 팝업 메뉴를 소환합니다. 그리고 Git Bash Here를 클릭하여 Git Bash를 실행한다.
  3. 콘솔에 java -jar BuildTools.jar --rev <원하는 마인크래프트 버전>를 입력하고 엔터를 누른다.[10]
  4. 컴파일이 완료될 때까지 콘솔을 종료하지 말고 기다린다.
  5. 빌드가 끝나면[11] target 폴더에 Spigot이 컴파일된 것을 확인할 수 있다.

예시로, java -jar BuildTools.jar --rev 1.12.2를 입력하여 1.12.2 버전의 Spigot을 다운로드할 수 있다.

2024년 패치로 BuildTools 자체에 GUI가 있어 쉽게 만들 수 있다. 버전 선택(Select Version)을 하고 경로를 지정한 다음 컴파일(Compile) 버튼을 누르기만 하면 알아서 빌드된다. 또한 윈도우 전용인 .exe 버전도 배포하고 있다.

5.3. Bukkit API 외부 라이브러리 적용하기

마인크래프트 플러그인은 외부 라이브러리가 없으면 적용되지 않습니다. 무조건 등록해 주셔야 합니다.

파일:플러그인 개발 - Properties 메뉴.png

파일:플러그인 개발 - Properties.png

파일:플러그인 개발 - Jar 파일 선택.png

프로젝트 우클릭 - Build Path... - Configure Build Path... - Libraries 탭으로 이동 - Add External JARs...를 사용하면 빠르게 등록 및 제거하실 수 있습니다.

5.3.1. 패키지와 클래스 생성

패키지(Package)의 역할은 클래스(Class)를 단위 또는 그룹별로 모으는 역할과 클래스의 위치를 알려주는 역할을 합니다.

파일:플러그인 개발 - 패키지 메뉴.png
패키지는 (프로젝트가 선택된 상태에서) File - New - Package 방법, 프로젝트 우클릭 - New - Package 방법, 패키지 추가 아이콘 방법의 세 가지 방법을 사용할 수 있습니다.

파일:플러그인 개발 - 패키지.png
패키지 이름은 프로젝트와 다르게 무조건 영어로 입력하여야 합니다. 후에 입력할 plugin.yml 파일에서 메인 클래스를 찾지 못하는 상황이 발생할 수 있습니다. 또한, 프로젝트 이름과 달리 절대 띄어쓰기를 하면 안 됩니다.

또한 통상적으로 Java 개발 시 패키지는 다음과 같은 규칙을 이용합니다.
최상위 클래스명(대부분 com, org, net 등).제작자명.프로젝트 이름

굳이 위의 규칙을 지키지 않아도 프로그래밍 및 컴파일 시에는 문제되지 않습니다.

클래스(Class)는 Java에서 컴파일 후 실행할 명령들이 코드로 담긴 문서라고 생각하시면 됩니다.

파일:플러그인 개발 - 클래스 메뉴.png
클래스는 패키지와 비슷하게 (프로젝트가 선택된 상태에서) File - New - Class 방법, 프로젝트 우클릭 - New - Class 방법, 클래스 추가 아이콘 방법의 세 가지 방법을 사용할 수 있습니다.

파일:플러그인 개발 - 클래스.png
클래스 이름을 작성할 때에도 무조건 영어로 작성하여야 하며, 굳이 대문자를 사용하지 않아도 됩니다. 또한, 메인 클래스의 이름은 Main이 아닌 자신의 플러그인의 이름으로 사용해도 됩니다. (예: MyPlugin.java, YourPlugin.java 등) 클래스도 패키지와 마찬가지로 절대 띄어쓰기를 하면 안 됩니다.

5.3.2. 코드 작성

파일:플러그인 개발 - 메인 클래스 코드 작성.png
#!syntax java
package main;

import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.plugin.java.JavaPlugin;

public class Main extends JavaPlugin{
	@Override
	public void onEnable(){
		Bukkit.getConsoleSender().sendMessage(ChatColor.BLUE + "플러그인이 활성화 되었습니다.");
	}
	
	@Override
	public void onDisable(){
		Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "플러그인이 비활성화 되었습니다.");
	}
}




파일:플러그인 개발 - 파일 추가 메뉴.png
파일:플러그인 개발 - plugin.yml 파일.png
파일:플러그인 개발 - plugin.yml 에디터 선택.png
파일:플러그인 개발 - plugin.yml 에디터 선택2.png
파일:플러그인 개발 - plugin.yml 작성.png
코드
파일명 plugin.yml
종류 YAML
name: FirstMinecraftPlugin
version: 1.0
author: Devonnuri
description: My First Minecraft Plugin

main: main.Main
name플러그인의 이름 (필수, 공백 없이)
version플러그인의 버전 (필수)
main플러그인의 메인 클래스 (필수)
author플러그인의 저자
description플러그인의 설명

6. 첫 프로젝트 시작( intelliJ )

이 부분은 IntellIJ Gradle개발을 기준으로 서술하였다. 따라서 build를 위해서 build에 대한 기본정보를 build.gradle에 모아 두었다.

6.1. 프로젝트 시작

파일:intellij_mcplugin_generate_project.png
IntelliJ에서 New Project를 클릭한 뒤, Minecraft( Old Wizard )[12]를 클릭한다.
Name: 프로젝트의 이름이다. 플러그인의 이름과는 다르다.
Location: 개발하는 폴더의 위치를 지정한다.
Platform Type: 모드와 플러그인줄 개발할 형태를 지정한다. 우리는 플러그인이므로, Plugin에 체크한다.
Platform: 서버를 열 플랫폼을 지정한다. 우리는 Bukkit으로 개발하므로 Bukkit에 체크한다.
Bukkit Platform: Spigot보다는 Paper가 최적화가 조금 더 잘되어있기에, 플러그인을 개발하여 사용할 예정이라면 Paper를 추천한다.
Minecraft Version: 이 Intellij Plugin[13]는 Old Wizard 기준으로 1.12버전부터 1.21버전까지 지원한다.[14]
Plugin Mame: 플러그인의 이름을 지정한다. 컴파일할 때 이 이름으로 나온다.
Main Class: 프로그램을 구동할 때 가장 먼저 확인하는 클래스의 이름을 지정한다. 이 파일에 JavaPlugin이 상속되어, 플러그인으로 작용하게 된다.
Build System: Gradle을 추천한다.
Paper Manifest: 체크하지 않고 개발하는 방법을 설명한다. 기존 개발법은 체크하지 않는 것이다.

나머지는 미리 설정하였으므로 크게 의미가 있지는 않다. 이제, create버튼을 누르면 자동으로 플러그인을 위한 기본적인 폴더를 세팅해준다. Eclipse의 경우에는 일일이 입력해야하는데 여기는 Package부터 Main Class까지 전부다 자동으로 해준다.

Eclipse의 경우에는 Bukkit API를 수동으로 다운받아야하지만, 이 경우에는 자동으로 추가해주므로 훨씬 편리하다.

!! 주의 !!
1.21버전을 기준으로 플러그인에 정보를 작성하는 plugin.yml의 작성 방식이 변경되었다. 여기서, Old Wizard라는 부분은 기존에 개발하던 방식 즉, 이 문서에서 설명하는 방법이며, Old Wizard표시가 되어있지 않은 부분은 새롭게 개발된 방법이다.
Old Wizard제공: 1.12 ~ 1.21 , New: 1.18 ~ 1.12

6.2. Gradle 버전 설정

Gradle은 마인크래프트 버전에 따라서 적절한 버전으로 설정해주어야한다. 보통은 Gradle버전에 관한 문제를 띄우며 클릭만 해주면 적절한 버전으로 다운로드 해주지만, 어떤 경우에는 특정 gradle 버전에 버그가 존재해서 수동으로 버전을 변경해야하는 경우도 있다. 프로젝트 파일에서 gradle-wrapper.properties에 들어가서 버전을 변경해야한다.
파일:intellij_mcplugin_gradle_version_setting.png

6.3. build.gradle 세팅

여기서 필요한 경우 주석처리된 부분을 참조하여 개발한다.
plugins {
    // 이 플러그인 개발을 위해 필요한 프로그램들을 작성한다.
    // id ''형식으로 작성한다.
    
    id 'java'
}

group = 'org.tutorial' // 그룹명은 intellij를 사용해서 refactor기능을 사용하면 자동으로 변경되므로, 수동 수정은 비권장한다.
version = '1.0-SNAPSHOT' // 버전은 수동으로 바꿔주는게 좋다.

repositories {
    mavenCentral()
    maven {
        name = "papermc-repo"
        url = "https://repo.papermc.io/repository/maven-public/"
    }
    maven {
        name = "sonatype"
        url = "https://oss.sonatype.org/content/groups/public/"
    }
}

dependencies { 
    //참조할 라이브러리들을 작성한다. compileOnly ""형식으로 작성한다.

    compileOnly "io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT" 
}

def targetJavaVersion = 21 //자바 버전은 경우에 따라 다를 수 있다. 이 경우에는 1.21기준이라 Java 21을 사용하였다.
java {
    def javaVersion = JavaVersion.toVersion(targetJavaVersion)
    sourceCompatibility = javaVersion
    targetCompatibility = javaVersion
    if (JavaVersion.current() < javaVersion) {
        toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion)
    }
}

tasks.withType(JavaCompile).configureEach {
    options.encoding = 'UTF-8' // 컴파일시 한국어 지원이 가능하도록 UTF-8 인코더를 지원한다.

    if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) {
        options.release.set(targetJavaVersion)
    }
}

processResources {
    def props = [version: version]
    inputs.properties props
    filteringCharset 'UTF-8'
    filesMatching('plugin.yml') {
        expand props
    }
}

7. 코드 작성에 관한 팁

  • https://hub.spigotmc.org/javadocs/bukkit/에서 레퍼런스를 보실 수 있습니다. 레퍼런스를 볼 능력이 요구됩니다.
  • Java객체 지향 프로그래밍을 잘 모르셔도 간단한 플러그인의 경우 그렇게 문제가 되지는 않습니다. 그러나, 미리 Java의 기본을 익히고 오는 것이 매우 강력히 권장됩니다.
  • 이 문서에서 다루는 버킷 외에 Spigot에 대해 관심이 있다면 이 곳을 확인하세요.
  • 이클립스는 단축키가 지원됩니다. 대표적으로 많이 쓰이는 단축키들입니다.
    Ctrl+Shift+F 자동 정렬

    Ctrl+Space 자동 완성

    Ctrl+Shift+O 자동 import문 추가

8. 기본설정( 메인 클래스 , 콜백함수 설정)

메인클래스는 플러그인을 서버에 등록하는 곳으로 JavaPlugin 클래스를 상속받아야한다. 상속받지 못하면 서버 플러그인으로써의 기능을 하지 못하게된다. Main Class는 다음과 같이 작성해야한다.
#!syntax java
package org.tutorial.projectname; //패캐지의 이름이다. Intellij의 경우 refactor를 사용하면 자동으로 변경 가능하다

import org.bukkit.plugin.java.JavaPlugin;

public final class ProjectName extends JavaPlugin { //JavaPlugin 클래스를 상속받는다.

    
    @Override
    public void onEnable() { // 플러그인이 서버 실행 또는 reload를 통해서 서버에 등록되면 실행된다.

    }


    @Override
    public void onDisable() { // 플러그인이 서버가 닫히거나 reload를 통해서 서버가 정지되면 실행된다.

    }
}

8.1. onLoad()

플러그인 로딩시 onEnable보다 먼저 실행된다. 위의 Main Class에 등록하면 된다.
#!syntax java
@Override
public void onLoad() {
    // 플러그인을 로드 하였을 때(활성화 이전) 발생하는 콜백 함수
    // 버킷을 실행하거나 /reload 명령어로 재시작하였을 때 발생합니다.
}

9. 다양한 기능에 관한 기본설정

개발을 위해서 필요한 다양한 기능들을 작성하기 위한 최소한의 코드를 작성하였다. 주로 사용하는 부분은 명령어를 제작하는 것과 실행 그리고 event를 받아오는 것이다.

9.1. 명령어 제작 및 실행

명령어를 만들기 위해서는 명령어를 입력햇을 때 실행할 부분, 명령어를 플러그인에 등록, 명령어를 플러그인 정보에 추가하는 3단계를 거쳐야 플러그인이 적용된 서버에 플러그인이 추가한 명령어가 생성된다.

9.1.1. 명령어 실행부 작성( CommandExecutor, TabCompleter )

클래스를 하나 생성한 뒤, 참조할 interface에 CommandExecutor와 TabCompleter를 추가한다. 다음과 같이 추가하면 된다.
#!syntax java
package org.tutorial.projectname;

import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;

public class PluginCommand implements CommandExecutor, TabCompleter {

    @Override
    public boolean onCommand(@NotNull CommandSender sender, org.bukkit.command.@NotNull Command command, @NotNull String label, @NotNull String[] args) { //커맨드의 실행을 담당할 부분
        return false;
    }

    @Override
    public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, Command command, @NotNull String label, @NotNull String[] args) { //Tab키를 눌러서 명령어 자동 완성을 담당할 부분
        return null;
    }
}
CommandSender: 커맨드를 실행한 주체( 블럭, 플레이어, 서버 )에 접근한다.
Command: 보낸 명령어
label: 명령어의 첫 부분( summon, tp등 )
args: 뒤쪽에 입력한 인수들을 의미한다. args[0]부터 입력하며, label은 제외된다는 특징이있다.
return( boolean ): true이면 명령어가 실행된 것으로 보고하며, false이면 실행이 되지 않은 것으로 보고한다.
return( List<String> ): list에 탭키로 자동완성할 문자들을 쭉 작성한다. null인 경우 반환하지 않는다.

CommandExecutor는 명령어가 실행되는 경우 어떻게 실행될지를 정의하는 부분이며, TabCompleter는 탭키를 눌러 자동완성을 하는 경우에 어떻게 자동완성 시킬지를 정의하는 부분이다. 두 부분은 같은 클래스에 정의해야 혼란이 줄어들기에 같은 클래스에 정의하는 것을 추천한다.

9.1.2. 명령어를 플러그인에 등록

JavaPlugin을 상속받은 곳 즉, Main Class에 다음과 같이 입력한다.
#!syntax java
getCommand("명령어 이름").setExecutor( new PluginCommand() ); //명령어를 실행할 Class를 정의한다. 이 경우에는 PluginCommand 클래스가 담당한다.
getCommand("명령어 이름").setTabCompleter( new PluginCommand() ); //명령어에서 자동완성 기능을 담당할 Class를 정의한다. 이 경우에는 PluginCommand 클래스가 담당한다.

'명령어 이름' 부분에는 label을 작성하면된다.

IntelliJ의 경우 DataFlowIssue가 발생한다는 경고문이 생기는데 클래스를 잘 정의하고 작성하였다면 그런 버그는 일어나지 않으므로, method부분에 SupressWarning을 넣어놓아도 좋다. 전체적으로 보면 아래와 같다.

#!syntax java
package org.tutorial.projectname;

import org.bukkit.plugin.java.JavaPlugin;

public final class ProjectName extends JavaPlugin {

    @SuppressWarnings("DataFlowIssue")
    @Override
    public void onEnable() {
        getCommand("command").setExecutor( new PluginCommand() );
        getCommand("command").setTabCompleter( new PluginCommand() );

    }

    @Override
    public void onDisable() {
        
    }
}

command라는 명령어를 실행할 Class가 PluginCommand로써 정의되었다.

9.1.3. 플러그인 정보에 명령어 등록

plugin.yml이라는 파일에 다음과 같은 명령어가 추가되었음을 알려야 한다. 정의해주지 않을 시에는 NullPointerException 에러가 발생한다. plugin.yml에는 다음과 같이 설정한다.
name: ProjectName
version: '${version}'
main: org.tutorial.projectname.ProjectName
api-version: '1.21'
commands: 
  command:
    description: tutorial command
    aliases: [ "a" , "b" ]
    usage: tutorial command
commands( 필수 ): 커맨드를 등록해놓기 위한 부분이다.
command( 필수 ): 커맨드의 이름을 작성하면 된다. 지금은 커맨드의 이름을 command로 설정한 것 뿐이고, 명령어 이름에 따라 바꾸면 된다. label의 이름을 따른다.
description( 필수 아님 ): 명령어에 관한 설명을 적는다.
aliases( 필수 아님 ): 명령어와 같은 기능을 할 다른 label들을 정의한다.
usage( 필수 아님 ): 간단한 명령어를 적는다.

10. 제목 표시하기

파일:플러그인 개발 - Title 테스트.png
이런 걸 표시해봅시다.

#!syntax java
public class 클래스명
{
    public void sendTitle(Player player, String title, int FadeInTime, int ShowTime, int FadeOutTime){
			CraftPlayer p = (CraftPlayer)player;
		    PlayerConnection c = p.getHandle().playerConnection;
		    IChatBaseComponent TitleText = ChatSerializer.a(title);
		    Packet<?> Length = new PacketPlayOutTitle(EnumTitleAction.TIMES, TitleText, FadeInTime*20, ShowTime*20, FadeOutTime*20);
		    Packet<?> TitlePacket = new PacketPlayOutTitle(EnumTitleAction.TITLE, TitleText, FadeInTime*20, ShowTime*20, FadeOutTime*20);
		    c.sendPacket(TitlePacket);
		    c.sendPacket(Length);
	}
}


주의: 패킷을 사용하면 nms 패키지를 참조하게 되고, 버전이 바뀔 때마다 패킷 구조가 바뀌므로 권장하지 않습니다. 아래는 spigot api에서 제공하는 타이틀 메소드입니다.

#!syntax java
player.sendTitle ("제목 메시지", "부제목 메시지", 나타나는 시간, 화면에 머무는 시간, 사라지는 시간);
/*제목 메시지나 부제목 메시지를 없에려면 null을, 시간을 기본값으로 설정하려면 -1을 대입하면 됩니다. 시간 기본값은 왼쪽부터 10틱, 70틱, 20틱 입니다.*/

11. Bukkit API


이 문단은 버킷의 패키지, 패키지 안에 있는 클래스와 인터페이스, 열거형 등을 통틀어 Bukkit API에 대하여 다룬다.

11.1. org.bukkit.plugin.java.JavaPlugin


모든 Bukkit 플러그인은 JavaPlugin을 상속해야 합니다.
아래의 메소드는 JavaPlugin에 있는 메소드입니다. (@Override가 있는 경우 오버라이드 가능)

11.1.1. protected File getFile()

#!syntax java
    @Override
    protected File getFile()
    {
        return super.getFile();
    }

11.1.2. public ChunkGenerator getDefaultWorldGenerator(String worldName, String id)

#!syntax java
    @Override
    public ChunkGenerator getDefaultWorldGenerator(String worldName, String id)
    {
        return super.getDefaultWorldGenerator(worldName, id);
    }

11.1.3. public EbeanServer getDatabase()

#!syntax java
    @Override
    public EbeanServer getDatabase()
    {
        return super.getDatabase();
    }

11.1.4. public FileConfiguration getConfig()

#!syntax java
    @Override
    public FileConfiguration getConfig()
    {
        return super.getConfig();
    }

11.1.5. public InputStream getResource(String filename)

#!syntax java
    @Override
    public InputStream getResource(String filename)
    {
        return super.getResource(filename);
    }

11.1.6. protected void installDDL()

#!syntax java
    @Override
    protected void installDDL()
    {
        super.installDDL();
    }

11.1.7. protected void removeDDL()

#!syntax java
    @Override
    protected void removeDDL()
    {
        super.removeDDL();
    }

11.1.8. public PluginCommand getCommand(String name)

#!syntax java
    @Override
    public PluginCommand getCommand(String name)
    {
        return super.getCommand(name);
    }

11.1.9. public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args)

#!syntax java
    @Override
    public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args)
    {
        return super.onTabComplete(sender, command, alias, args);
    }


주요 사용 예시:

#!syntax java
    @Override
    public List<String> onTabComplete( CommandSender sender, Command command, String label, String[] args) {
        if ( args.length == 1) {
            List<String> list = new ArrayList<>( (int) list의 크기);
            list.add( string source );
            list.add( string source );
            list.add( string source );
            .
            .
            .
            return list;
        }
        else if ( args.length == 2) {
            양식은 비슷함
        }
        .
        .
        .
        else if ( args.length == n[ 자신이 원하는 수 ]){

        }
        return null;
    }



11.1.10. public void reloadConfig()

#!syntax java
    @Override
    public void reloadConfig()
    {
        super.reloadConfig();
    }

11.1.11. public void saveConfig()

#!syntax java
    @Override
    public void saveConfig()
    {
        super.saveConfig();
    }

11.1.12. public void saveDefaultConfig()

#!syntax java
    @Override
    public void saveDefaultConfig()
    {
        super.saveDefaultConfig();
    }

11.1.13. public void saveResource(String resourcePath, boolean replace)

#!syntax java
    @Override
    public void saveResource(String resourcePath, boolean replace)
    {
        super.saveResource(resourcePath, replace);
    }

11.1.14. public List<Class<?>> getDatabaseClasses()

#!syntax java
    @Override
    public List<Class<?>> getDatabaseClasses()
    {
        return super.getDatabaseClasses();
    }

11.1.15. org.bukkit.entity

11.1.15.1. Player 인터페이스
분류 의미
v void(리턴 안 함))
b boolean(참, 거짓)
i int(32비트 정수)
s String(문자열)
d Double (배정밀도 부동소수점 실수)
f Float (단정밀도 부동소수점 실수)
<이름> 클래스
분류 메서드 기능
v chat(String str) 채팅 띄우기
InetSocketAddress getAddress() 접속 주소(IP) 구하기
i getLevel() 레벨 구하기
f getWalkSpeed() 걷는속도 구하기 (-1.0 ~ 1.0)
v setWalkSpeed(float value) 걷는속도 설정하기 (-1.0 ~ 1.0)
v setDisplayName(String name) 디스플레이 이름 변경
s getPlayerListName() 플레이어 목록의 이름 구하기
v setPlayerListName(String name)) 링크 참고
v setLevel(int level) 경험치 레벨 설정
i getLevel() 경험치 레벨 얻어오기
v giveExp(int amount) 경험치를 amount만큼 주기
v giveExpLevels(int amount) 경험치 레벨을 amount만큼 주기
i getFoodLevel() 배고픔 수치 구하기 (0~10)
v setFoodLevel(int value) 배고픔 수치를 value로 설정
v kickPlayer(String message) message를 사유로 퇴장시키기
v hidePlayer(Player player) player를 보이지 않게 하기
v sendMessage(String message) 플레이어에게 메시지 보내기 (ChatColor와 혼용 가능)
PlayerInventory getInventory() 플레이어의 인벤토리를 불러옴 (HumanEntity에서 상속됨)
11.1.15.1.1. setPlayerListName
인지: String name
인게임 플레이어 리스트의 이름을 설정합니다.
16자까지 허용되며, 색깔이 지원됩니다.
일어날 수 있는 예외들:
  • IllegalArgumentException - 이름이 너무 길거나[15] 누군가가 이미 쓰고 있는 이름인 경우
(원문)
Sets the name that is shown on the in-game player list.
The name cannot be longer than 16 characters, but ChatColor is supported.

If the value is null, the name will be identical to HumanEntity.getName().

This name is case sensitive and unique, two names with different casing will appear as two different people. If a player joins afterwards with a name that conflicts with a player's custom list name, the joining player's player list name will have a random number appended to it (1-2 characters long in the default implementation). If the joining player's name is 15 or 16 characters long, part of the name will be truncated at the end to allow the addition of the two digits.

Parameters:
name - new player list name
Throws:
IllegalArgumentException - if the name is already used by someone else
IllegalArgumentException - if the length of the name is too long
11.1.15.2. HumanEntity 인터페이스
분류 의미
v void(리턴 안 함))
b boolean(참, 거짓)
i int(32비트 정수)
s String(문자열)
d Double (배정밀도 부동소수점 실수)
f Float (단정밀도 부동소수점 실수)
<이름> 클래스
분류 메서드 기능
v closeInventory() 보관함을 닫는다.
i getCooldown(Material material) 특정한 아이템의 쿨타운을 구한다.(아마도)
Inventory getEnderChest() 엔더 상자를 인벤토리 형태로 가져온다.
i getExpToLevel() 다음 레벨까지 남은 경험치를 가져온다.
GameMode getGameMode() 게임 모드를 가져온다.
PlayerInventory[16] getInventory() 플레이어의 인벤토리를 가져온다.
ItemStack getItemOnCursor() 마우스 커서 아래의 아이템을 구한다. 비어있을 수 있다.
MainHand getMainHand() 주 손(설정에서 변경)을 가져온다.
s getName() 플레이어의 이름을 반환한다.


Player 인터페이스에서도 상속받는 인터페이스로 마인크래프트에서 사람(플레이어)의 메소드를 대량 포함한다.
게임에서 NPC[17]나 플레이어에 반영되는 내용을 그대로 포함한다.
항목내용
선언 public interface HumanEntity
상속 LivingEntity, AnimalTamer, Permissible, InventoryHolder

11.2. org.bukkit.event

12. Craftbukkit 구현부 레퍼런스

플러그인을 개발하다 보면 Bukkit API에서 직접 지원하지 않는 기능이 필요할 때가 있는데, 이 경우 직접 서버의 구현부에 접근해서 데이터를 조작하는 상당히 까다로운 작업이 요구된다.

파일:버킷_구조도.png\
org.craftbukkit 패키지와 net.minecraft.server[18] 패키지는 Craftbukkit 기반 서버의 실제 구현 부분이다. NMS는 마인크래프트 자체의 코드로, 난독화되어 있기 때문에 이를 사용하기 위해서는 난독화를 해제할 수 있는 매핑을 사용해야 한다. NMS Mapper에서 NMS 매핑 표를 확인할 수 있으나, 이도 상당히 번거롭기에 PaperWeight, SpecialSource와 같은 역난독화 프로그램을 사용하는 게 편리하다.

매핑의 종류로는 모장 매핑, MCP 매핑, Spigot 매핑 등이 있으며, 아래의 문단은 모두 최신 버전의 모장 매핑 기준으로 서술한다.

12.1. net.minecraft.network.protocol.game

마인크래프트의 패킷 클래스들이 들어있는 패키지이다.

wiki.vg에서 패킷의 이름, 하는 역할, 사용법 등을 볼 수 있다. wiki.vg에 나와 있는 패킷과 NMS 패킷 클래스는 일대일 대응되지만 이름이 조금씩 다르므로 주의.

12.1.1. Clientbound 패킷 사용법

클라이언트로 패킷을 보내기 위해선 우선 패킷 객체를 생성해야 한다.[19]
#!syntax java
    Clientbound<패킷이름>Packet packet =  new Clientbound<패킷이름>Packet(<...>);

패킷 객체를 생성했다면, 플레이어 객체와 연결 상태를 NMS 형태로 불러오자.[20]
#!syntax java
    ServerPlayer nmsPlayer = ((CraftPlayer) player).getHandle();
    ServerGamePacketListenerImpl connection = nmsPlayer.connection;

이제 connection을 통해 패킷을 전송하면 된다.
#!syntax java
    connection.send(packet);



[1] 모드 제작 프로그램인 MCreator에 Spigot Generator 유저 플러그인을 설치하면 스피곳용 플러그인 제작이 가능하다. 단, 개발 중인 프로젝트이므로 지원하는 기능이 불완전하다.[2] 물론 Java에 대해 아무것도 몰라도 만들 수는 있다. 그러나 결과가 영 좋지 않을 것이다...[3] 구글링 할 때 원문 검색을 자주 하게 된다. 검색 결과는 번역기를 사용해서 이해하면 되지만, 영어 검색은 번역기만으로는 부족하기 때문에 어느 정도의 영어 지식이 필요하다.[4] Ultimate는 유료이므로 Community Edition을 선택하자. 학생인 경우에는 Education Pack을 신청하여 무료로 Ultimate Edition을 사용할 수 있으나, 신청 기간만 2주이다..[5] 기본적으로 패키지는 상위 클래스명.상속 클래스명(기본적으로 com.<제작자명>.프로젝트 명)을 사용하는 것이 관례이다.[6] 이 방법은 Git으로부터 코드를 컴파일하여 내려받는 방법을 사용하기 때문에 다운로드 속도가 느릴 수 있다.[7] 자바 개발자용 도구; 이하 "jdk"[8] 윈도우 환경에만 해당. 리눅스 환경에서는 터미널에서 바로 실행시킬 수 있다.[9] 예를 들어 C:\BuildTools 또는 D:\BuildTools 등[10] -jar에는 하이픈이 1개, --rev에는 하이픈이 2개임에 주의한다. 지원하는 마인크래프트 버전의 목록은 #에서 확인할 수 있다. 또한 --rev <버전>을 생략하면 가장 최근에 출시된 안정화된 버전을 다운로드하는데, 이는 최신 버전과는 다르므로 이에 유의하자.[11] 빌드 종료 시 Build success! 메시지와 함께 BuildTools가 종료된다.[12] old wizard가 아니면 이 문서에서 서술하는 방법대로 했을 때 플러그인이 작동하지 않는다.[13] 우리가 개발하는 플러그인이 아니라 처음에 다운로드 받은 Intellij에서 제공하는 플러그인을 의미한다.[14] 추가적으로 업데이트가 진행되어서 계속 사용가능하다.[15] 16글자 정도[16] Inventory를 상속받는 인터페이스로 보관함과 더불어 갑옷 슬롯과 방패(왼손)의 내용도 포함한다.[17] 시티즌 등의 사람 추가 플러그인[18] 일반적으로 앞의 글자를 따서 NMS라고 칭한다.[19] 패킷마다 생성자가 다르다. 인자로 정보를 넘겨주는 형태의 생성자를 사용할 수도 있고, FriendlyByteBuf를 사용해 직접 정보를 바이트 단위에서 입력할 수도 있다. 특별한 경우가 아니라면 인자로 넘겨주는 형태가 가장 깔끔하다.[20] player는 패킷을 보내고자 하는 플레이어이다.