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.
This commit is contained in:
David Quintana authored and GitHub committed 2021-08-10 15:45:08 -07:00
1 parent 52d7df06da
commit 10b51f257f
34 files changed
+1012 -441

No files matched your search

@@ -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<Block> 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<Block, ToolType, Integer> 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<Block, ToolType, Integer> setter)
{
blockToolSetter = setter;
}
private static final DummyBlockReader DUMMY_WORLD = new DummyBlockReader();
private static class DummyBlockReader implements BlockGetter {
@@ -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());
}
}
@@ -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();
}
@@ -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<Block> tag;
@Nonnull
private final Supplier<Ingredient> repairIngredient;
public ForgeTier(int level, int uses, float speed, float attackDamageBonus, int enchantmentValue,
@Nonnull Tag<Block> tag, @Nonnull Supplier<Ingredient> 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<Block> 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 + ']';
}
}
@@ -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<String> classes)
{
@@ -129,6 +129,10 @@ public class Tags
public static final IOptionalNamedTag<Block> STORAGE_BLOCKS_QUARTZ = tag("storage_blocks/quartz");
public static final IOptionalNamedTag<Block> STORAGE_BLOCKS_REDSTONE = tag("storage_blocks/redstone");
public static final IOptionalNamedTag<Block> NEEDS_WOOD_TOOL = tag("needs_wood_tool");
public static final IOptionalNamedTag<Block> NEEDS_GOLD_TOOL = tag("needs_gold_tool");
public static final IOptionalNamedTag<Block> NEEDS_NETHERITE_TOOL = tag("needs_netherite_tool");
private static IOptionalNamedTag<Block> tag(String name)
{
return BlockTags.createOptional(new ResourceLocation("forge", name));
@@ -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<Object> after, List<Object> 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<Tier> 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<Block> 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<Tier> 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<Object> afters, List<Object> 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<ResourceLocation, Tier> tiers = HashBiMap.create();
private static final Multimap<ResourceLocation, ResourceLocation> edges = HashMultimap.create();
private static final Multimap<ResourceLocation, ResourceLocation> 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<Tier> sortedTiers = new ArrayList<>();
private static final List<Tier> 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<JsonObject>()
{
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<Tier> 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<Tier> 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<Tier> graph = GraphBuilder.directed().nodeOrder(ElementOrder.<Tier>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<Tier> tierList = TopologicalSort.topologicalSort(graph, null);
setTierOrder(tierList);
}
private static void setTierOrder(List<Tier> 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<ResourceLocation> list = new ArrayList<>();
for(int i=0;i<count;i++)
list.add(buffer.readResourceLocation());
return new SyncPacket(list);
}
private static void handle(SyncPacket packet, Supplier<NetworkEvent.Context> context)
{
setTierOrder(packet.tiers.stream().map(TierSortingRegistry::byName).toList());
context.get().setPacketHandled(true);
}
private record SyncPacket(List<ResourceLocation> 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();
}
}
}
@@ -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<String, ToolAction> 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<ToolAction> 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;
}
}
@@ -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<ToolAction> DEFAULT_AXE_ACTIONS = Stream.of(AXE_DIG, AXE_STRIP, AXE_SCRAPE, AXE_WAX_OFF).collect(Collectors.toCollection(Sets::newIdentityHashSet));
public static final Set<ToolAction> DEFAULT_HOE_ACTIONS = Stream.of(HOE_DIG /* TODO: , HOE_TILL */).collect(Collectors.toCollection(Sets::newIdentityHashSet));
public static final Set<ToolAction> DEFAULT_SHOVEL_ACTIONS = Stream.of(SHOVEL_DIG, SHOVEL_FLATTEN).collect(Collectors.toCollection(Sets::newIdentityHashSet));
public static final Set<ToolAction> DEFAULT_PICKAXE_ACTIONS = Stream.of(PICKAXE_DIG).collect(Collectors.toCollection(Sets::newIdentityHashSet));
public static final Set<ToolAction> DEFAULT_SWORD_ACTIONS = Stream.of(SWORD_DIG).collect(Collectors.toCollection(Sets::newIdentityHashSet));
}
@@ -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<String, ToolType> 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;
}
}
@@ -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;
}
/**
@@ -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);
}
/**
@@ -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<ToolType> 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
*
@@ -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<CompoundTag>
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<ToolType> getToolTypes() {
return self().getItem().getToolTypes(self());
}
default InteractionResult onItemUseFirst(UseOnContext context)
{
Player entityplayer = context.getPlayer();
@@ -130,6 +115,19 @@ public interface IForgeItemStack extends ICapabilitySerializable<CompoundTag>
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.
@@ -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();
}
@@ -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;
}
/**