From 10b51f257fc0ea2318580f4064f4bba4d5511ab9 Mon Sep 17 00:00:00 2001 From: David Quintana Date: Wed, 11 Aug 2021 00:45:08 +0200 Subject: [PATCH] Redesign the tool system to rely on the new vanilla tags. (#7970) Changed: - ToolType is replaced with ToolAction, which can selectively represent which actions are supported by an item. - Added a canPerformAction method to query ToolActions. - Renamed canHarvestBlock to isCorrectToolForDrops to match vanilla. - Added TierSortingRegistry, which can be used to add custom tiers and supports sorting between vanilla or with other mods. How to use: For the breaking system there's 2 methods: - getDestroySpeed decides if an item *can* mine the block fast. Override if you have multiple tags or your item isn't a DiggerItem. - isCorrectToolForDrops decides if an item will get drops. Returning false will also slow down mining, regardless of getDestroySpeed. Override if you have type-dependant tiers or your item isn't a DiggerItem. For the tier system mods can just call `TierSortingRegistry.registerTier(tier, "name", List.of(lesser tiers), List.of(better tiers));` to register their tiers. There's helper methods to query tiers such as TierSortingRegsitry.isCorrectTierForDrops. The ToolActions solve 2 problems: 1. distinguishing which kind of digger an item can be 2. querying if the item can perform specific secondary actions. Any item can override `canPerformAction` to customize which actions it performs. --- .../minecraft/world/item/AxeItem.java.patch | 47 +- .../world/item/DiggerItem.java.patch | 26 +- .../minecraft/world/item/HoeItem.java.patch | 21 +- .../net/minecraft/world/item/Item.java.patch | 26 +- .../minecraft/world/item/ItemStack.java.patch | 2 +- .../world/item/PickaxeItem.java.patch | 12 + .../world/item/ShovelItem.java.patch | 18 +- .../minecraft/world/item/SwordItem.java.patch | 12 + .../net/minecraft/world/item/Tier.java.patch | 15 + .../net/minecraft/world/item/Tiers.java.patch | 9 + .../world/level/block/BambooBlock.java.patch | 9 + .../level/block/BambooSaplingBlock.java.patch | 11 + .../world/level/block/Block.java.patch | 30 +- .../block/state/BlockBehaviour.java.patch | 40 +- .../net/minecraftforge/common/ForgeHooks.java | 74 +--- .../common/ForgeInternalHandler.java | 6 + .../net/minecraftforge/common/ForgeMod.java | 1 - .../net/minecraftforge/common/ForgeTier.java | 114 +++++ .../minecraftforge/common/MinecraftForge.java | 5 +- .../java/net/minecraftforge/common/Tags.java | 4 + .../common/TierSortingRegistry.java | 414 ++++++++++++++++++ .../net/minecraftforge/common/ToolAction.java | 73 +++ .../minecraftforge/common/ToolActions.java | 88 ++++ .../net/minecraftforge/common/ToolType.java | 61 --- .../common/extensions/IForgeBlock.java | 65 +-- .../common/extensions/IForgeBlockState.java | 31 +- .../common/extensions/IForgeItem.java | 44 +- .../common/extensions/IForgeItemStack.java | 42 +- .../event/ForgeEventFactory.java | 6 +- .../event/world/BlockEvent.java | 18 +- .../debug/block/HarvestToolTest.java | 46 -- .../debug/item/TagBasedToolTypesTest.java | 76 ++++ src/test/resources/META-INF/mods.toml | 4 +- .../data/forge/item_tier_ordering.json | 3 + 34 files changed, 1012 insertions(+), 441 deletions(-) create mode 100644 patches/minecraft/net/minecraft/world/item/PickaxeItem.java.patch create mode 100644 patches/minecraft/net/minecraft/world/item/SwordItem.java.patch create mode 100644 patches/minecraft/net/minecraft/world/item/Tier.java.patch create mode 100644 patches/minecraft/net/minecraft/world/item/Tiers.java.patch create mode 100644 patches/minecraft/net/minecraft/world/level/block/BambooSaplingBlock.java.patch create mode 100644 src/main/java/net/minecraftforge/common/ForgeTier.java create mode 100644 src/main/java/net/minecraftforge/common/TierSortingRegistry.java create mode 100644 src/main/java/net/minecraftforge/common/ToolAction.java create mode 100644 src/main/java/net/minecraftforge/common/ToolActions.java delete mode 100644 src/main/java/net/minecraftforge/common/ToolType.java delete mode 100644 src/test/java/net/minecraftforge/debug/block/HarvestToolTest.java create mode 100644 src/test/java/net/minecraftforge/debug/item/TagBasedToolTypesTest.java create mode 100644 src/test/resources/data/forge/item_tier_ordering.json diff --git a/patches/minecraft/net/minecraft/world/item/AxeItem.java.patch b/patches/minecraft/net/minecraft/world/item/AxeItem.java.patch index 66579b4f0d..564913c63f 100644 --- a/patches/minecraft/net/minecraft/world/item/AxeItem.java.patch +++ b/patches/minecraft/net/minecraft/world/item/AxeItem.java.patch @@ -1,33 +1,38 @@ --- a/net/minecraft/world/item/AxeItem.java +++ b/net/minecraft/world/item/AxeItem.java -@@ -23,7 +_,7 @@ - protected static final Map f_150683_ = (new Builder()).put(Blocks.f_50011_, Blocks.f_50044_).put(Blocks.f_49999_, Blocks.f_50010_).put(Blocks.f_50043_, Blocks.f_50049_).put(Blocks.f_50004_, Blocks.f_50009_).put(Blocks.f_50015_, Blocks.f_50048_).put(Blocks.f_50003_, Blocks.f_50008_).put(Blocks.f_50013_, Blocks.f_50046_).put(Blocks.f_50001_, Blocks.f_50006_).put(Blocks.f_50014_, Blocks.f_50047_).put(Blocks.f_50002_, Blocks.f_50007_).put(Blocks.f_50012_, Blocks.f_50045_).put(Blocks.f_50000_, Blocks.f_50005_).put(Blocks.f_50686_, Blocks.f_50687_).put(Blocks.f_50688_, Blocks.f_50689_).put(Blocks.f_50695_, Blocks.f_50696_).put(Blocks.f_50697_, Blocks.f_50698_).build(); - - public AxeItem(Tier p_40521_, float p_40522_, float p_40523_, Item.Properties p_40524_) { -- super(p_40522_, p_40523_, p_40521_, BlockTags.f_144280_, p_40524_); -+ super(p_40522_, p_40523_, p_40521_, BlockTags.f_144280_, p_40524_.addToolType(net.minecraftforge.common.ToolType.AXE, p_40521_.m_6604_())); - } - - public InteractionResult m_6225_(UseOnContext p_40529_) { -@@ -31,7 +_,7 @@ +@@ -31,11 +_,9 @@ BlockPos blockpos = p_40529_.m_8083_(); Player player = p_40529_.m_43723_(); BlockState blockstate = level.m_8055_(blockpos); - Optional optional = this.m_150690_(blockstate); -+ Optional optional = Optional.ofNullable(blockstate.getToolModifiedState(level, blockpos, player, p_40529_.m_43722_(), net.minecraftforge.common.ToolType.AXE)); - Optional optional1 = WeatheringCopper.m_154899_(blockstate); - Optional optional2 = Optional.ofNullable(HoneycombItem.f_150864_.get().get(blockstate.m_60734_())).map((p_150694_) -> { - return p_150694_.m_152465_(blockstate); -@@ -67,6 +_,12 @@ - } else { - return InteractionResult.PASS; +- Optional optional1 = WeatheringCopper.m_154899_(blockstate); +- Optional optional2 = Optional.ofNullable(HoneycombItem.f_150864_.get().get(blockstate.m_60734_())).map((p_150694_) -> { +- return p_150694_.m_152465_(blockstate); +- }); ++ Optional optional = Optional.ofNullable(blockstate.getToolModifiedState(level, blockpos, player, p_40529_.m_43722_(), net.minecraftforge.common.ToolActions.AXE_STRIP)); ++ Optional optional1 = Optional.ofNullable(blockstate.getToolModifiedState(level, blockpos, player, p_40529_.m_43722_(), net.minecraftforge.common.ToolActions.AXE_SCRAPE)); ++ Optional optional2 = Optional.ofNullable(blockstate.getToolModifiedState(level, blockpos, player, p_40529_.m_43722_(), net.minecraftforge.common.ToolActions.AXE_WAX_OFF)); + ItemStack itemstack = p_40529_.m_43722_(); + Optional optional3 = Optional.empty(); + if (optional.isPresent()) { +@@ -69,9 +_,20 @@ } -+ } -+ + } + + @javax.annotation.Nullable + public static BlockState getAxeStrippingState(BlockState originalState) { + Block block = f_150683_.get(originalState.m_60734_()); + return block != null ? block.m_49966_().m_61124_(RotatedPillarBlock.f_55923_, originalState.m_61143_(RotatedPillarBlock.f_55923_)) : null; - } - ++ } ++ private Optional m_150690_(BlockState p_150691_) { + return Optional.ofNullable(f_150683_.get(p_150691_.m_60734_())).map((p_150689_) -> { + return p_150689_.m_49966_().m_61124_(RotatedPillarBlock.f_55923_, p_150691_.m_61143_(RotatedPillarBlock.f_55923_)); + }); ++ } ++ ++ @Override ++ public boolean canPerformAction(ItemStack stack, net.minecraftforge.common.ToolAction toolAction) { ++ return net.minecraftforge.common.ToolActions.DEFAULT_AXE_ACTIONS.contains(toolAction); + } + } diff --git a/patches/minecraft/net/minecraft/world/item/DiggerItem.java.patch b/patches/minecraft/net/minecraft/world/item/DiggerItem.java.patch index 48d99968eb..5e55b7f321 100644 --- a/patches/minecraft/net/minecraft/world/item/DiggerItem.java.patch +++ b/patches/minecraft/net/minecraft/world/item/DiggerItem.java.patch @@ -1,10 +1,26 @@ --- a/net/minecraft/world/item/DiggerItem.java +++ b/net/minecraft/world/item/DiggerItem.java -@@ -33,6 +_,7 @@ +@@ -61,7 +_,11 @@ + return this.f_40981_; } - public float m_8102_(ItemStack p_41004_, BlockState p_41005_) { -+ if (getToolTypes(p_41004_).stream().anyMatch(e -> p_41005_.isToolEffective(e))) return f_40980_; - return this.f_40979_.m_8110_(p_41005_.m_60734_()) ? this.f_40980_ : 1.0F; ++ @Deprecated // FORGE: Use stack sensitive variant below + public boolean m_8096_(BlockState p_150816_) { ++ if (net.minecraftforge.common.TierSortingRegistry.isTierSorted(m_43314_())) { ++ return net.minecraftforge.common.TierSortingRegistry.isCorrectTierForDrops(m_43314_(), p_150816_) && p_150816_.m_60620_(this.f_40979_); ++ } + int i = this.m_43314_().m_6604_(); + if (i < 3 && p_150816_.m_60620_(BlockTags.f_144284_)) { + return false; +@@ -70,5 +_,11 @@ + } else { + return i < 1 && p_150816_.m_60620_(BlockTags.f_144286_) ? false : p_150816_.m_60620_(this.f_40979_); + } ++ } ++ ++ // FORGE START ++ @Override ++ public boolean isCorrectToolForDrops(ItemStack stack, BlockState state) { ++ return state.m_60620_(f_40979_) && net.minecraftforge.common.TierSortingRegistry.isCorrectTierForDrops(m_43314_(), state); } - + } diff --git a/patches/minecraft/net/minecraft/world/item/HoeItem.java.patch b/patches/minecraft/net/minecraft/world/item/HoeItem.java.patch index 6d1ba81783..7e7bf03829 100644 --- a/patches/minecraft/net/minecraft/world/item/HoeItem.java.patch +++ b/patches/minecraft/net/minecraft/world/item/HoeItem.java.patch @@ -1,14 +1,6 @@ --- a/net/minecraft/world/item/HoeItem.java +++ b/net/minecraft/world/item/HoeItem.java -@@ -26,13 +_,16 @@ - }, m_150849_(Blocks.f_50493_.m_49966_(), Items.f_151017_)))); - - public HoeItem(Tier p_41336_, int p_41337_, float p_41338_, Item.Properties p_41339_) { -- super((float)p_41337_, p_41338_, p_41336_, BlockTags.f_144281_, p_41339_); -+ super((float)p_41337_, p_41338_, p_41336_, BlockTags.f_144281_, p_41339_.addToolType(net.minecraftforge.common.ToolType.HOE, p_41336_.m_6604_())); - } - - public InteractionResult m_6225_(UseOnContext p_41341_) { +@@ -33,6 +_,9 @@ Level level = p_41341_.m_43725_(); BlockPos blockpos = p_41341_.m_8083_(); Pair, Consumer> pair = f_41332_.get(level.m_8055_(blockpos).m_60734_()); @@ -28,3 +20,14 @@ } public static Consumer m_150858_(BlockState p_150859_) { +@@ -72,5 +_,10 @@ + + public static boolean m_150856_(UseOnContext p_150857_) { + return p_150857_.m_43719_() != Direction.DOWN && p_150857_.m_43725_().m_8055_(p_150857_.m_8083_().m_7494_()).m_60795_(); ++ } ++ ++ @Override ++ public boolean canPerformAction(ItemStack stack, net.minecraftforge.common.ToolAction toolAction) { ++ return net.minecraftforge.common.ToolActions.DEFAULT_HOE_ACTIONS.contains(toolAction); + } + } diff --git a/patches/minecraft/net/minecraft/world/item/Item.java.patch b/patches/minecraft/net/minecraft/world/item/Item.java.patch index 829e90c492..2b5b8edd5d 100644 --- a/patches/minecraft/net/minecraft/world/item/Item.java.patch +++ b/patches/minecraft/net/minecraft/world/item/Item.java.patch @@ -12,13 +12,12 @@ protected static final UUID f_41374_ = UUID.fromString("CB3F55D3-645C-4F38-A497-9C13A33DB5CF"); protected static final UUID f_41375_ = UUID.fromString("FA233E1C-4180-4865-B01B-BCCE9785ACA3"); public static final int f_150884_ = 64; -@@ -92,7 +_,9 @@ +@@ -92,7 +_,8 @@ f_150883_.error("Item classes should end with Item and {} doesn't.", (Object)s); } } - + this.canRepair = p_41383_.canRepair; -+ this.toolClasses.putAll(p_41383_.toolClasses); + initClient(); } @@ -77,7 +76,7 @@ CreativeModeTab creativemodetab = this.m_41471_(); return creativemodetab != null && (p_41390_ == CreativeModeTab.f_40754_ || p_41390_ == creativemodetab); } -@@ -325,12 +_,37 @@ +@@ -325,12 +_,26 @@ return false; } @@ -86,7 +85,6 @@ return ImmutableMultimap.of(); } -+ private final java.util.Map toolClasses = Maps.newHashMap(); + private final net.minecraftforge.common.util.ReverseTagWrapper reverseTags = new net.minecraftforge.common.util.ReverseTagWrapper<>(this, net.minecraft.tags.ItemTags::m_13193_); + protected final boolean canRepair; + @@ -96,16 +94,6 @@ + } + + @Override -+ public java.util.Set getToolTypes(ItemStack stack) { -+ return toolClasses.keySet(); -+ } -+ -+ @Override -+ public int getHarvestLevel(ItemStack stack, net.minecraftforge.common.ToolType tool, @Nullable Player player, @Nullable BlockState blockState) { -+ return toolClasses.getOrDefault(tool, -1); -+ } -+ -+ @Override + public java.util.Set getTags() { + return reverseTags.getTagNames(); + } @@ -148,16 +136,15 @@ public static class Properties { int f_41478_ = 64; int f_41479_; -@@ -379,6 +_,8 @@ +@@ -379,6 +_,7 @@ Rarity f_41482_ = Rarity.COMMON; FoodProperties f_41483_; boolean f_41484_; + private boolean canRepair = true; -+ private java.util.Map toolClasses = Maps.newHashMap(); public Item.Properties m_41489_(FoodProperties p_41490_) { this.f_41483_ = p_41490_; -@@ -421,6 +_,16 @@ +@@ -421,6 +_,11 @@ public Item.Properties m_41486_() { this.f_41484_ = true; @@ -166,11 +153,6 @@ + + public Item.Properties setNoRepair() { + canRepair = false; -+ return this; -+ } -+ -+ public Item.Properties addToolType(net.minecraftforge.common.ToolType type, int level) { -+ toolClasses.put(type, level); return this; } } diff --git a/patches/minecraft/net/minecraft/world/item/ItemStack.java.patch b/patches/minecraft/net/minecraft/world/item/ItemStack.java.patch index fb1123ac5c..573ea8ac89 100644 --- a/patches/minecraft/net/minecraft/world/item/ItemStack.java.patch +++ b/patches/minecraft/net/minecraft/world/item/ItemStack.java.patch @@ -165,7 +165,7 @@ public boolean m_41735_(BlockState p_41736_) { - return this.m_41720_().m_8096_(p_41736_); -+ return this.m_41720_().canHarvestBlock(this, p_41736_); ++ return this.m_41720_().isCorrectToolForDrops(this, p_41736_); } public InteractionResult m_41647_(Player p_41648_, LivingEntity p_41649_, InteractionHand p_41650_) { diff --git a/patches/minecraft/net/minecraft/world/item/PickaxeItem.java.patch b/patches/minecraft/net/minecraft/world/item/PickaxeItem.java.patch new file mode 100644 index 0000000000..3cc3068a54 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/item/PickaxeItem.java.patch @@ -0,0 +1,12 @@ +--- a/net/minecraft/world/item/PickaxeItem.java ++++ b/net/minecraft/world/item/PickaxeItem.java +@@ -6,4 +_,9 @@ + public PickaxeItem(Tier p_42961_, int p_42962_, float p_42963_, Item.Properties p_42964_) { + super((float)p_42962_, p_42963_, p_42961_, BlockTags.f_144282_, p_42964_); + } ++ ++ @Override ++ public boolean canPerformAction(ItemStack stack, net.minecraftforge.common.ToolAction toolAction) { ++ return net.minecraftforge.common.ToolActions.DEFAULT_PICKAXE_ACTIONS.contains(toolAction); ++ } + } diff --git a/patches/minecraft/net/minecraft/world/item/ShovelItem.java.patch b/patches/minecraft/net/minecraft/world/item/ShovelItem.java.patch index 5e4b2b8e49..1ce4aea8ef 100644 --- a/patches/minecraft/net/minecraft/world/item/ShovelItem.java.patch +++ b/patches/minecraft/net/minecraft/world/item/ShovelItem.java.patch @@ -1,27 +1,18 @@ --- a/net/minecraft/world/item/ShovelItem.java +++ b/net/minecraft/world/item/ShovelItem.java -@@ -21,7 +_,7 @@ - protected static final Map f_43110_ = Maps.newHashMap((new Builder()).put(Blocks.f_50440_, Blocks.f_152481_.m_49966_()).put(Blocks.f_50493_, Blocks.f_152481_.m_49966_()).put(Blocks.f_50599_, Blocks.f_152481_.m_49966_()).put(Blocks.f_50546_, Blocks.f_152481_.m_49966_()).put(Blocks.f_50195_, Blocks.f_152481_.m_49966_()).put(Blocks.f_152549_, Blocks.f_152481_.m_49966_()).build()); - - public ShovelItem(Tier p_43114_, float p_43115_, float p_43116_, Item.Properties p_43117_) { -- super(p_43115_, p_43116_, p_43114_, BlockTags.f_144283_, p_43117_); -+ super(p_43115_, p_43116_, p_43114_, BlockTags.f_144283_, p_43117_.addToolType(net.minecraftforge.common.ToolType.SHOVEL, p_43114_.m_6604_())); - } - - public InteractionResult m_6225_(UseOnContext p_43119_) { @@ -32,9 +_,9 @@ return InteractionResult.PASS; } else { Player player = p_43119_.m_43723_(); - BlockState blockstate1 = f_43110_.get(blockstate.m_60734_()); -+ BlockState blockstate1 = blockstate.getToolModifiedState(level, blockpos, player, p_43119_.m_43722_(), net.minecraftforge.common.ToolType.SHOVEL); ++ BlockState blockstate1 = blockstate.getToolModifiedState(level, blockpos, player, p_43119_.m_43722_(), net.minecraftforge.common.ToolActions.SHOVEL_FLATTEN); BlockState blockstate2 = null; - if (blockstate1 != null && level.m_8055_(blockpos.m_7494_()).m_60795_()) { + if (blockstate1 != null && level.m_46859_(blockpos.m_7494_())) { level.m_5594_(player, blockpos, SoundEvents.f_12406_, SoundSource.BLOCKS, 1.0F, 1.0F); blockstate2 = blockstate1; } else if (blockstate.m_60734_() instanceof CampfireBlock && blockstate.m_61143_(CampfireBlock.f_51227_)) { -@@ -61,5 +_,10 @@ +@@ -61,5 +_,15 @@ return InteractionResult.PASS; } } @@ -30,5 +21,10 @@ + @javax.annotation.Nullable + public static BlockState getShovelPathingState(BlockState originalState) { + return f_43110_.get(originalState.m_60734_()); ++ } ++ ++ @Override ++ public boolean canPerformAction(ItemStack stack, net.minecraftforge.common.ToolAction toolAction) { ++ return net.minecraftforge.common.ToolActions.DEFAULT_SHOVEL_ACTIONS.contains(toolAction); } } diff --git a/patches/minecraft/net/minecraft/world/item/SwordItem.java.patch b/patches/minecraft/net/minecraft/world/item/SwordItem.java.patch new file mode 100644 index 0000000000..7980a356f9 --- /dev/null +++ b/patches/minecraft/net/minecraft/world/item/SwordItem.java.patch @@ -0,0 +1,12 @@ +--- a/net/minecraft/world/item/SwordItem.java ++++ b/net/minecraft/world/item/SwordItem.java +@@ -70,4 +_,9 @@ + public Multimap m_7167_(EquipmentSlot p_43274_) { + return p_43274_ == EquipmentSlot.MAINHAND ? this.f_43267_ : super.m_7167_(p_43274_); + } ++ ++ @Override ++ public boolean canPerformAction(ItemStack stack, net.minecraftforge.common.ToolAction toolAction) { ++ return net.minecraftforge.common.ToolActions.DEFAULT_SWORD_ACTIONS.contains(toolAction); ++ } + } diff --git a/patches/minecraft/net/minecraft/world/item/Tier.java.patch b/patches/minecraft/net/minecraft/world/item/Tier.java.patch new file mode 100644 index 0000000000..0b6b3b1dda --- /dev/null +++ b/patches/minecraft/net/minecraft/world/item/Tier.java.patch @@ -0,0 +1,15 @@ +--- a/net/minecraft/world/item/Tier.java ++++ b/net/minecraft/world/item/Tier.java +@@ -9,9 +_,12 @@ + + float m_6631_(); + ++ @Deprecated // FORGE: Use TierSortingRegistry to define which tiers are better than others + int m_6604_(); + + int m_6601_(); + + Ingredient m_6282_(); ++ ++ @javax.annotation.Nullable default net.minecraft.tags.Tag getTag() { return null; } + } diff --git a/patches/minecraft/net/minecraft/world/item/Tiers.java.patch b/patches/minecraft/net/minecraft/world/item/Tiers.java.patch new file mode 100644 index 0000000000..588cab692e --- /dev/null +++ b/patches/minecraft/net/minecraft/world/item/Tiers.java.patch @@ -0,0 +1,9 @@ +--- a/net/minecraft/world/item/Tiers.java ++++ b/net/minecraft/world/item/Tiers.java +@@ -64,4 +_,6 @@ + public Ingredient m_6282_() { + return this.f_43326_.m_13971_(); + } ++ ++ @javax.annotation.Nullable public net.minecraft.tags.Tag getTag() { return net.minecraftforge.common.ForgeHooks.getTagFromVanillaTier(this); } + } diff --git a/patches/minecraft/net/minecraft/world/level/block/BambooBlock.java.patch b/patches/minecraft/net/minecraft/world/level/block/BambooBlock.java.patch index 06fd879783..b6f2f60e6a 100644 --- a/patches/minecraft/net/minecraft/world/level/block/BambooBlock.java.patch +++ b/patches/minecraft/net/minecraft/world/level/block/BambooBlock.java.patch @@ -14,3 +14,12 @@ } } +@@ -171,7 +_,7 @@ + } + + public float m_5880_(BlockState p_48901_, Player p_48902_, BlockGetter p_48903_, BlockPos p_48904_) { +- return p_48902_.m_21205_().m_41720_() instanceof SwordItem ? 1.0F : super.m_5880_(p_48901_, p_48902_, p_48903_, p_48904_); ++ return p_48902_.m_21205_().canPerformAction(net.minecraftforge.common.ToolActions.SWORD_DIG) ? 1.0F : super.m_5880_(p_48901_, p_48902_, p_48903_, p_48904_); + } + + protected void m_48910_(BlockState p_48911_, Level p_48912_, BlockPos p_48913_, Random p_48914_, int p_48915_) { diff --git a/patches/minecraft/net/minecraft/world/level/block/BambooSaplingBlock.java.patch b/patches/minecraft/net/minecraft/world/level/block/BambooSaplingBlock.java.patch new file mode 100644 index 0000000000..7ddd49cf1f --- /dev/null +++ b/patches/minecraft/net/minecraft/world/level/block/BambooSaplingBlock.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/block/BambooSaplingBlock.java ++++ b/net/minecraft/world/level/block/BambooSaplingBlock.java +@@ -77,7 +_,7 @@ + } + + public float m_5880_(BlockState p_48981_, Player p_48982_, BlockGetter p_48983_, BlockPos p_48984_) { +- return p_48982_.m_21205_().m_41720_() instanceof SwordItem ? 1.0F : super.m_5880_(p_48981_, p_48982_, p_48983_, p_48984_); ++ return p_48982_.m_21205_().canPerformAction(net.minecraftforge.common.ToolActions.SWORD_DIG) ? 1.0F : super.m_5880_(p_48981_, p_48982_, p_48983_, p_48984_); + } + + protected void m_48972_(Level p_48973_, BlockPos p_48974_) { diff --git a/patches/minecraft/net/minecraft/world/level/block/Block.java.patch b/patches/minecraft/net/minecraft/world/level/block/Block.java.patch index 31e4e24a82..307efd5de4 100644 --- a/patches/minecraft/net/minecraft/world/level/block/Block.java.patch +++ b/patches/minecraft/net/minecraft/world/level/block/Block.java.patch @@ -13,15 +13,6 @@ private static final LoadingCache f_49785_ = CacheBuilder.newBuilder().maximumSize(512L).weakKeys().build(new CacheLoader() { public Boolean load(VoxelShape p_49972_) { return !Shapes.m_83157_(Shapes.m_83144_(), p_49972_, BooleanOp.f_82687_); -@@ -169,6 +_,8 @@ - super(p_49795_); - StateDefinition.Builder builder = new StateDefinition.Builder<>(this); - this.m_7926_(builder); -+ this.harvestLevel = p_49795_.getHarvestLevel(); -+ this.harvestTool = p_49795_.getHarvestTool(); - this.f_49792_ = builder.m_61101_(Block::m_49966_, BlockState::new); - this.m_49959_(this.f_49792_.m_61090_()); - if (SharedConstants.f_136183_) { @@ -177,7 +_,7 @@ f_49790_.error("Block classes should end with Block and {} doesn't.", (Object)s); } @@ -100,15 +91,13 @@ } public void m_5871_(ItemStack p_49816_, @Nullable BlockGetter p_49817_, List p_49818_, TooltipFlag p_49819_) { -@@ -486,6 +_,100 @@ +@@ -486,6 +_,81 @@ protected ImmutableMap m_152458_(Function p_152459_) { return this.f_49792_.m_61056_().stream().collect(ImmutableMap.toImmutableMap(Function.identity(), p_152459_)); } + + /* ======================================== FORGE START =====================================*/ + protected Random RANDOM = new Random(); -+ private net.minecraftforge.common.ToolType harvestTool; -+ private int harvestLevel; + private final net.minecraftforge.common.util.ReverseTagWrapper reverseTags = new net.minecraftforge.common.util.ReverseTagWrapper<>(this, BlockTags::m_13115_); + private Object renderProperties; + @@ -133,17 +122,6 @@ + public void initializeClient(java.util.function.Consumer consumer) { + } + -+ @Nullable -+ @Override -+ public net.minecraftforge.common.ToolType getHarvestTool(BlockState state) { -+ return harvestTool; //TODO: RE-Evaluate -+ } -+ -+ @Override -+ public int getHarvestLevel(BlockState state) { -+ return harvestLevel; //TODO: RE-Evaluate -+ } -+ + @Override + public boolean canSustainPlant(BlockState state, BlockGetter world, BlockPos pos, Direction facing, net.minecraftforge.common.IPlantable plantable) { + BlockState plant = plantable.getPlant(world, pos.m_142300_(facing)); @@ -191,12 +169,6 @@ + return reverseTags.getTagNames(); + } + -+ static { -+ net.minecraftforge.common.ForgeHooks.setBlockToolSetter((block, tool, level) -> { -+ block.harvestTool = tool; -+ block.harvestLevel = level; -+ }); -+ } + /* ========================================= FORGE END ======================================*/ public static final class BlockStatePairKey { diff --git a/patches/minecraft/net/minecraft/world/level/block/state/BlockBehaviour.java.patch b/patches/minecraft/net/minecraft/world/level/block/state/BlockBehaviour.java.patch index 1f27f8d8fd..778b4391b9 100644 --- a/patches/minecraft/net/minecraft/world/level/block/state/BlockBehaviour.java.patch +++ b/patches/minecraft/net/minecraft/world/level/block/state/BlockBehaviour.java.patch @@ -43,7 +43,7 @@ } else { - int i = p_60467_.m_36298_(p_60466_) ? 30 : 100; - return p_60467_.m_36281_(p_60466_) / f / (float)i; -+ int i = net.minecraftforge.common.ForgeHooks.canHarvestBlock(p_60466_, p_60467_, p_60468_, p_60469_) ? 30 : 100; ++ int i = net.minecraftforge.common.ForgeHooks.isCorrectToolForDrops(p_60466_, p_60467_) ? 30 : 100; + return p_60467_.getDigSpeed(p_60466_, p_60469_) / f / (float)i; } } @@ -97,12 +97,10 @@ public BlockState m_60717_(Rotation p_60718_) { return this.m_60734_().m_6843_(this.m_7160_(), p_60718_); } -@@ -821,8 +_,11 @@ +@@ -821,8 +_,9 @@ ResourceLocation f_60894_; boolean f_60895_ = true; boolean f_60896_; -+ private int harvestLevel = -1; -+ private net.minecraftforge.common.ToolType harvestTool; + private java.util.function.Supplier lootTableSupplier; BlockBehaviour.StateArgumentPredicate> f_60897_ = (p_60935_, p_60936_, p_60937_, p_60938_) -> { - return p_60935_.m_60783_(p_60936_, p_60937_, Direction.UP) && p_60935_.m_60791_() < 14; @@ -110,40 +108,6 @@ }; BlockBehaviour.StatePredicate f_60898_ = (p_60985_, p_60986_, p_60987_) -> { return p_60985_.m_60767_().m_76337_() && p_60985_.m_60838_(p_60986_, p_60987_); -@@ -882,6 +_,8 @@ - blockbehaviour$properties.f_60895_ = p_60927_.f_60439_.f_60895_; - blockbehaviour$properties.f_60896_ = p_60927_.f_60439_.f_60896_; - blockbehaviour$properties.f_60889_ = p_60927_.f_60439_.f_60889_; -+ blockbehaviour$properties.harvestLevel = p_60927_.f_60439_.harvestLevel; -+ blockbehaviour$properties.harvestTool = p_60927_.f_60439_.harvestTool; - return blockbehaviour$properties; - } - -@@ -896,6 +_,24 @@ - return this; - } - -+ public BlockBehaviour.Properties harvestLevel(int harvestLevel) { -+ this.harvestLevel = harvestLevel; -+ return this; -+ } -+ -+ public BlockBehaviour.Properties harvestTool(net.minecraftforge.common.ToolType harvestTool) { -+ this.harvestTool = harvestTool; -+ return this; -+ } -+ -+ public int getHarvestLevel() { -+ return this.harvestLevel; -+ } -+ -+ public net.minecraftforge.common.ToolType getHarvestTool() { -+ return this.harvestTool; -+ } -+ - public BlockBehaviour.Properties m_60911_(float p_60912_) { - this.f_60891_ = p_60912_; - return this; @@ -949,9 +_,15 @@ return this; } diff --git a/src/main/java/net/minecraftforge/common/ForgeHooks.java b/src/main/java/net/minecraftforge/common/ForgeHooks.java index 3f5c54e436..b2c5758d69 100644 --- a/src/main/java/net/minecraftforge/common/ForgeHooks.java +++ b/src/main/java/net/minecraftforge/common/ForgeHooks.java @@ -56,13 +56,15 @@ import javax.annotation.Nullable; import it.unimi.dsi.fastutil.longs.LongSet; import net.minecraft.advancements.Advancement; -import net.minecraft.world.level.block.Block; +import net.minecraft.tags.BlockTags; import net.minecraft.world.entity.ai.attributes.Attribute; import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.DefaultAttributes; import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.*; import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.storage.loot.LootContext; import net.minecraft.world.level.storage.loot.LootTable; import net.minecraft.world.level.storage.loot.LootTables; @@ -84,13 +86,6 @@ import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.inventory.AnvilMenu; import net.minecraft.world.Container; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.BucketItem; -import net.minecraft.world.item.EnchantedBookItem; -import net.minecraft.world.item.PotionItem; -import net.minecraft.world.item.SpawnEggItem; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.TippedArrowItem; import net.minecraft.world.item.context.UseOnContext; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; @@ -178,7 +173,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; -import org.apache.logging.log4j.util.TriConsumer; import net.minecraft.ChatFormatting; import net.minecraft.core.Direction; @@ -193,7 +187,6 @@ import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.state.pattern.BlockInWorld; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.dimension.LevelStem; @@ -219,50 +212,12 @@ public class ForgeHooks return false; } - public static boolean canHarvestBlock(@Nonnull BlockState state, @Nonnull Player player, @Nonnull BlockGetter world, @Nonnull BlockPos pos) + public static boolean isCorrectToolForDrops(@Nonnull BlockState state, @Nonnull Player player) { - //state = state.getActualState(world, pos); if (!state.requiresCorrectToolForDrops()) return ForgeEventFactory.doPlayerHarvestCheck(player, state, true); - ItemStack stack = player.getMainHandItem(); - ToolType tool = state.getHarvestTool(); - if (stack.isEmpty() || tool == null) - return player.hasCorrectToolForDrops(state); - - int toolLevel = stack.getHarvestLevel(tool, player, state); - if (toolLevel < 0) - return player.hasCorrectToolForDrops(state); - - return ForgeEventFactory.doPlayerHarvestCheck(player, state, toolLevel >= state.getHarvestLevel()); - } - - public static boolean isToolEffective(LevelReader world, BlockPos pos, @Nonnull ItemStack stack) - { - BlockState state = world.getBlockState(pos); - //state = state.getActualState(world, pos); - for (ToolType type : stack.getToolTypes()) - { - if (state.isToolEffective(type)) - return true; - } - return false; - } - - private static boolean toolInit = false; - static void initTools() - { - if (toolInit) - return; - toolInit = true; - - //This is taken from PickaxeItem, if that changes update here. - for (Block block : new Block[]{Blocks.OBSIDIAN, Blocks.CRYING_OBSIDIAN, Blocks.NETHERITE_BLOCK, Blocks.RESPAWN_ANCHOR, Blocks.ANCIENT_DEBRIS}) - blockToolSetter.accept(block, ToolType.PICKAXE, 3); - for (Block block : new Block[]{Blocks.DIAMOND_BLOCK, Blocks.DIAMOND_ORE, Blocks.EMERALD_ORE, Blocks.EMERALD_BLOCK, Blocks.GOLD_BLOCK, Blocks.GOLD_ORE, Blocks.REDSTONE_ORE}) - blockToolSetter.accept(block, ToolType.PICKAXE, 2); - for (Block block : new Block[]{Blocks.IRON_BLOCK, Blocks.IRON_ORE, Blocks.LAPIS_BLOCK, Blocks.LAPIS_ORE}) - blockToolSetter.accept(block, ToolType.PICKAXE, 1); + return player.hasCorrectToolForDrops(state); } /** @@ -898,6 +853,19 @@ public class ForgeHooks return "default"; } + public static Tag getTagFromVanillaTier(Tiers tier) + { + return switch(tier) + { + case WOOD -> Tags.Blocks.NEEDS_WOOD_TOOL; + case GOLD -> Tags.Blocks.NEEDS_GOLD_TOOL; + case STONE -> BlockTags.NEEDS_STONE_TOOL; + case IRON -> BlockTags.NEEDS_IRON_TOOL; + case DIAMOND -> BlockTags.NEEDS_DIAMOND_TOOL; + case NETHERITE -> Tags.Blocks.NEEDS_NETHERITE_TOOL; + }; + } + @FunctionalInterface public interface BiomeCallbackFunction { @@ -1093,12 +1061,6 @@ public class ForgeHooks return false; } - private static TriConsumer blockToolSetter; - //Internal use only Modders, this is specifically hidden from you, as you shouldn't be editing other people's blocks. - public static void setBlockToolSetter(TriConsumer setter) - { - blockToolSetter = setter; - } private static final DummyBlockReader DUMMY_WORLD = new DummyBlockReader(); private static class DummyBlockReader implements BlockGetter { diff --git a/src/main/java/net/minecraftforge/common/ForgeInternalHandler.java b/src/main/java/net/minecraftforge/common/ForgeInternalHandler.java index 75094de159..d2cbecba7a 100644 --- a/src/main/java/net/minecraftforge/common/ForgeInternalHandler.java +++ b/src/main/java/net/minecraftforge/common/ForgeInternalHandler.java @@ -140,5 +140,11 @@ public class ForgeInternalHandler throw new IllegalStateException("Can not retrieve LootModifierManager until resources have loaded once."); return INSTANCE; } + + @SubscribeEvent + public void resourceReloadListeners(AddReloadListenerEvent event) + { + event.addListener(TierSortingRegistry.getReloadListener()); + } } diff --git a/src/main/java/net/minecraftforge/common/ForgeMod.java b/src/main/java/net/minecraftforge/common/ForgeMod.java index f7c7abcac0..d50376d4ed 100644 --- a/src/main/java/net/minecraftforge/common/ForgeMod.java +++ b/src/main/java/net/minecraftforge/common/ForgeMod.java @@ -168,7 +168,6 @@ public class ForgeMod implements WorldPersistenceHooks.WorldPersistenceHook MinecraftForge.EVENT_BUS.addListener(VillagerTradingManager::loadTrades); MinecraftForge.EVENT_BUS.register(MinecraftForge.INTERNAL_HANDLER); - MinecraftForge.EVENT_BUS.register(this); BiomeDictionary.init(); } diff --git a/src/main/java/net/minecraftforge/common/ForgeTier.java b/src/main/java/net/minecraftforge/common/ForgeTier.java new file mode 100644 index 0000000000..b39efb141f --- /dev/null +++ b/src/main/java/net/minecraftforge/common/ForgeTier.java @@ -0,0 +1,114 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.common; + +import net.minecraft.tags.Tag; +import net.minecraft.world.item.Tier; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.block.Block; + +import javax.annotation.Nonnull; +import java.util.Objects; +import java.util.function.Supplier; + +/** + * Helper class to define a custom tier + */ +@SuppressWarnings("ClassCanBeRecord") // can't make it a record because the method names will be obfuscated +public final class ForgeTier implements Tier +{ + private final int level; + private final int uses; + private final float speed; + private final float attackDamageBonus; + private final int enchantmentValue; + @Nonnull + private final Tag tag; + @Nonnull + private final Supplier repairIngredient; + + public ForgeTier(int level, int uses, float speed, float attackDamageBonus, int enchantmentValue, + @Nonnull Tag tag, @Nonnull Supplier repairIngredient) + { + this.level = level; + this.uses = uses; + this.speed = speed; + this.attackDamageBonus = attackDamageBonus; + this.enchantmentValue = enchantmentValue; + this.tag = tag; + this.repairIngredient = repairIngredient; + } + + @Override + public int getUses() + { + return this.uses; + } + + @Override + public float getSpeed() + { + return this.speed; + } + + @Override + public float getAttackDamageBonus() + { + return this.attackDamageBonus; + } + + @Override + public int getLevel() + { + return this.level; + } + + @Override + public int getEnchantmentValue() + { + return this.enchantmentValue; + } + + @Nonnull + public Tag getTag() + { + return this.tag; + } + + @Nonnull + @Override + public Ingredient getRepairIngredient() + { + return this.repairIngredient.get(); + } + + @Override + public String toString() + { + return "ForgeTier[" + + "level=" + level + ", " + + "uses=" + uses + ", " + + "speed=" + speed + ", " + + "attackDamageBonus=" + attackDamageBonus + ", " + + "enchantmentValue=" + enchantmentValue + ", " + + "tag=" + tag + ", " + + "repairIngredient=" + repairIngredient + ']'; + } +} diff --git a/src/main/java/net/minecraftforge/common/MinecraftForge.java b/src/main/java/net/minecraftforge/common/MinecraftForge.java index 3fcd81ac9f..91b5b605df 100644 --- a/src/main/java/net/minecraftforge/common/MinecraftForge.java +++ b/src/main/java/net/minecraftforge/common/MinecraftForge.java @@ -51,12 +51,9 @@ public class MinecraftForge LOGGER.info(FORGE,"MinecraftForge v{} Initialized", ForgeVersion.getVersion()); UsernameCache.load(); - ForgeHooks.initTools(); + TierSortingRegistry.init(); } - - - /* public static void preloadCrashClasses(ASMDataTable table, String modID, Set classes) { diff --git a/src/main/java/net/minecraftforge/common/Tags.java b/src/main/java/net/minecraftforge/common/Tags.java index 5923a5fdbc..0f082144f4 100644 --- a/src/main/java/net/minecraftforge/common/Tags.java +++ b/src/main/java/net/minecraftforge/common/Tags.java @@ -129,6 +129,10 @@ public class Tags public static final IOptionalNamedTag STORAGE_BLOCKS_QUARTZ = tag("storage_blocks/quartz"); public static final IOptionalNamedTag STORAGE_BLOCKS_REDSTONE = tag("storage_blocks/redstone"); + public static final IOptionalNamedTag NEEDS_WOOD_TOOL = tag("needs_wood_tool"); + public static final IOptionalNamedTag NEEDS_GOLD_TOOL = tag("needs_gold_tool"); + public static final IOptionalNamedTag NEEDS_NETHERITE_TOOL = tag("needs_netherite_tool"); + private static IOptionalNamedTag tag(String name) { return BlockTags.createOptional(new ResourceLocation("forge", name)); diff --git a/src/main/java/net/minecraftforge/common/TierSortingRegistry.java b/src/main/java/net/minecraftforge/common/TierSortingRegistry.java new file mode 100644 index 0000000000..3a6119b7b3 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/TierSortingRegistry.java @@ -0,0 +1,414 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.common; + +import com.google.common.collect.*; +import com.google.common.graph.ElementOrder; +import com.google.common.graph.GraphBuilder; +import com.google.common.graph.MutableGraph; +import com.google.gson.*; +import it.unimi.dsi.fastutil.booleans.BooleanConsumer; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.packs.resources.PreparableReloadListener; +import net.minecraft.server.packs.resources.Resource; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.server.packs.resources.SimplePreparableReloadListener; +import net.minecraft.tags.BlockTags; +import net.minecraft.tags.Tag; +import net.minecraft.util.GsonHelper; +import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.world.item.DiggerItem; +import net.minecraft.world.item.Tier; +import net.minecraft.world.item.Tiers; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.ClientPlayerNetworkEvent; +import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.fml.loading.FMLEnvironment; +import net.minecraftforge.fml.loading.toposort.TopologicalSort; +import net.minecraftforge.fmllegacy.network.NetworkDirection; +import net.minecraftforge.fmllegacy.network.NetworkEvent; +import net.minecraftforge.fmllegacy.network.NetworkRegistry; +import net.minecraftforge.fmllegacy.network.PacketDistributor; +import net.minecraftforge.fmllegacy.network.simple.SimpleChannel; +import net.minecraftforge.fmllegacy.server.ServerLifecycleHooks; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public class TierSortingRegistry +{ + private static final Logger LOGGER = LogManager.getLogger(); + + /** + * Registers a tier into the tier sorting registry. + * @param tier The tier to register + * @param name The name to use internally for dependency resolution + * @param after List of tiers to place this tier after (the tiers in the list will be considered lesser tiers) + * @param before List of tiers to place this tier before (the tiers in the list will be considered better tiers) + */ + public static synchronized Tier registerTier(Tier tier, ResourceLocation name, List after, List before) + { + if (tiers.containsKey(name)) + throw new IllegalStateException("Duplicate tier name " + name); + + processTier(tier, name, after, before); + + hasCustomTiers = true; + return tier; + } + + /** + * Returns the list of tiers in the order defined by the dependencies. + * This list will remain valid + * @return An unmodifiable list of tiers ordered lesser to greater + */ + public static List getSortedTiers() + { + return sortedTiersUnmodifiable; + } + + /** + * Returns the tier associated with a name, if registered into the sorting system. + * @param name The name to look up + * @return The tier, or null if not registered + */ + @Nullable + public static Tier byName(ResourceLocation name) + { + return tiers.get(name); + } + + /** + * Returns the name associated with a tier, if the tier is registered into the sorting system. + * @param tier The tier to look up + * @return The name for the tier, or null if not registered + */ + @Nullable + public static ResourceLocation getName(Tier tier) + { + return tiers.inverse().get(tier); + } + + /** + * Queries if a tier should be evaluated using the sorting system, by calling isCorrectTierForDrops + * @param tier The tier to query + * @return True if isCorrectTierForDrops should be called for the tier + */ + public static boolean isTierSorted(Tier tier) + { + return getName(tier) != null; + } + + /** + * Queries if a tier is high enough to be able to get drops for the given blockstate. + * @param tier The tier to look up + * @param state The state to test against + * @return True if the tier is good enough + */ + public static boolean isCorrectTierForDrops(Tier tier, BlockState state) + { + if (!isTierSorted(tier)) + return isCorrectTierVanilla(tier, state); + + for (int x = sortedTiers.indexOf(tier) + 1; x < sortedTiers.size(); x++) { + Tag tag = sortedTiers.get(x).getTag(); + if (tag != null && state.is(tag)) + return false; + } + return true; + } + + /** + * Helper to query all tiers that are lower than the given tier + * @param tier The tier + * @return All the lower tiers + */ + public static List getTiersLowerThan(Tier tier) + { + if (!isTierSorted(tier)) return List.of(); + return sortedTiers.stream().takeWhile(t -> t != tier).toList(); + } + + // ===================== PRIVATE INTERNAL STUFFS BELOW THIS LINE ===================== + + /** + * Fallback for when a tier isn't in the registry, copy of the logic in {@link DiggerItem#isCorrectToolForDrops} + */ + private static boolean isCorrectTierVanilla(Tier tier, BlockState state) + { + int i = tier.getLevel(); + if (i < 3 && state.is(BlockTags.NEEDS_DIAMOND_TOOL)) + { + return false; + } + else if (i < 2 && state.is(BlockTags.NEEDS_IRON_TOOL)) + { + return false; + } + else if (i < 1 && state.is(BlockTags.NEEDS_STONE_TOOL)) + { + return false; + } + return true; + } + + private static void processTier(Tier tier, ResourceLocation name, List afters, List befores) + { + tiers.put(name, tier); + for(Object after : afters) + { + ResourceLocation other = getTierName(after); + edges.put(other, name); + } + for(Object before : befores) + { + ResourceLocation other = getTierName(before); + edges.put(name, other); + } + } + + private static ResourceLocation getTierName(Object entry) + { + if (entry instanceof String s) + return new ResourceLocation(s); + if (entry instanceof ResourceLocation rl) + return rl; + if (entry instanceof Tier t) + return Objects.requireNonNull(getName(t), "Can't have sorting dependencies for tiers not registered in the TierSortingRegistry"); + throw new IllegalStateException("Invalid object type passed into the tier dependencies " + entry.getClass()); + } + + private static boolean hasCustomTiers = false; + private static final BiMap tiers = HashBiMap.create(); + private static final Multimap edges = HashMultimap.create(); + private static final Multimap vanillaEdges = HashMultimap.create(); + + static { + var wood = new ResourceLocation("wood"); + var stone = new ResourceLocation("stone"); + var iron = new ResourceLocation("iron"); + var diamond = new ResourceLocation("diamond"); + var netherite = new ResourceLocation("netherite"); + var gold = new ResourceLocation("gold"); + processTier(Tiers.WOOD, wood, List.of(), List.of()); + processTier(Tiers.GOLD, gold, List.of(wood), List.of(stone)); + processTier(Tiers.STONE, stone, List.of(wood), List.of(iron)); + processTier(Tiers.IRON, iron, List.of(stone), List.of(diamond)); + processTier(Tiers.DIAMOND, diamond, List.of(iron), List.of(netherite)); + processTier(Tiers.NETHERITE, netherite, List.of(diamond), List.of()); + vanillaEdges.putAll(edges); + } + + private static final List sortedTiers = new ArrayList<>(); + private static final List sortedTiersUnmodifiable = Collections.unmodifiableList(sortedTiers); + + private static final ResourceLocation CHANNEL_NAME = new ResourceLocation("forge:tier_sorting"); + private static final String PROTOCOL_VERSION = "1.0"; + private static final SimpleChannel SYNC_CHANNEL = NetworkRegistry.newSimpleChannel( + CHANNEL_NAME, () -> "1.0", + versionFromServer -> PROTOCOL_VERSION.equals(versionFromServer) || (allowVanilla() && NetworkRegistry.ACCEPTVANILLA.equals(versionFromServer)), + versionFromClient -> PROTOCOL_VERSION.equals(versionFromClient) || (allowVanilla() && NetworkRegistry.ACCEPTVANILLA.equals(versionFromClient)) + ); + + static boolean allowVanilla() + { + return !hasCustomTiers; + } + + /*package private*/ + static void init() + { + SYNC_CHANNEL.registerMessage(0, SyncPacket.class, SyncPacket::encode, TierSortingRegistry::receive, TierSortingRegistry::handle, Optional.of(NetworkDirection.PLAY_TO_CLIENT)); + MinecraftForge.EVENT_BUS.addListener(TierSortingRegistry::playerLoggedIn); + if (FMLEnvironment.dist == Dist.CLIENT) ClientEvents.init(); + } + + /*package private*/ + static PreparableReloadListener getReloadListener() + { + return new SimplePreparableReloadListener() + { + static final ResourceLocation ITEM_TIER_ORDERING_JSON = new ResourceLocation("forge", "item_tier_ordering.json"); + + final Gson gson = (new GsonBuilder()).create(); + + @Nonnull + @Override + protected JsonObject prepare(@Nonnull ResourceManager resourceManager, ProfilerFiller p) + { + if (!resourceManager.hasResource(ITEM_TIER_ORDERING_JSON)) + return new JsonObject(); + + try (Resource r = resourceManager.getResource(ITEM_TIER_ORDERING_JSON); InputStream stream = r.getInputStream(); Reader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) + { + return gson.fromJson(reader, JsonObject.class); + } + catch (IOException e) + { + LOGGER.error("Could not read Tier sorting file " + ITEM_TIER_ORDERING_JSON, e); + return new JsonObject(); + } + } + + @Override + protected void apply(@Nonnull JsonObject data, @Nonnull ResourceManager resourceManager, ProfilerFiller p) + { + try + { + if (data.size() > 0) + { + JsonArray order = GsonHelper.getAsJsonArray(data, "order"); + List customOrder = new ArrayList<>(); + for (JsonElement entry : order) + { + ResourceLocation id = new ResourceLocation(entry.getAsString()); + Tier tier = byName(id); + if (tier == null) throw new IllegalStateException("Tier not found with name " + id); + customOrder.add(tier); + } + + List missingTiers = tiers.values().stream().filter(tier -> !customOrder.contains(tier)).toList(); + if (missingTiers.size() > 0) + throw new IllegalStateException("Tiers missing from the ordered list: " + missingTiers.stream().map(tier -> Objects.toString(TierSortingRegistry.getName(tier))).collect(Collectors.joining(", "))); + + setTierOrder(customOrder); + return; + } + } + catch(Exception e) + { + LOGGER.error("Error parsing Tier sorting file " + ITEM_TIER_ORDERING_JSON, e); + } + + recalculateItemTiers(); + } + }; + } + + @SuppressWarnings("UnstableApiUsage") + private static void recalculateItemTiers() + { + final MutableGraph graph = GraphBuilder.directed().nodeOrder(ElementOrder.insertion()).build(); + + for(Tier tier : tiers.values()) + { + graph.addNode(tier); + } + edges.forEach((key, value) -> { + if (tiers.containsKey(key) && tiers.containsKey(value)) + graph.putEdge(tiers.get(key), tiers.get(value)); + }); + List tierList = TopologicalSort.topologicalSort(graph, null); + + setTierOrder(tierList); + } + + private static void setTierOrder(List tierList) + { + runInServerThreadIfPossible(hasServer -> { + sortedTiers.clear(); + sortedTiers.addAll(tierList); + if(hasServer) syncToAll(); + }); + } + + private static void runInServerThreadIfPossible(BooleanConsumer runnable) + { + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + if (server != null) server.execute(() -> runnable.accept(true)); + else runnable.accept(false); + } + + private static void syncToAll() + { + for(ServerPlayer serverPlayer : ServerLifecycleHooks.getCurrentServer().getPlayerList().getPlayers()) + { + syncToPlayer(serverPlayer); + } + } + + private static void playerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) + { + if (event.getPlayer() instanceof ServerPlayer serverPlayer) + { + syncToPlayer(serverPlayer); + } + } + + private static void syncToPlayer(ServerPlayer serverPlayer) + { + if (SYNC_CHANNEL.isRemotePresent(serverPlayer.connection.getConnection()) && !serverPlayer.connection.getConnection().isMemoryConnection()) + { + SYNC_CHANNEL.send(PacketDistributor.PLAYER.with(() -> serverPlayer), new SyncPacket(sortedTiers.stream().map(TierSortingRegistry::getName).toList())); + } + } + + private static SyncPacket receive(FriendlyByteBuf buffer) + { + int count = buffer.readVarInt(); + List list = new ArrayList<>(); + for(int i=0;i context) + { + setTierOrder(packet.tiers.stream().map(TierSortingRegistry::byName).toList()); + context.get().setPacketHandled(true); + } + + private record SyncPacket(List tiers) + { + private void encode(FriendlyByteBuf buffer) + { + buffer.writeVarInt(tiers.size()); + for(ResourceLocation loc : tiers) + buffer.writeResourceLocation(loc); + } + } + + private static class ClientEvents + { + public static void init() + { + MinecraftForge.EVENT_BUS.addListener(ClientEvents::clientLogInToServer); + } + + private static void clientLogInToServer(ClientPlayerNetworkEvent.LoggedInEvent event) + { + if (event.getNetworkManager() == null || !event.getNetworkManager().isMemoryConnection()) + recalculateItemTiers(); + } + } +} diff --git a/src/main/java/net/minecraftforge/common/ToolAction.java b/src/main/java/net/minecraftforge/common/ToolAction.java new file mode 100644 index 0000000000..602e663fe6 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/ToolAction.java @@ -0,0 +1,73 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.common; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@SuppressWarnings("ClassCanBeRecord") +public class ToolAction +{ + private static final Map actions = new ConcurrentHashMap<>(); + + /** + * Returns all registered actions. + * This collection can be kept around, and will update itself in response to changes to the map. + * See {@link ConcurrentHashMap#values()} for details. + */ + public static Collection getActions() + { + return Collections.unmodifiableCollection(actions.values()); + } + + /** + * Gets or creates a new ToolAction for the given name. + */ + public static ToolAction get(String name) + { + return actions.computeIfAbsent(name, ToolAction::new); + } + + /** + * Returns the name of this tool action + */ + public String name() + { + return name; + } + + @Override + public String toString() + { + return "ToolAction[" + name + "]"; + } + + private final String name; + + /** + * Use {@link #get(String)} to get or create a ToolAction + */ + private ToolAction(String name) + { + this.name = name; + } +} diff --git a/src/main/java/net/minecraftforge/common/ToolActions.java b/src/main/java/net/minecraftforge/common/ToolActions.java new file mode 100644 index 0000000000..bc9464eb22 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/ToolActions.java @@ -0,0 +1,88 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.common; + +import com.google.common.collect.Sets; +import net.minecraftforge.common.extensions.IForgeBlock; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class ToolActions +{ + /** + * Exposed by axes to allow querying tool behaviours + */ + public static final ToolAction AXE_DIG = ToolAction.get("axe_dig"); + + /** + * Exposed by pickaxes to allow querying tool behaviours + */ + public static final ToolAction PICKAXE_DIG = ToolAction.get("pickaxe_dig"); + + /** + * Exposed by shovels to allow querying tool behaviours + */ + public static final ToolAction SHOVEL_DIG = ToolAction.get("shovel_dig"); + + /** + * Exposed by shovels to allow querying tool behaviours + */ + public static final ToolAction HOE_DIG = ToolAction.get("hoe_dig"); + + /** + * Exposed by swords to allow querying tool behaviours + */ + public static final ToolAction SWORD_DIG = ToolAction.get("sword_dig"); + + /** + * Passed onto {@link IForgeBlock#getToolModifiedState} when an axe wants to strip a log + */ + public static final ToolAction AXE_STRIP = ToolAction.get("axe_strip"); + + /** + * Passed onto {@link IForgeBlock#getToolModifiedState} when an axe wants to scrape oxidization off copper + */ + public static final ToolAction AXE_SCRAPE = ToolAction.get("axe_scrape"); + + /** + * Passed onto {@link IForgeBlock#getToolModifiedState} when an axe wants to remove wax out of copper + */ + public static final ToolAction AXE_WAX_OFF = ToolAction.get("axe_wax_off"); + + /** + * Passed onto {@link IForgeBlock#getToolModifiedState} when a shovel wants to turn dirt into path + */ + public static final ToolAction SHOVEL_FLATTEN = ToolAction.get("shovel_flatten"); + + ///** + // * Passed onto {@link IForgeBlock#getToolModifiedState} when a hoe wants to turn dirt into soil + // */ + // TODO: public static final ToolAction HOE_TILL = ToolAction.get("till"); + + + // Default actions supported by each tool type + public static final Set DEFAULT_AXE_ACTIONS = Stream.of(AXE_DIG, AXE_STRIP, AXE_SCRAPE, AXE_WAX_OFF).collect(Collectors.toCollection(Sets::newIdentityHashSet)); + public static final Set DEFAULT_HOE_ACTIONS = Stream.of(HOE_DIG /* TODO: , HOE_TILL */).collect(Collectors.toCollection(Sets::newIdentityHashSet)); + public static final Set DEFAULT_SHOVEL_ACTIONS = Stream.of(SHOVEL_DIG, SHOVEL_FLATTEN).collect(Collectors.toCollection(Sets::newIdentityHashSet)); + public static final Set DEFAULT_PICKAXE_ACTIONS = Stream.of(PICKAXE_DIG).collect(Collectors.toCollection(Sets::newIdentityHashSet)); + public static final Set DEFAULT_SWORD_ACTIONS = Stream.of(SWORD_DIG).collect(Collectors.toCollection(Sets::newIdentityHashSet)); +} diff --git a/src/main/java/net/minecraftforge/common/ToolType.java b/src/main/java/net/minecraftforge/common/ToolType.java deleted file mode 100644 index 725df375bb..0000000000 --- a/src/main/java/net/minecraftforge/common/ToolType.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2021. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation version 2.1 - * of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package net.minecraftforge.common; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.regex.Pattern; - -public final class ToolType -{ - private static final Pattern VALID_NAME = Pattern.compile("[^a-z_]"); //Only a-z and _ are allowed, meaning names must be lower case. And use _ to separate words. - private static final Map VALUES = new ConcurrentHashMap<>(); - - public static final ToolType AXE = get("axe"); - public static final ToolType HOE = get("hoe"); - public static final ToolType PICKAXE = get("pickaxe"); - public static final ToolType SHOVEL = get("shovel"); - - /** - * Gets the ToolType for the specified name, or creates a new one if none for that name does yet exist. - * This method can be called during parallel loading - */ - public static ToolType get(String name) - { - return VALUES.computeIfAbsent(name, k -> - { - if (VALID_NAME.matcher(name).find()) - throw new IllegalArgumentException("ToolType.get() called with invalid name: " + name); - return new ToolType(name); - }); - } - - private final String name; - - private ToolType(String name) - { - this.name = name; - } - - public String getName() - { - return name; - } -} diff --git a/src/main/java/net/minecraftforge/common/extensions/IForgeBlock.java b/src/main/java/net/minecraftforge/common/extensions/IForgeBlock.java index 845c9c235e..decfd3b26e 100644 --- a/src/main/java/net/minecraftforge/common/extensions/IForgeBlock.java +++ b/src/main/java/net/minecraftforge/common/extensions/IForgeBlock.java @@ -32,25 +32,19 @@ import net.minecraft.world.entity.boss.enderdragon.EnderDragon; import net.minecraft.world.entity.boss.wither.WitherBoss; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.projectile.WitherSkull; -import net.minecraft.world.level.block.LadderBlock; -import net.minecraft.world.level.block.TrapDoorBlock; +import net.minecraft.world.item.*; +import net.minecraft.world.level.block.*; import net.minecraft.world.level.material.FluidState; -import net.minecraft.world.item.AxeItem; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.ShovelItem; import net.minecraft.world.level.pathfinder.BlockPathTypes; import net.minecraft.tags.BlockTags; import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.block.Rotation; import net.minecraft.core.BlockPos; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import net.minecraft.server.level.ServerLevel; import net.minecraftforge.common.ForgeHooks; import net.minecraftforge.common.IPlantable; -import net.minecraftforge.common.Tags; -import net.minecraftforge.common.ToolType; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.BlockGetter; @@ -58,18 +52,9 @@ import net.minecraft.world.level.Explosion; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.block.BeaconBeamBlock; -import net.minecraft.world.level.block.BedBlock; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.FarmBlock; -import net.minecraft.world.level.block.FenceGateBlock; -import net.minecraft.world.level.block.FireBlock; -import net.minecraft.world.level.block.HalfTransparentBlock; -import net.minecraft.world.level.block.HorizontalDirectionalBlock; -import net.minecraft.world.level.block.LeavesBlock; -import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.common.ToolAction; +import net.minecraftforge.common.ToolActions; @SuppressWarnings("deprecation") public interface IForgeBlock @@ -163,7 +148,7 @@ public interface IForgeBlock */ default public boolean canHarvestBlock(BlockState state, BlockGetter world, BlockPos pos, Player player) { - return ForgeHooks.canHarvestBlock(state, player, world, pos); + return ForgeHooks.isCorrectToolForDrops(state, player); } /** @@ -461,31 +446,6 @@ public interface IForgeBlock return false; } - /** - * Queries the class of tool required to harvest this block, if null is returned - * we assume that anything can harvest this block. - */ - ToolType getHarvestTool(BlockState state); - - /** - * Queries the harvest level of this item stack for the specified tool class, - * Returns -1 if this tool is not of the specified type - * - * @return Harvest level, or -1 if not the specified tool type. - */ - int getHarvestLevel(BlockState state); - - /** - * Checks if the specified tool type is efficient on this block, - * meaning that it digs at full speed. - */ - default boolean isToolEffective(BlockState state, ToolType tool) - { - if (tool == ToolType.PICKAXE && (self() == Blocks.REDSTONE_ORE || self() == Blocks.REDSTONE_LAMP || self() == Blocks.OBSIDIAN)) - return false; - return tool == getHarvestTool(state); - } - /** * Sensitive version of getSoundType * @param state The state @@ -729,14 +689,21 @@ public interface IForgeBlock * @param pos The block position in world * @param player The player clicking the block * @param stack The stack being used by the player + * @param toolAction The action being performed by the tool * @return The resulting state after the action has been performed */ @Nullable - default BlockState getToolModifiedState(BlockState state, Level world, BlockPos pos, Player player, ItemStack stack, ToolType toolType) + default BlockState getToolModifiedState(BlockState state, Level world, BlockPos pos, Player player, ItemStack stack, ToolAction toolAction) { - if (toolType == ToolType.AXE) return AxeItem.getAxeStrippingState(state); -// else if(toolType == ToolType.HOE) return HoeItem.getHoeTillingState(state); //TODO HoeItem bork - else return toolType == ToolType.SHOVEL ? ShovelItem.getShovelPathingState(state) : null; + if (!stack.canPerformAction(toolAction)) return null; + if (ToolActions.AXE_STRIP.equals(toolAction)) return AxeItem.getAxeStrippingState(state); + else if(ToolActions.AXE_SCRAPE.equals(toolAction)) return WeatheringCopper.getPrevious(state).orElse(null); + else if(ToolActions.AXE_WAX_OFF.equals(toolAction)) return Optional.ofNullable(HoneycombItem.WAX_OFF_BY_BLOCK.get().get(state.getBlock())).map((p_150694_) -> { + return p_150694_.withPropertiesOf(state); + }).orElse(null); + //else if(ToolActions.HOE_TILL.equals(toolAction)) return HoeItem.getHoeTillingState(state); //TODO HoeItem bork + else if (ToolActions.SHOVEL_FLATTEN.equals(toolAction)) return ShovelItem.getShovelPathingState(state); + return null; } /** diff --git a/src/main/java/net/minecraftforge/common/extensions/IForgeBlockState.java b/src/main/java/net/minecraftforge/common/extensions/IForgeBlockState.java index cde9c82e39..eba46a9749 100644 --- a/src/main/java/net/minecraftforge/common/extensions/IForgeBlockState.java +++ b/src/main/java/net/minecraftforge/common/extensions/IForgeBlockState.java @@ -36,7 +36,6 @@ import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import net.minecraft.server.level.ServerLevel; import net.minecraftforge.common.IPlantable; -import net.minecraftforge.common.ToolType; import net.minecraft.core.Direction; import net.minecraft.world.level.BlockAndTintGetter; @@ -48,6 +47,7 @@ import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.common.ToolAction; public interface IForgeBlockState { @@ -387,29 +387,6 @@ public interface IForgeBlockState return self().getBlock().getWeakChanges(self(), world, pos); } - /** - * Queries the class of tool required to harvest this block, if null is returned - * we assume that anything can harvest this block. - */ - default ToolType getHarvestTool() - { - return self().getBlock().getHarvestTool(self()); - } - - default int getHarvestLevel() - { - return self().getBlock().getHarvestLevel(self()); - } - - /** - * Checks if the specified tool type is efficient on this block, - * meaning that it digs at full speed. - */ - default boolean isToolEffective(ToolType tool) - { - return self().getBlock().isToolEffective(self(), tool); - } - /** * Sensitive version of getSoundType * @param world The world @@ -648,10 +625,10 @@ public interface IForgeBlockState * @return The resulting state after the action has been performed */ @Nullable - default BlockState getToolModifiedState(Level world, BlockPos pos, Player player, ItemStack stack, ToolType toolType) + default BlockState getToolModifiedState(Level world, BlockPos pos, Player player, ItemStack stack, ToolAction toolAction) { - BlockState eventState = net.minecraftforge.event.ForgeEventFactory.onToolUse(self(), world, pos, player, stack, toolType); - return eventState != self() ? eventState : self().getBlock().getToolModifiedState(self(), world, pos, player, stack, toolType); + BlockState eventState = net.minecraftforge.event.ForgeEventFactory.onToolUse(self(), world, pos, player, stack, toolAction); + return eventState != self() ? eventState : self().getBlock().getToolModifiedState(self(), world, pos, player, stack, toolAction); } /** diff --git a/src/main/java/net/minecraftforge/common/extensions/IForgeItem.java b/src/main/java/net/minecraftforge/common/extensions/IForgeItem.java index 6ba717ed82..3e93ea8780 100644 --- a/src/main/java/net/minecraftforge/common/extensions/IForgeItem.java +++ b/src/main/java/net/minecraftforge/common/extensions/IForgeItem.java @@ -19,6 +19,7 @@ package net.minecraftforge.common.extensions; +import java.util.List; import java.util.Set; import java.util.function.Consumer; @@ -26,6 +27,9 @@ import javax.annotation.Nullable; import com.google.common.collect.Multimap; +import net.minecraft.tags.Tag; +import net.minecraft.world.item.*; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.entity.Entity; @@ -37,14 +41,7 @@ import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.monster.EnderMan; import net.minecraft.world.entity.monster.piglin.PiglinAi; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ArmorItem; -import net.minecraft.world.item.ArmorMaterials; -import net.minecraft.world.item.Items; import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.AxeItem; -import net.minecraft.world.item.CreativeModeTab; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.nbt.CompoundTag; @@ -54,7 +51,8 @@ import net.minecraft.core.BlockPos; import net.minecraft.util.Mth; import net.minecraft.network.chat.Component; import net.minecraft.world.level.Level; -import net.minecraftforge.common.ToolType; +import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.common.ToolAction; // TODO review most of the methods in this "patch" public interface IForgeItem @@ -519,14 +517,26 @@ public interface IForgeItem stack.getOrCreateTag().putInt("Damage", Math.max(0, damage)); } + /** + * Queries if an item can perform the given action. + * See {@link net.minecraftforge.common.ToolActions} for a description of each stock action + * @param stack The stack being used + * @param toolAction The action being queried + * @return True if the stack can perform the action + */ + default boolean canPerformAction(ItemStack stack, ToolAction toolAction) + { + return false; + } + /** * ItemStack sensitive version of {@link #canHarvestBlock(IBlockState)} * * @param stack The itemstack used to harvest the block * @param state The block trying to harvest - * @return true if can harvest the block + * @return true if the stack can harvest the block */ - default boolean canHarvestBlock(ItemStack stack, BlockState state) + default boolean isCorrectToolForDrops(ItemStack stack, BlockState state) { return self().isCorrectToolForDrops(state); } @@ -544,20 +554,6 @@ public interface IForgeItem return self().getMaxStackSize(); } - Set getToolTypes(ItemStack stack); - - /** - * Queries the harvest level of this item stack for the specified tool class, - * Returns -1 if this tool is not of the specified type - * - * @param stack This item stack instance - * @param toolClass Tool Class - * @param player The player trying to harvest the given blockstate - * @param blockState The block to harvest - * @return Harvest level, or -1 if not the specified tool type. - */ - int getHarvestLevel(ItemStack stack, ToolType tool, @Nullable Player player, @Nullable BlockState blockState); - /** * ItemStack sensitive version of getItemEnchantability * diff --git a/src/main/java/net/minecraftforge/common/extensions/IForgeItemStack.java b/src/main/java/net/minecraftforge/common/extensions/IForgeItemStack.java index ef11a48b41..9eabb584f7 100644 --- a/src/main/java/net/minecraftforge/common/extensions/IForgeItemStack.java +++ b/src/main/java/net/minecraftforge/common/extensions/IForgeItemStack.java @@ -19,14 +19,15 @@ package net.minecraftforge.common.extensions; -import java.util.Set; - import javax.annotation.Nullable; +import net.minecraft.tags.Tag; import net.minecraft.world.entity.monster.EnderMan; +import net.minecraft.world.item.Tier; import net.minecraft.world.item.crafting.RecipeType; -import net.minecraft.world.level.block.state.pattern.BlockInWorld; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.pattern.BlockInWorld; import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Mob; @@ -43,9 +44,11 @@ import net.minecraft.world.InteractionResult; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.world.level.Level; -import net.minecraftforge.common.ToolType; +import net.minecraftforge.common.ToolAction; import net.minecraftforge.common.capabilities.ICapabilitySerializable; +import java.util.List; + /* * Extension added to ItemStack that bounces to ItemSack sensitive Item methods. Typically this is just for convince. */ @@ -88,24 +91,6 @@ public interface IForgeItemStack extends ICapabilitySerializable return self().getItem().getBurnTime(self(), recipeType); } - /** - * Queries the harvest level of this item stack for the specified tool class, - * Returns -1 if this tool is not of the specified type - * - * @param tool the tool type of the item - * @param player The player trying to harvest the given blockstate - * @param state The block to harvest - * @return Harvest level, or -1 if not the specified tool type. - */ - default int getHarvestLevel(ToolType tool, @Nullable Player player, @Nullable BlockState state) - { - return self().getItem().getHarvestLevel(self(), tool, player, state); - } - - default Set getToolTypes() { - return self().getItem().getToolTypes(self()); - } - default InteractionResult onItemUseFirst(UseOnContext context) { Player entityplayer = context.getPlayer(); @@ -130,6 +115,19 @@ public interface IForgeItemStack extends ICapabilitySerializable self().save(ret); return ret; } + + /** + * Queries if an item can perform the given action. + * See {@link net.minecraftforge.common.ToolActions} for a description of each stock action + * @param stack The stack being used + * @param toolAction The action being queried + * @return True if the stack can perform the action + */ + default boolean canPerformAction(ToolAction toolAction) + { + return self().getItem().canPerformAction(self(), toolAction); + } + /** * Called before a block is broken. Return true to prevent default block * harvesting. diff --git a/src/main/java/net/minecraftforge/event/ForgeEventFactory.java b/src/main/java/net/minecraftforge/event/ForgeEventFactory.java index 6beb7009cf..32b099707b 100644 --- a/src/main/java/net/minecraftforge/event/ForgeEventFactory.java +++ b/src/main/java/net/minecraftforge/event/ForgeEventFactory.java @@ -91,7 +91,7 @@ import net.minecraftforge.client.event.ClientChatReceivedEvent; import net.minecraftforge.client.event.RenderBlockOverlayEvent; import net.minecraftforge.client.event.RenderBlockOverlayEvent.OverlayType; import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.common.ToolType; +import net.minecraftforge.common.ToolAction; import net.minecraftforge.common.capabilities.CapabilityDispatcher; import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraftforge.common.util.BlockSnapshot; @@ -361,9 +361,9 @@ public class ForgeEventFactory } @Nullable - public static BlockState onToolUse(BlockState originalState, Level world, BlockPos pos, Player player, ItemStack stack, ToolType toolType) + public static BlockState onToolUse(BlockState originalState, Level world, BlockPos pos, Player player, ItemStack stack, ToolAction toolAction) { - BlockToolInteractEvent event = new BlockToolInteractEvent(world, pos, originalState, player, stack, toolType); + BlockToolInteractEvent event = new BlockToolInteractEvent(world, pos, originalState, player, stack, toolAction); return MinecraftForge.EVENT_BUS.post(event) ? null : event.getFinalState(); } diff --git a/src/main/java/net/minecraftforge/event/world/BlockEvent.java b/src/main/java/net/minecraftforge/event/world/BlockEvent.java index d1bd1cb5c5..edfc3a5053 100644 --- a/src/main/java/net/minecraftforge/event/world/BlockEvent.java +++ b/src/main/java/net/minecraftforge/event/world/BlockEvent.java @@ -35,7 +35,7 @@ import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.Level; import net.minecraftforge.common.ForgeHooks; -import net.minecraftforge.common.ToolType; +import net.minecraftforge.common.ToolAction; import net.minecraftforge.common.util.BlockSnapshot; import net.minecraftforge.eventbus.api.Cancelable; import net.minecraftforge.eventbus.api.Event; @@ -45,8 +45,6 @@ import com.google.common.collect.ImmutableList; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import net.minecraftforge.eventbus.api.Event.HasResult; - public class BlockEvent extends Event { private static final boolean DEBUG = Boolean.parseBoolean(System.getProperty("forge.debugBlockEvent", "false")); @@ -92,7 +90,7 @@ public class BlockEvent extends Event super(world, pos, state); this.player = player; - if (state == null || !ForgeHooks.canHarvestBlock(state, player, world, pos)) // Handle empty block or player unable to break block scenario + if (state == null || !ForgeHooks.isCorrectToolForDrops(state, player)) // Handle empty block or player unable to break block scenario { this.exp = 0; } @@ -445,16 +443,16 @@ public class BlockEvent extends Event private final Player player; private final ItemStack stack; - private final ToolType toolType; + private final ToolAction toolAction; private BlockState state; - public BlockToolInteractEvent(LevelAccessor world, BlockPos pos, BlockState originalState, Player player, ItemStack stack, ToolType toolType) + public BlockToolInteractEvent(LevelAccessor world, BlockPos pos, BlockState originalState, Player player, ItemStack stack, ToolAction toolAction) { super(world, pos, originalState); this.player = player; this.stack = stack; this.state = originalState; - this.toolType = toolType; + this.toolAction = toolAction; } /**Gets the player using the tool.*/ @@ -469,10 +467,10 @@ public class BlockEvent extends Event return stack; } - /**Gets the current type of the tool being compared against.*/ - public ToolType getToolType() + /**Gets the action being performed.*/ + public ToolAction getToolAction() { - return toolType; + return toolAction; } /** diff --git a/src/test/java/net/minecraftforge/debug/block/HarvestToolTest.java b/src/test/java/net/minecraftforge/debug/block/HarvestToolTest.java deleted file mode 100644 index 259c2f1ac2..0000000000 --- a/src/test/java/net/minecraftforge/debug/block/HarvestToolTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2021. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation version 2.1 - * of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package net.minecraftforge.debug.block; - -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.entity.player.Player; -import net.minecraft.network.chat.TextComponent; -import net.minecraftforge.common.ToolType; -import net.minecraftforge.event.world.BlockEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; - -@Mod("block_harvest_tool_test") -@Mod.EventBusSubscriber -public class HarvestToolTest -{ - @SubscribeEvent - public static void onBlockBreak(BlockEvent.BreakEvent event) - { - Player player = event.getPlayer(); - BlockState state = event.getState(); - for (ToolType toolType : player.getMainHandItem().getToolTypes()) { - if (state.isToolEffective(toolType)) { - player.sendMessage(new TextComponent(String.format("Tool was effective. tool type: %s | harvest level: %d", toolType.getName(), state.getHarvestLevel())), player.getUUID()); - break; - } - } - } -} diff --git a/src/test/java/net/minecraftforge/debug/item/TagBasedToolTypesTest.java b/src/test/java/net/minecraftforge/debug/item/TagBasedToolTypesTest.java new file mode 100644 index 0000000000..e685bf6a98 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/item/TagBasedToolTypesTest.java @@ -0,0 +1,76 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2021. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.debug.item; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.BlockTags; +import net.minecraft.tags.Tag; +import net.minecraft.world.item.*; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.common.ForgeTier; +import net.minecraftforge.common.TierSortingRegistry; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +import javax.annotation.Nullable; +import java.util.List; + +@Mod("tag_based_tool_types") +@Mod.EventBusSubscriber(bus= Mod.EventBusSubscriber.Bus.MOD) +public class TagBasedToolTypesTest +{ + public static final Tag.Named MY_TOOL_TAG = BlockTags.createOptional(new ResourceLocation("tag_based_tool_types:minable/my_tool")); + public static final Tag.Named MY_TIER_TAG = BlockTags.createOptional(new ResourceLocation("tag_based_tool_types:needs_my_tier_tool")); + public static final Tier MY_TIER = TierSortingRegistry.registerTier( + new ForgeTier(5, 5000, 10, 100, 0, MY_TIER_TAG, () -> Ingredient.of(Items.BEDROCK)), + new ResourceLocation("tag_based_tool_types:my_tier"), + List.of(Tiers.DIAMOND), List.of()); + + @SubscribeEvent + public static void registerItems(RegistryEvent.Register event) + { + event.getRegistry().register(new DiggerItem(1, 1, MY_TIER, MY_TOOL_TAG, new Item.Properties().tab(CreativeModeTab.TAB_TOOLS)) + { + @Override + public float getDestroySpeed(ItemStack stack, BlockState state) + { + if (state.is(BlockTags.MINEABLE_WITH_AXE)) return speed; + if (state.is(BlockTags.MINEABLE_WITH_PICKAXE)) return speed; + return super.getDestroySpeed(stack, state); + } + + @Override + public boolean isCorrectToolForDrops(ItemStack stack, BlockState state) + { + if (state.is(BlockTags.MINEABLE_WITH_PICKAXE)) + return TierSortingRegistry.isCorrectTierForDrops(Tiers.WOOD, state); + if (state.is(BlockTags.MINEABLE_WITH_AXE)) + return TierSortingRegistry.isCorrectTierForDrops(MY_TIER, state); + if (state.is(MY_TOOL_TAG)) + return TierSortingRegistry.isCorrectTierForDrops(MY_TIER, state); + return false; + } + }.setRegistryName("test_item")); + } +} diff --git a/src/test/resources/META-INF/mods.toml b/src/test/resources/META-INF/mods.toml index 64c572f401..f3cde6fec2 100644 --- a/src/test/resources/META-INF/mods.toml +++ b/src/test/resources/META-INF/mods.toml @@ -18,8 +18,6 @@ license="LGPL v2.1" modId="slipperiness_test" [[mods]] modId="custom_slime_block_test" -[[mods]] - modId="block_harvest_tool_test" [[mods]] modId="client_chat_event_test" [[mods]] @@ -144,5 +142,7 @@ license="LGPL v2.1" modId="entity_renderer_events_test" [[mods]] modId="send_datapacks_to_client" +[[mods]] + modId="tag_based_tool_types" # ADD ABOVE THIS LINE diff --git a/src/test/resources/data/forge/item_tier_ordering.json b/src/test/resources/data/forge/item_tier_ordering.json new file mode 100644 index 0000000000..33ed5f2e11 --- /dev/null +++ b/src/test/resources/data/forge/item_tier_ordering.json @@ -0,0 +1,3 @@ +{ + "order": [ "wood", "stone", "gold", "iron", "diamond", "netherite", "tag_based_tool_types:my_tier" ] +} \ No newline at end of file