반응형

https://github.com/gloomn/blogMCPluginProjects

 

GitHub - gloomn/blogMCPluginProjects

Contribute to gloomn/blogMCPluginProjects development by creating an account on GitHub.

github.com

 

플러그인 코드들은 모두 여기 있으니 코드가 필요하시면 다운로드 하시면 됩니다!

이전 강의

https://syntaxack.tistory.com/entry/minecraftplugin6

 

마인크래프트 플러그인 강좌 6강 - 커스텀 커맨드(긴 커맨드), 날씨 커맨드

마인크래프트 긴 커맨드 구현2강에서는 기본적인 커맨드 구현을 해보았다.예를 들어 /test 같은 단일 커맨드 같은 것 말이다.https://syntaxack.tistory.com/entry/minecraftplugin2 마인크래프트 플러그인 강좌

syntaxack.tistory.com

쿨타임 시스템

쿨타임 시스템은 한 번 능력을 사용하면 일정 시간 동안은 사용할 수 없게 하는 시스템이다.

마인크래프트에서 한 번 구현을 해 볼 것이다.

 

프로젝트 만들기

Intellij를 실행해서 새로운 마인크래프트 프로젝트를 만들어준다.

프로젝트 생성 방법을 모르면 아래 링크를 눌러 한 번만 보면 된다.

https://syntaxack.tistory.com/entry/minecraftplugin1

 

마인크래프트 플러그인 강좌 1강 - 첫 플러그인 만들기

이전 강의https://syntaxack.tistory.com/entry/minecraftplugin0 마인크래프트 플러그인 강좌 0강 - 준비하기마인크래프트 플러그인이란?마인크래프트 플러그인은 마인크래프트 내에서 기능을 확장할 수 있도

syntaxack.tistory.com

1. paper/spigot/sponge 템플릿에서 여러분이 사용하는 버킷 플러그인을 선택한다.

2. 빌드 시스템은 Gradle를 선택한다.

3. 언어는 자바를 선택한다.

4. 마인크래프트 버전은 여러분이 플러그인을 적용항 버전과 paper 버전이 일치하도록 선택한다.

예를 들어서 마인크래프트 버전 1.21.4, paper 버전 1.21.4 이면 1.21.4를 선택한다.

5. 플러그인 이름과 클래스 이름을 적는다.

 

생성을 눌러준다.

https://syntaxack.tistory.com/entry/minecraftpluginproblem1

 

마인크래프트 플러그인 강좌 - 잘못된 Gradle JVM 구성을 발견했습니다.

IntelliJ를 사용해서 마인크래프트 플러그인 프로젝트를 만들었을 때 잘못된 Gradle JVM 설정이라는 알람이 뜰 때가 있다.이는 JDK와 Gradle이 호환되지 않아서 발생하는 문제이다.이때는 인터넷 검색

syntaxack.tistory.com

만약 Gradle과 JVM 버전이 안 맞는다는 오류가 뜨면 위의 포스트를 보면 된다.

 

Gradle Build 설정

우리는 jar 파일로 빌드할 때 빌드 위치를 바로 플러그인 폴더로 빌드되게 설정할 것이다.

build.gradle 파일을 열어준다.

tasks.jar{
    archiveFileName = 'cooltimePlugin.jar'
    destinationDirectory = file('C:\\Users\\kijoon\\Desktop\\Server\\plugins')
}

destinationDirectory에 여러분의 plugins 폴더로 설정하면 된다.

 

코드 작성

프로젝트를 생성 할 때 만들어졌던 Cooltime.java 파일 외로 2개의 파일을 더 만들어야 한다.

GUICommand.java랑 GUIListener.java이다.

Cooltime.java

package org.blog.cooltime;

import org.bukkit.plugin.java.JavaPlugin;

public final class Cooltime extends JavaPlugin {

    @Override
    public void onEnable() {
        // Plugin startup logic
        getLogger().info("플러그인이 활성화되었습니다.");
        getCommand("gui").setExecutor(new GUICommand());
        getServer().getPluginManager().registerEvents(new GUIListener(), this);
    }

    @Override
    public void onDisable() {
        // Plugin shutdown logic
        getLogger().info("플러그인이 비활성화되었습니다.");
    }
}

 

getCommand 부분은 plugin.yml에 등록한 /gui 명령어에 대해 그 명령어가 실행되었을 때 처리할 실제 코드를 설정하는 코드이다.

나는 gui 명령어의 처리 코드를 GUICommand 파일에 적어주었으니 그 파일의 코드를 사용한다고 설정하는 것이다.

 

getServer 부분은 서버의 이벤트 관리 시스템에 GUIListener 클래스의 이벤트 리스너를 등록하는 코드이다.

즉 유저가 GUI를 클릭하거나 열 때 발생하는 이벤트를 서버가 감지하게 한다.

registerEvents(listener, plugin)에서 listener는 이벤트 핸들러 클래스 즉 GUIListner 파일이 되고, plugin은 현재 플러그인 객체 즉 플러그인 메인 클래스 자신이다.

 

GUICommand.java

package org.blog.cooltime;

import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;

public class GUICommand implements CommandExecutor {

    @Override
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args)
    {
        if(!(sender instanceof Player)) return false;

        Player player = (Player) sender;
        Inventory gui = Bukkit.createInventory(null, 9, Component.text("스킬 GUI").color(NamedTextColor.GREEN));

        ItemStack skillItem = new ItemStack(Material.DIAMOND_BLOCK);
        ItemMeta meta = skillItem.getItemMeta();
        meta.displayName(Component.text("파워 스킬 사용").color(NamedTextColor.AQUA));
        skillItem.setItemMeta(meta);

        gui.setItem(4, skillItem);
        player.openInventory(gui);
        return true;
    }
}

 

CommandExecutor 인터페이스의 onCommand는 저번 강의에서 설명했으니 넘어가겠다.

if(!(sender instanceof Player)) return false; :

명령어를 콘솔에서 실행한 경우 무시하고 종료한다. GUI는 플레이어 전용이므로, 플레이어가 아닌 경우엔 아무 작업도 하지 않도록 한다.

 

Inventory gui = Bukkit.createInventory(...):

인벤토리 GUI를 생성한다. null은 이 GUI는 어떤 특정한 holder도 갖지 않도록 한다. 즉 인벤토리의 주인이 없다는 것이다. 

9는 슬롯의 수이고, Componet.text 부분은 GUI의 제목이다.

 

ItemStack skillItem = new ItemStack(Material.DIAMOND_BLOCK);:

GUI 안에 넣을 아이템을 생성한다.

 

ItemMeta meta = skillItem.getItemMeta();
meta.displayName(Component.text("파워 스킬 사용").color(NamedTextColor.AQUA));
skillItem.setItemMeta(meta);:
아이템에 이름 설정한다. setDisplayName은 deprecated이므로 displayName을 사용한다.

 

gui.setItem(4, skillItem);:

gui 슬롯에 5번째 칸에 해당 아이템을 넣는다.

인벤토리 슬롯은 0부터 시작한다.

 

player.openInventory(gui);

GUI를 해당 플레이어에게 보여준다.

 

GUIListener.java

package org.blog.cooltime;

import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import java.util.Map;
import org.bukkit.inventory.ItemStack;

import java.util.HashMap;
import java.util.UUID;

public class GUIListener implements Listener {

    private final Map<UUID, Long> cooldowns = new HashMap<>();
    private final long cooldownTime = 10 * 1000;

    @EventHandler
    public void onInventoryClick(InventoryClickEvent event)
    {
        if(!(event.getWhoClicked() instanceof Player)) return;
        Player player = (Player) event.getWhoClicked();

        if(event.getView().title().equals(Component.text("스킬 GUI").color(NamedTextColor.GREEN)))
        {
            event.setCancelled(true);

            ItemStack clicked = event.getCurrentItem();
            if(clicked == null || clicked.getType() != Material.DIAMOND_BLOCK) return;

            UUID uuid = player.getUniqueId();
            long now = System.currentTimeMillis();

            if(cooldowns.containsKey(uuid))
            {
                long lastUsed = cooldowns.get(uuid);
                long timeSince = now - lastUsed;
                if(timeSince < cooldownTime)
                {
                    long secondsLeft = (cooldownTime - timeSince) / 1000;
                    player.sendMessage(Component.text("쿨다운 중입니다!").color(NamedTextColor.RED));
                    return;
                }
            }

            player.sendMessage(Component.text("파워 스킬이 발동되었습니다!").color(NamedTextColor.GOLD));
            player.getWorld().strikeLightning(player.getLocation());

            cooldowns.put(uuid, now);
        }
    }
}

private final Map<UUID, Long> cooldowns = new HashMap<>();:

플레이어마다 마지막으로 스킬 쓴 시간을 저장하는 맵이다.

UUID는 플레이어 고유 ID이고 Long은 마지막으로 스킬을 쓴 시간(밀리초)이다.

 

private final long cooldownTime = 10 * 1000; // 10초:

쿨다운 시간 10초로 설정한다(1000ms x 10)

 

@EventHandler
public void onInventoryClick(InventoryClickEvent event):

인벤토리(GUI)에서 클릭하면 실행된다.

 

if(!(event.getWhoClicked() instanceof Player)) return;:

클릭한 사람이 플레이어가 아니면 무시한다(콘솔에서 실행 안됨)

 

Player player = (Player) event.getWhoClicked();:

클릭한 사람을 player로 형변환한다.

 

if(event.getView().title().equals(Component.text("스킬 GUI").color(NamedTextColor.GREEN))):

열린 인벤토리 제목이 "스킬 GUI"이고, 초록색일 때만 아래 코드를 실행한다.

즉 내가 만든 GUI인지 확인하는 조건이다.

 

event.setCancelled(true);:
아이템을 옮기거나 가져가지 못하게 막는다.

 

ItemStack clicked = event.getCurrentItem();
if(clicked == null || clicked.getType() != Material.DIAMOND_BLOCK) return;:

클릭한 아이템이 다이아몬드 블럭이 아닐 경우 무시한다.

 

UUID uuid = player.getUniqueId();
long now = System.currentTimeMillis();:

플레이어의 ID와 현재 시간을 저장한다.

 

if(cooldowns.containsKey(uuid))

플레이어가 이전에 스킬 쓴 적이 있으면

 

long lastUsed = cooldowns.get(uuid);
long timeSince = now - lastUsed;

마지막 사용 이후 지난 시간을 계산한다.

 

if(timeSince < cooldownTime)
{
    long secondsLeft = (cooldownTime - timeSince) / 1000;
    player.sendMessage(Component.text("쿨다운 중입니다!").color(NamedTextColor.RED));
    return;
}
아직 쿨다운 안 끝났으면 -> 빨간 메시지 보내고 리턴한다.

 

player.sendMessage(Component.text("파워 스킬이 발동되었습니다!").color(NamedTextColor.GOLD));
player.getWorld().strikeLightning(player.getLocation());

메시지를 보여주고, 플레이어 위치에 번개를 소환한다.

 

cooldowns.put(uuid, now);

지금 사용한 시간을 지정해서 다음 클릭 때 비교할 수 있게 한다.

 

plugin.yml 작성하기

name: cooltime
version: '1.0-SNAPSHOT'
main: org.blog.cooltime.Cooltime
api-version: '1.21'
commands:
  gui:
    description: 스킬 GUI를 엽니다.

 

테스트하기

gradle build에서 jar을 선택하고 빌드한다.

/gui를 입력하면 위와 같이 스킬 GUI가 열리고 다이아몬드 블럭이 있다.

 

클릭하면

파워 스킬이 발동되면서 번개가 친다.

 

10초가 되기 전에 다시 눌러보면

쿨다운 중이라는 메시지가 빨간색으로 출력된다.

반응형