개인 자료란 (JE)

  서버 커뮤니티

Profile SN1 대표칭호 없음
Profile

질문하기 플러그인

[완] 여러 버전의 NMS에 접근 가능한 라이브러리를 만들고 싶습니다

2022.09.29 조회 수 471 추천 수 1
게임버전 (JE) 관련없음 
게임버전 (BE) 관련없음 

플러그인에서 편하게 NMS의 메소드를 사용할 수 있도록 하는 라이브러리를 만들고 싶습니다.


이해를 돕기 위해 실제로 제가 맞닥뜨린 사례를 설명하겠습니다.


현재(1.19.2) 기준으로 Spigot API와 Paper API에서는 플레이어를 여러분이 원하는 대미지 유형(Bukkit의 DamageCause 혹은 NMS의 DamageSource)으로 죽일 수 없습니다. Damagable#damage(double amount, Entity source) 등의 메소드로 줄 수 있는 대미지의 유형은 항상 DamageCause#VOID 혹은 DamageCause#SUICIDE로 보입니다.

이를 해결하기 위해서는 NMS에 접근할 수 있어야 합니다. NMS의 LivingEntity에는 LivingEntity#hurt(DamageSource source, float amount)라는 메소드가 존재하거든요.


하지만 NMS의 LivingEntity를 상속받는 Player 오브젝트에 접근하기 위해서는 여러 난관이 존재합니다. 먼저 Bukkit API 버전명에 영향을 받는 CraftPlayer 오브젝트로 Player 오브젝트를 캐스트 해야 합니다.

net.minecraft.world.entity.player.Player getNMSPlayer(org.bukkit.entity.Player player) {
  return ((org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer) player).getHandle();
}

만약 여기서 블록(선인장 등)으로 인한 대미지를 주려면 추가로 역시나 Bukkit API 버전명에 영향을 받는 CraftEventFactory를 만들어야 합니다.

void hurtCactus(org.bukkit.entity.Player player, float amount) {
  org.bukkit.craftbukkit.v1_19_R1.event.CraftEventFactory.blockDamage = player.getWorld().getBlockAt(0, 0, 0);
  getNMSPlayer(player).hurt(DamageSource.CACTUS, amount);
}

현재는 이 방법이 유일하게 플레이어에게 특정 유형의 대미지를 주면서, 동시에 이 대미지로 플레이어를 죽일 수 있도록 하는 방법입니다.


그런데 위 방법은 보시다시피 import 하는 클래스 이름에 Bukkit의 특정 API 버전명(v1_19_R1)이 명시되어 있습니다.  코드 유지보수 측면에서 이렇게 특정 API 버전명에 의존해야 하는 방법은 별로 선호하지 않습니다.


그래서 생각한 것이 아예 NMS 관련 메소드만을 전담하는 라이브러리를 만드는 것입니다. 가령 아래와 같은 모양의 클래스가 있다면 어떨까요.

public interface NPlayer extends org.bukkit.entity.Player {
  public void hurt(CustomDamageSource source, float amount);
}
public interface CustomDamageSource {
  // Wrapper of NMS DamageSource, only implements some of it though
}

실제 플러그인에서 사용할 때에는 그냥 Bukkit의 PlayerNPlayer로 캐스트 한 후에 NPlayer#hurt(CustomDamageSource source, float amount)를 사용하면 되는 겁니다.


다만 이걸 어떻게 해야 여러 버전의 NMS/Bukkit API를 지원할 수 있게 만들 수 있는지를 모르겠습니다.




한줄 요약: Bukkit API의 Player를 상속하는 새로운 클래스를 제공하는 방법으로 여러 버전의 NMS/Bukkit API를 사용 가능케 하는 라이브러리를 어떻게 만들어야 할지 모르겠습니다.

6개의 댓글

코코냐
2022.09.29

테라리아 모드를 만들 때 유동적으로 클래스를 받아오기 위하여 리플렉션을 사용한 적이 있습니다. 자바에도 리플렉션 자체는 있는 것으로 알고있는데, 한번 활용해보는건 어떨까요?

작은거인
2022.09.29

마인크래프트 플러그인 개발을 할떄 많이 발생하는 문제입니다.

실제 서버들에서는 해결 방법이 대략 2가지가 사용되죠.

1. Abstraction - 각 버전마다 클래스 만들기

2. Reflection

 

Abstraction이 서버 성능 측면에서는 더 빠릅니다만, 각 버전에서 작동하게 하기 위해 더 많은 코드를 추가해야하죠.

Reflection은 직접 클래스를 찾는 과정을 거쳐야 하기 때문에 매우 느리다고 알려져 있습니다.

뭐 그래서 보통 nms로 하려는 일에 따라 달라지긴 합니다만, 개발 커뮤니티에서는 대부분 abstraction을 추천하더군요..

코코냐
2022.09.29
@작은거인

처음에 미리 찾아두는 방식이면 속도 개선이 되지 않을까 싶네요

0reo
2022.09.29

사용하는 부분에따라 다르겠지만 아예 구조가 달라서 reflection써도 깨지는일이 있더군요.. 그냥 버전별로 하시는게 나을거라 생각이 드네요

DDang_
2022.09.29

버킷 포크 해서 개발해보세욘

SN1
2022.09.30

각 버전마다 Gradle Subproject를 만드는 형식으로 진행하고 있습니다. 여기에 대해 또다른 문제가 생겼으므로, 별도의 질문 게시글을 작성하도록 하겠습니다.

 

여러분들의 조언 하나하나가 모두 도움이 되었습니다, 감사합니다.

뉴스 및 창작물
/files/thumbnails/761/908/003/262x150.crop.jpg?20241025153749

건축

서울 숭례문(崇禮門) 4

KHC

2024-10-25

2

/files/thumbnails/578/899/003/262x150.crop.jpg?20241010142350

건축

경주 월정교 1

KHC

2024-10-10

2

/files/thumbnails/219/899/003/262x150.crop.jpg?20241009200950

건축

송전탑+도시 2

dbasd12

2024-10-09

2

/files/thumbnails/246/898/003/262x150.crop.jpg?20241008102328

레드스톤

단다단 - 오토노케(オトノケ) | 마인크래프트 노트블럭 커버

노트블럭전문가

2024-10-08

1

/files/thumbnails/348/896/003/262x150.crop.jpg?20241006103035

디도스/봇테러등등을 낚는 방법 4

물귀신

2024-10-06

3