- version
minecraft - 1.20.1forge - 47.0.19
- reference
- image
- 각 영상의 정보란에 images 를 다운 받을 수 있음.
Item, CreativeModeTab 추가하기
DeferredRegister는 게임 로딩 시점에 forge 가 알아서 object 를 등록 해준다.
이전에는 개발자가 eventListener 에 직접 등록해야 했다.
forge 대략적인 로딩 순서
mods파일을 탐색하여 모든.jar파일을 읽는다.resources/META-INF의mods.toml을 읽어 mod 의 기본 정보를 읽는다.- 이때
DeferredRegister가 생성되며 아직 등록한 object 는 생성되지 않는다. - 모든 mod 가 로딩된 후
DeferredRegister가 활성화 되어 추가한 object 를 생성한다. - 모든 초기화와 등록이 된 후 게임이 실행된다.
위 순서 중 생략한 부분이 있는데 추후 관련된 부분을 개발할 경우 추가할 예정.
Block 생성
forge 는 instance 생성을 피하고 method, member variable 을 static 으로 구현을 권장한다.
Block 을 구현할 때 BlockItem 도 등록하는 게 일반적이다.
Custom Recipes, Loot tables
custom block 을 생성할 때 다음 3가지 과정이 필요하다.
[backend]DeferredRegister에Block을 등록한다.[frontend]blockstates에 어떤 block model 을 사용할지 정의한다.[frontend]models/block에 block model 을 정의한다.
Advanced Items
Custom Item Metal Detector 를 생성했다.
이 Item을 사용하면 y축 + 방향으로 64칸을 탐색하여 사전에 정의한 Valuable block에 해당되면 그 좌표를 출력한다.
내구도 100으로 설정하고 한번 사용하면 1씩 감소하도록 했지만 감소되지 않는다.
추후 해결할 예정이다.
Advanced Block, Food Item
Sound Block 을 만들어 봤다.
우클릭하면 소리가 난다.
음식 Strawberry 를 만들어 봤다.
0.1의 확률로 이동속도가 10초 빨라진다.
지난 시간에 Metal Detector 를 사용하면 내구도가 감소하지 않은 이유는 Creative mode 에서는 체력, 배고픔, 내구도가 감소 하지 않기 때문이였다.
포만감이 가득 찬 경우 음식을 먹을 수 없다.
Fuel Item, Tooltips, Tags
custom Tags 를 만들때 #forge:ores 의 의미는 forge 에 등록된 모든 ores 이다.
즉, custom ores 도 여기에 포함된다.
다른 mod 에서 추가된 ores 도 포함되므로 사용하기 좋다.
Data generator
resource/assets 에는 custom object 에 대한 정보(.json)가 있다.
이는 수작업으로 만들고 있었는데 Provider 라는 기능이 있어 이것들을 생성해 준다.
덕분에 우리는 lang 과 texture 에 신경쓰면 된다.
Day 7(2)
BlockLootTables 를 만들때 sapphire 기반의 ores 의 drop 은 raw sapphire 가 된다.
이유는 ores 는 환경에 따라 형태가 다를 뿐 채굴 결과는 동일해야 한다는 마인크래프트 기본 로직이 있기 때문이다.
end stone sapphire, nether sapphire 는 sapphire 의 한 종류이며 drop 이 raw sapphire 라면 인벤토리 관리와 제련이 편리하기 때문이다.
Stairs, Slabs, etc.
영상에 나온대로 하지 않고 기존의 method 를 활용했다.
java 문법을 좀 더 공부한다면 NewItemModelProvider 를 간단하게 만들 수 있을듯...
2D Texture with 3D model, custom tools, LangProvider
resource/assets/lang/en_us.json 을 NewEnLangProvider 로 대체 하였다.
수작업으로 item 을 추가하는 부분을 자동으로 바꾸는 방법에 대해 생각해 볼 예정이다.
예를 들면 NewCreativeTabs 에 새로 추가한 item 을 수작업으로 등록해야 한다.
코드의 간결화를 위해 반드시 수작업은 반드시 바꿔야 한다.
custom armor
trimmable armor 란 치장 가능한 혹은 외형을 꾸밀 수 있는 방어구를 말한다.
성능에 영향을 주지 않는, 시각적으로만 변화를 줄 수 있는 방어구이다.
이에 관한 models/item .json 을 만들어야 하는데 관련 코드가 약간 복잡하다.
따라서 제공된 코드를 보고 분석하여 나만의 코드를 만들 예정이며 현재는 빈칸으로 남겨놨다.
full-suit set-bonus effect
제공받은 예시 코드를 refactoring 할 것.
server-side, client-side 구분하여 프로그래밍 할 수 있다.
event bus 에 등록하면 server-side 에서만 실행할 수 있다.
다만 어려운 점은 어떤 부분을 event bus 에 등록하는 것이냐 인데...
global loot modifier
global loot modifier 는 모든 block, entity 등 에 대한 전리품 획득을 수정할 수 있다.
provider 는 전리품 규칙을 json 으로 생성한다.
java object 에서 의미있는 데이터를 모아 통신가능한 형태(json 같이)로 저장한다.
이를 직렬화(serializer) 라 한다(반대는 역직렬화).
전리품 획득 상황이 나오면 앞서 설정한 규칙이 현 상황과 일치하면 새로운 규칙에 의한 전리품을 추가한다.
adding item to suspicious sand, custom crops
수상한 모래(suspicious sand) 에 전리품을 추가하는 방법을 배웠다.
영상에서는 새로운 loot modifier 를 만들었지만 나는 기존의 modifier 를 이용하려고 한다.
loot modifier 의 doApply 는 전리품을 추가하는 방식이다.
만약 다른 방식이 필요하다면 새로운 modifier 를 만드는게 좋다.
수상한 모래에 sapphire 를 추가하여 이를 확인하고 싶었는데 도저히 찾을 수가 없어 보류한다.
world 를 생성할 때 야생의 수상한 모래만 drop table 에 연결되고 creative mode 에서 얻은 수상한 모래에는 연결되지 않는다.
이름에 predicate 가 있으면 참 혹은 거짓을 반환하는 역할이다.
StatePropertiesPredicate 는 주어진 block 의 상태를 확인하여 properties 가 조건에 맞는지 검사하는 역할을 한다.
STRAWBERRY_CROP 는 BlockItem 을 만들 필요가 없어서 등록할 때 BLOCKS.register 를 사용했다.
block high crops
Day 14 와 다른 점은 2칸 높이의 작물 구현이다.
CornCropBlock
- SHAPE_BY_AGE : AGE 에 따른 block 의 시각적 모양
- randomTick : 밝기 조건, 윗 블럭 생성 조건 아니면 AGE 증가
- canSurvive : 아래 block 이 파괴되면 윗 block 도 파괴
- growCrops :
뼛가루(Bonemeal)를 사용하여 성장, currentAge 에 따라 윗 block 생성
noCollission : 충돌없이 지나갈 수 있음.
noOcclusion : 빛을 가리거나 막을 수 없음.
-
NewBlockStateProvidermakeStrawberryCrop, strawberryStates 를 refactoring 할 것. -
CornCropBlockrandomTick 을 refactoring 해볼 것.
custom flowers
commonSetup 은 게임 초기화 단계에서 생성과 렌더링에 관계없이 서버와 클라이언트 모두에 공통적으로 실행되어야 하는 로직을 실행하는데 사용된다.
예를 들어 아이템-블록 간의 연결 정의(예: flower-potted_flower), player 에게 특정 recipe 공개 등.
여기서는 반드시 event.enqueueWork 를 사용해야 하는데 병렬로 처리할 수 있기 때문이다.
commonSetup 은 모든 object 들이 registry 에 등록된 후 실행된다.
main thread 에서 실행하도록 보장한다.
아직 commonSetup 에 대해 이해하지 못함...
- refactoring:
NewBlockStateProvider,NewItemModelProvider
refactoring, bug fix
- refactoring:
NewBlockStateProvider,NewItemModelProvider,AbsCropBlock - fix:
NewModBlockLootTables
trades, villager, sound
- trades for villager and wandering trader
- custom villagers
- custom sounds
@Mod.EventBusSubscriber 가 붙은 class 는 해당 class 의 @SubscribeEvent 가 붙은 static method 를 forge 의 event system 에 등록한다.
위 annotation 이 붙었다고 해서 server-side 에서 실행되는 게 아니다.
event 에 따라서 어디서 실행될지 결정되는 것.
POI(Point Of Interest) 는 villager 가 상호작용할 수 있는 특정 위치를 말한다.
예를 들어 침대 POI 는 잠자는 장소이다.
이들은 POI 를 기준으로 경로를 탐색하고 활동한다.
주의점은 이미 POI type 을 가지고 있는 block 에 새로운 POI type 을 만들 수 없다.
assets/sounds.json 은 datagen 대상이 아니다.
이는 client assets 에 속하기 때문이다.
sapphire staff 는 gui, 1인칭, 3인칭에 따라 모델링이 다르다.
이런 복합 모델은 manual 정의를 한다.
이를 생성하는 datagen helper 는 없다.
마크의 사운드 확장자는 ogg 만 가능하다.
다른 확장자(예: mp3) 는 라이센스 문제가 발생할 수 있다.
disc
- custom disc
- Item 이 특수한 상호작용을 하는 경우(예: trimmable, sound event) 관련 tag 를 추가한다.
- tag 를 추가한다고 작동되는 게 아니라 동작도 구현해야 한다.
mod 를 개발할 때 일반적으로 DB 를 사용하지 않는다고 한다.
예를 들어 tooltip 을 가져오기 위해 DB 를 사용하는 건 복잡한 과정이라고 한다.
- feat:
NewEnLangProviderItem, Block 에 description 추가하는 method 구현
mob
-
custom mobs
-
EntityModelvsHierarchicalModel- 전자는 단순한, 후자는 복잡한 model 에 사용하기 적합하다.
- 전자는 entity 의 모든 part 를 수동으로 설정해야 됨, 후자는 root part 를 설정하면 나머지 부분이 이를 따라 움직인다.
- 요구되는 resource 크기 차이는 없다.
- resource 는 model 을 구성하는 part 가 많을수록 gpu 요구량이 커진다.
- 또한 생성해야 하는 model 이 많을수록 cpu, memory 요구량이 커진다.
-
NewEventBusvsNewClientEventBus- 전자는 entity 의 attribute 을 server, client 가 모두 알아야 해서 양쪽에서 실행된다.
- 후자는 entity 의 rendering 관련으로 server 에서 실행할 필요가 없다.
RhinoModel 을 보면 코드의 양이 많다.
이는 하드코딩이 아니며 BlockBench 에서 modeling 하고 java 로 변환한 것이다.
modeling 이 복잡할수록 양이 많아 진다.
minecraft 에서 Entity 는 living object(예: player, monster...), 움직이지만 생명체가 아닌 object(예: arrow, dropped item, falling block...), special object(예: orb, ...) 를 말한다.
attack animation
player 가 monster 에게 damage 를 주거나 받을 때 log file 생성하는 것이 더 효율적이다.
db 연결하여 저장하는 것은 비추천한다.
static initialization block 은 jvm 이 해당 class 를 load 할 때 단 한 번 실행된다.
이것은 static field 의 initialization 을 위해 존재하므로 이 다음으로 static block 이 실행된다.
class field 는 상속되지 않는다.
같은 이름을 사용할 경우 hiding 이라 부른다.
자식 class 에서 상속받은 method 를 사용하면 method 가 정의된 scope 내 field 를 참조한다.
즉 hiding 한 자식 class 의 field 를 참조하는 것이 아닌 부모 class 의 field 를 참조한다.
- feat:
NewItemModelProvider.trimmableArmorItem구현 - refactoring:
NewEvents,MetalDetectorItem,NewCreativeTabs
block entity
- custom block entity
NewBlocks 에서 blockItem 을 만드는 block 과 그렇지 않은 block 을 구분했다(BLOCKS, NO_BLOCK_ITEMS).
NewBlockLootTables.getKnownBlocks 는 loot table 을 만들어야 할 모든 custom block 들을 반환해야 한다.
기존은 BLOCKS 만 반환하여 오류가 발생했다.
NO_BLOCK_ITEMS 를 추가하여 해결했다.
RhinoModel.setupAnim 의 logic 중 idleAnimationState 에 idle, attack state 가 입력되고 있다.
논리적으로 문제가 있어 이를 수정했다.
recipe type
- custom recipe types
- bug:
GemPolishingStationBlockEntity에 문제가 생겨 polishing 작동이 되지 않는다. - refactoring:
GemPolishing관련 모든 코드(하드코딩, method 분리) - feat: custom recipe type 나머지 부분 완성
Day 22, 23
- Day 22, 23 일부 수정.
- Day 23 미흡한 부분 추가.
- fix:
GemPolishingStationBlockEntity.tick이 부분 중resetProgress()의 위치가 잘못 되어 수정함. - fix:
IntStream.of- >IntStream.range로 수정
JEI, block entity renderer
- JEI(just enough item) compatibility
- inventory, recipe 탐색을 도와주는 mod.
- block entity renderer
- gem polishing station 에 recipe 에 맞는 input 을 넣으면 station 에 icon 이 나온다.
- output 이 생성되면 icon 이 교체된다.
- 매 tick 마다 rendering 한다.
- rendering 에 난수가 사용되는데 이는 객체에 미묘한 불규칙성을 부여한다.
- 예를 들어 동일한 block 이라도 드랍시 난수 기반으로 약간 회전을 시키거나 반복되는 패턴을 피하기 위함이다.
- 모든 요소를 완벽히 계산하는 것 보다 난수를 이용해 효율적으로 계산할 수 있다.
NBT(Named Binary Tag) 게임 데이터를 저장하기 위해 NBT 형식을 사용한다.
chunk world 데이터를 관리하고 로드하는데 사용되는 가장 기본적인 구획 단위.
가로x세로x높이 16x16x256 블록.
해당 chunk 에 있는 모든 block의 위치, 종류, 상태와 entity, block entity 등을 파일로 저장한다.
GemPolishingStationBlockEntity 는 memory 에 해당 chunk 가 있을 때만 tick method 를 실행한다.
chunk 데이터가 release 되면 block entity 는 멈추고 상태를 저장한다.
chunk 를 release 하고 block entity 만 실행하게 할 수 없다.
영구적으로 block entity 를 실행하고 싶으면 강제로 chunk load 를 하여야 한다(IChunkloader 혹은 ForgeHooks.onChunkLoader 사용).
custom wood
- custom wood
- 영상에서 pine log -> pine planks 를 만드는 recipe 생략했다.
BlockBehaviour.Properties.strengthhardness높을수록 손이나 도구로 파괴하는데 오래 걸림.resistance높을수록 폭발 저항이 높음.
NewFlammableRotatedPillarBlock에 의해 불이 붙는 기능을 구현했더라도BlockTags.LOGS_THAT_BURNtag 가 없으면 동작하지 않는다.
renderType
cutout특정 pixel 을 투명하게 처리하여 leaves block 의 구멍 뚫린 texture 를 표현해준다. 주변 빛을 투과할 수 있다.solid기본값으로 불투명하고 단단한 것처럼 표현해준다.
Wrapper primitive type 을 객체로 변환하여 기존 type 에 없는 기능을 동적으로 추가할 수 있다.
// example
LazyOptional<IItemHandler> itemHandler = LazyOptional.of(() -> itemHandler);IItemHandler 는 핵심 기능이고 추가로 LazyOptional 에 의해 지연 초기화 및 안전성을 제공받는다.
custom sign
- custom sign
- 표지판, 걸이형 표지판 블럭
custom boat
- custom boat
- custom boat entity 정의
- entity 등록
- boat item 생성 및 등록
- boat renderer 생성 및 등록, texture asset 경로 추가
- provider 에 등록
throwable projectiles
- throwable projectiles
-
NewBoatEntity,NewChestBoatEntity중복되는 코드가 많지만 고민 끝에 지금 방법이 최선이라 판단했다.
bug fix, refactoring, ore generation
- ore generation
NewConfiguredFeaturesover-world, nether, end 차원에서 어떤 ore 가 sapphire ore 로 대체되는지, 어떤 크기로 생성할지에 대한 정보를 나타낸다.RuleTestore 가 어떤 block 을 대체할 수 있는지 검사하는 조건이다.
- refactoring
-
GemPolishingStationBlockEntity,GemPolishingStationMenu일부 hard-coding 제거.
-
- fix:
NewBlocks의DICEblock 을NO_BLOCK_ITEM으로 등록했더니 해결했다.
ore generation
- ore generation
NewOrePlacements의 method 는OrePlacements에서 가져왔으며 private 이라 그대로 가져왔다.Registryvanilla minecraft 의 중앙 데이터베이스이며 모든 object 의 id 를 갖고 있다.ForgeRegistriesvanilla registry 사용을 편리하게 하기 위한 helper class 이다.DeferredRegisterRegistry에 안전하게 등록 해준다.ResourceKey객체의 id 이며RegistryObject등록할 때 내부적으로 생성된다.ConfiguredFeature무엇을 생성할 것인가,PlacedFeature어디에 생성할 것인가,BiomeModifier어떤 biome 에 무엇을 어디에 생성할 지 결정한다.BiomeFeature아닌BiomeModifier이유 : 기존 vanilla system 을 건드리지 않고 추가하거나 변경하는 방식이다.
Data-Driven예를 들어 곡괭이에 관한 데이터를 코드와 함께 유지한다면 수정하고 나서 다시 compile 해야 된다.
하지만 데이터를 코드와 분리한다면 json 과 같은 데이터 파일을 수정하면 된다.
이런 방식으로 기존 데이터를 건드리지 않고 추가하거난 덮어쓰는 것으로 사용할 수 있다.
custom tree, tree generation
- custom tree
- tree generation
NewPlacedFeatures중pine tree를 등록할때 contextExtra 의extraChance값은 반드시1 / N꼴이어야 한다.
그렇지 않으면 exception 발생한다.
float (1 / N)이런 식으로 사용 가능하다.
- refactoring:
NewItemModelProviderfunctional interface사용했다.
refactoring
@FunctionalInterface는->와::를 지원해준다.
오직 하나의 추상 method 를 가진다.Builder.of는Builderpattern 이며ofmethod 는 보통 instance 를 생성하는 의미로 쓴다.
constructor 로 생성하는 것보다 더 가독성이 좋고 특정 타입의 객체를 반환함을 보장한다.advancements/recipes도전과제 recipe unlock 조건을 나타낸다.GemPolishingRecipe는 vanilla recipe format 과 다른 custom format 을 읽기 위해 구현한 custom recipe type 이다.
- refactoring
-
New(Hanging)SignBlockEntity,NewBlockEntities.(HANGING)SIGNS순환 의존성(circular dependency) 인줄 알았으나 아니었다. -
NewSignBlock들은new NewSignBlockEntity를 사용하고 있어서 높은 결합도를 가진다.
따라서 이를 수정했다.
-
RecipeBuilder, trunk placers
- trunk placer
- trunk placer(줄기 배치)
- foliage placer(잎사귀 배치)
foliage placer, features
- foliage placer
interface.default interface 는 기본적으로 public 을 원칙으로 한다.
default 를 붙여 method 를 구현하는 것을 interface method 의 확장이라고 한다.
예를 들어 기존의 interface 에 기능 추가를 위해 method 를 추가하면 이를 사용하는 모든 class 는 구현하지 않으면 compile error 가 발생한다.
interface 에 default 를 붙여 구현하면 기능 확장과 compile error 를 막을 수 있다.
interface 의 default 는 접근 제어자가 아닌 구현 class 에 구현을 강제하지 않는 의미이다.
공변 반환 타입(covariant return type) overriding method 의 return type 은 변하지 않거나 subclass 일 수 있다.
이는 builder pattern 에서 유용한데 만약 return type 을 강제로 한다면 chaining 시 불필요한 casting 이 필요하다.
- feat:
GemPolishingRecipeBuilderShapelessRecipeBuilder를 참고하여 구현했다.
private field 를 가지고 있어 상속하는 것이 불편했다.
나중에 또 다른 custom recipe 을 만든다면 그때 추상화할 예정이다.
custom biome
- custom biome
TerraBlenderworld generation 과 biome 추가를 위한 mod.- testBiome 동작 원리?
mods.toml 는 runtime 시 특정 mod 설치를 강요하여 안정적인 실행을 보장한다.
build.gradle 은 개발 환경을 위해 mod 를 설치한다.
즉, 둘의 목적이 다르다.
custom biome 을 추가하기 위해 terrablender mod 를 사용하려고 한다.
그래서 mods.toml 에 runtime 시 필요한 의존설 설정을 했다.
custom dimension
- Day 36
- 새롭게 정의된 Biome 은 새로 생성된 world 나 기존 world 중 탐험하지 않은 chunk 에 적용된다.
- fix:
runData오류가 발생한다. forge 에서terrablender를 인식하지 못해서 발생하는데 원인을 모르겠다...- forge version 을 높였더니 간단하게 해결되었다...
Item, Block, ...
이전에 공부한 내용을 정리할 예정이다.
ItemuseOnblock 을 직접 우클릭했을 때 호출된다.
반환할 때InteractionResult.PASS를 반환하면use가 호출된다.useitem 을 사용하고 나면 상태가 변한다.
예를 들어 빵 3개 -> 2개, 화살 10개 -> 0개.
따라서 사용 성공 여부와 성공 후 상태 변화를 저장하여 반환한다.
InteractionHand상호작용 시 어떤 손을 사용했는지를 나타낸다.BlockHitResultblock 과 상호작용 시 block 의 좌표, 어떤 방향, block 의 click position 를 담고 있다.ForgeHooks특정 이벤트가 발생하기 직전/후에 사용하는데 쓰이는 게이트웨이이다.
- refactoring
CornCropBlock,MetalDetectorItem - renaming
SoundBlock->NewSoundBlock
review
BlockBehaviour변하지 않는 정적 속성을 가진다.
시스템적으로 변경이 불가능하지는 않지만 변경하지 않는다.
만일 변경한다면 이 class 는 singleton 이라 관련된 모든 block 에 영향을 준다.
속성에 변화를 주고 싶다면BlockState혹은BlockEntity를 이용한다.BlockState변할 수 있는 동적 속성을 가진다.
객체 자체는Immutable이며 변경 시 새 객체를 생성한다.
review
Player플레이어를 나타내는 모든 데이터와 동작을 담고 있다.
review
ConfiguredModelblockState.json 에 사용될 model/block.json 과 이 model 이 world 에 표시될 때 적용될 설정을 담는 container 이다.BlockEntity와BaseEntityBlock- 동적인 기능을 block 에 추가하려면 전자를 상속 후 구현하면 된다.
- 이때 특정 block 에 앞서 구현한
BlockEntity를 연결해야 되는데 권장하는 방법은BaseEntityBlock을 상속 후 구현하는 것이다. - 물론 후자를 사용하지 않고 연결하는 방법이 있지만 그리 권장하지 않는다.
BlockBehaviour대부분 method 들의@deprecated이유
Mojang의 디자인 변경으로Blockclass 동작 방식에서BlockState객체로 바뀌었다.
BlockState는 block 의 동적 상태를 가지고 있어 상태 정보에 좀 더 쉽게 접근할 수 있다.
여기서@deprecated의미는Block(or BlockBehaviour)에서 직접 method 를 호출하지 말라는 의미이다.
따라서 overriding 해도 문제 없다.Client/Server-side코드가 실행되는 context 는 두가지뿐이다.
client/server 에서 실행될 때 반환 값으로InteractionResult의 의미는 조금 다르다.
review
tick메모리에 올라와 있는 chunk 의BlockEntity와Block의tick은integrated server thread에서 실행된다(single thread).
여러 thread 가 동시에 접근하여 데이터에 접근하면 race condition 이 발생해 단일 스레드로 실행한다.
하지만 latency 가 긴 작업은 비동기로 하여 멀티 스레딩을 사용한다.- block 이 설치될 때마다 해당 block 의 instance 를 생성하지 않는다.
singleton pattern 을 사용한다. - 하지만
BlockEntity와BlockState는 설치 위치마다 instance 를 생성한다.
정확히는BlockStateinstance 생성이 아닌 해당 block 이 가질 수 있는 type 의 index 를 저장한다. 예:CornCropBlock은AGE가 0-8 이며 이 중 해당되는 것에 대한 index 를 생성한다.
- refactoring:
PineTrunkPlacer.placeTrunk
review
AutoCloseable이 interface 를 구현하는 객체는 사용 후 release resource 를 반드시 해야 된다.
그렇지 않으면 resource leak 이 발생하여 문제가 발생하기 때문이다.
예를 들어, db connection, file stream, network socket, ...Levelclass 는 이 interface 를 구현하는데 resource leak 때문이 아니라
여러 thread 에서 접근을 하여 race condition 방지와 integrity 유지를 위함이다.
근데 굳이try-with-resource안해도 될듯...Level의 역할- data
- entity, tick
- player interaction, physics
- 실제
tick을 호출하는 건ServerLevel이다.
- feat:
Dice provider이걸 함수로 만들어서 하는게 복잡할 것 같고 하드 코딩하기엔 차라리 json 으로 남겨두는게 좋을 듯.
그래서 model/block.json 은 남기고 blockstates.json 은 provider 로 생성하기로 했다.
review
IntrinsicHolderTagsProvider<Block>custom block tag provider 는 이 class 를 상속하여 구현하면 된다.
BlockTagsProvider는 deprecation 이다.void addtags(HolderLookup.Provider pProvider)overriding 해 원하는 tag 를 block 혹은 item 에 추가하면 된다.
권장하는 방법은 pProvider 로 block 혹은 itemHolderLookup<>을 가져오고 원하는 object 의Holder<>를 가져와서 추가하면 된다.
- refactoring
GemPolishingCategory,NewBlockLootTables