Added server validation: allowInSurvival and whitelist.

Instantly placing blocksets of size 1.
Added server config and moved some config values around.
This commit is contained in:
Christian Knaapen
2023-02-03 21:14:29 +01:00
parent 589059fd44
commit bda0908731
9 changed files with 236 additions and 165 deletions

View File

@@ -2,18 +2,22 @@ package nl.requios.effortlessbuilding;
import net.minecraftforge.common.ForgeConfigSpec;
import static net.minecraftforge.common.ForgeConfigSpec.*;
public class ClientConfig {
private static final ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder();
private static final Builder builder = new Builder();
public static final Visuals visuals = new Visuals(builder);
public static final ForgeConfigSpec spec = builder.build();
public static class Visuals {
public final ForgeConfigSpec.ConfigValue<Boolean> showBlockPreviews;
public final ForgeConfigSpec.ConfigValue<Boolean> onlyShowBlockPreviewsWhenBuilding;
public final ForgeConfigSpec.ConfigValue<Integer> maxBlockPreviews;
public final BooleanValue showBlockPreviews;
public final BooleanValue onlyShowBlockPreviewsWhenBuilding;
public final IntValue maxBlockPreviews;
public final IntValue appearAnimationLength;
public final IntValue breakAnimationLength;
public Visuals(ForgeConfigSpec.Builder builder) {
public Visuals(Builder builder) {
builder.push("Visuals");
showBlockPreviews = builder
@@ -27,8 +31,17 @@ public class ClientConfig {
maxBlockPreviews = builder
.comment("Don't show block previews when placing more than this many blocks. " +
"The outline will always be rendered.")
.define("maxBlockPreviews", 500);
.defineInRange("maxBlockPreviews", 500, 0, 5000);
appearAnimationLength = builder
.comment("How long it takes for a block to appear when placed in ticks.",
"Set to 0 to disable animation.")
.defineInRange("appearAnimationLength", 5, 0, 100);
breakAnimationLength = builder
.comment("How long the break animation is in ticks.",
"Set to 0 to disable animation.")
.defineInRange("breakAnimationLength", 10, 0, 100);
builder.pop();
}

View File

@@ -3,23 +3,24 @@ package nl.requios.effortlessbuilding;
import net.minecraftforge.common.ForgeConfigSpec;
import nl.requios.effortlessbuilding.create.foundation.render.SuperByteBufferCache;
import static net.minecraftforge.common.ForgeConfigSpec.*;
public class CommonConfig {
private static final ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder();
private static final Builder builder = new Builder();
public static final Reach reach = new Reach(builder);
public static final SurvivalBalancers survivalBalancers = new SurvivalBalancers(builder);
public static final Visuals visuals = new Visuals(builder);
public static final ForgeConfigSpec spec = builder.build();
public static class Reach {
public final ForgeConfigSpec.ConfigValue<Boolean> enableReachUpgrades;
public final ForgeConfigSpec.ConfigValue<Integer> maxReachCreative;
public final ForgeConfigSpec.ConfigValue<Integer> maxReachLevel0;
public final ForgeConfigSpec.ConfigValue<Integer> maxReachLevel1;
public final ForgeConfigSpec.ConfigValue<Integer> maxReachLevel2;
public final ForgeConfigSpec.ConfigValue<Integer> maxReachLevel3;
public final BooleanValue enableReachUpgrades;
public final IntValue maxReachCreative;
public final IntValue maxReachLevel0;
public final IntValue maxReachLevel1;
public final IntValue maxReachLevel2;
public final IntValue maxReachLevel3;
public Reach(ForgeConfigSpec.Builder builder) {
public Reach(Builder builder) {
builder.push("Reach");
enableReachUpgrades = builder
.comment("Reach: how far away the player can place blocks using mirror/array etc.",
@@ -30,35 +31,34 @@ public class CommonConfig {
maxReachCreative = builder
.comment("Maximum reach in creative",
"Keep in mind that chunks need to be loaded to be able to place blocks inside.")
.define("maxReachCreative", 200);
.defineInRange("maxReachCreative", 200, 0, 1000);
maxReachLevel0 = builder
.comment("Maximum reach in survival without upgrades",
"Reach upgrades are craftable consumables that permanently increase reach.",
"Set to 0 to disable Effortless Building until the player has consumed a reach upgrade.")
.define("maxReachLevel0", 20);
.defineInRange("maxReachLevel0", 20, 0, 1000);
maxReachLevel1 = builder
.comment("Maximum reach in survival with one upgrade")
.define("maxReachLevel1", 50);
.defineInRange("maxReachLevel1", 50, 0, 1000);
maxReachLevel2 = builder
.comment("Maximum reach in survival with two upgrades")
.define("maxReachLevel2", 100);
.defineInRange("maxReachLevel2", 100, 0, 1000);
maxReachLevel3 = builder
.comment("Maximum reach in survival with three upgrades")
.define("maxReachLevel3", 200);
.defineInRange("maxReachLevel3", 200, 0, 1000);
builder.pop();
}
}
public static class SurvivalBalancers {
public final ForgeConfigSpec.ConfigValue<Integer> quickReplaceMiningLevel;
public final ForgeConfigSpec.ConfigValue<Integer> undoStackSize;
public final IntValue quickReplaceMiningLevel;
public SurvivalBalancers(ForgeConfigSpec.Builder builder) {
public SurvivalBalancers(Builder builder) {
builder.push("SurvivalBalancers");
quickReplaceMiningLevel = builder
@@ -71,32 +71,6 @@ public class CommonConfig {
"4: blocks that can be harvested with netherite tools")
.defineInRange("quickReplaceMiningLevel", -1, -1, 3);
undoStackSize = builder
.comment("How many placements are remembered for the undo functionality.")
.worldRestart()
.define("undoStackSize", 50);
builder.pop();
}
}
public static class Visuals {
public final ForgeConfigSpec.ConfigValue<Integer> appearAnimationLength;
public final ForgeConfigSpec.ConfigValue<Integer> breakAnimationLength;
public Visuals(ForgeConfigSpec.Builder builder) {
builder.push("Visuals");
appearAnimationLength = builder
.comment("How long it takes for a block to appear when placed in ticks.",
"Set to 0 to disable animation.")
.define("appearAnimationLength", 5);
breakAnimationLength = builder
.comment("How long the break animation is in ticks.",
"Set to 0 to disable animation.")
.define("breakAnimationLength", 10);
builder.pop();
}
}

View File

@@ -74,6 +74,7 @@ public class EffortlessBuilding {
//Register config
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, CommonConfig.spec);
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ClientConfig.spec);
ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, ServerConfig.spec);
}
public static void setup(final FMLCommonSetupEvent event) {

View File

@@ -0,0 +1,55 @@
package nl.requios.effortlessbuilding;
import net.minecraftforge.common.ForgeConfigSpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static net.minecraftforge.common.ForgeConfigSpec.*;
public class ServerConfig {
private static final Builder builder = new Builder();
public static final ServerConfig.Validation validation = new ServerConfig.Validation(builder);
public static final ServerConfig.Memory memory = new ServerConfig.Memory(builder);
public static final ForgeConfigSpec spec = builder.build();
public static class Validation {
public final BooleanValue allowInSurvival;
public final BooleanValue useWhitelist;
public final ConfigValue<List<? extends String>> whitelist;
public Validation(Builder builder) {
builder.push("Validation");
allowInSurvival = builder
.comment("Allow use of the mod for players that are in survival mode. Otherwise, only creative mode players can use the mod.")
.define("allowInSurvival", true);
useWhitelist = builder
.comment("Use a whitelist to determine which players can use the mod. If false, all players can use the mod.")
.define("useWhitelist", false);
whitelist = builder
.comment("List of player names that can use the mod.")
.defineList("whitelist", Arrays.asList("Player1", "Player2"), o -> true);
builder.pop();
}
}
public static class Memory {
public final IntValue undoStackSize;
public Memory(Builder builder) {
builder.push("Memory");
undoStackSize = builder
.comment("How many placements are remembered for the undo functionality.")
.worldRestart()
.defineInRange("undoStackSize", 50, 10, 200);
builder.pop();
}
}
}

View File

@@ -40,7 +40,7 @@ public class BlockPreviews {
if (ClientConfig.visuals.showBlockPreviews.get()) {
for (PlacedBlocksEntry placed : placedBlocksList) {
int totalTime = placed.breaking ? CommonConfig.visuals.breakAnimationLength.get() : CommonConfig.visuals.appearAnimationLength.get();
int totalTime = placed.breaking ? ClientConfig.visuals.breakAnimationLength.get() : ClientConfig.visuals.appearAnimationLength.get();
if (totalTime <= 0) continue;
float dissolve = (ClientEvents.ticksInGame - placed.time) / (float) totalTime;
@@ -50,7 +50,7 @@ public class BlockPreviews {
//Expire
placedBlocksList.removeIf(placed -> {
int totalTime = placed.breaking ? CommonConfig.visuals.breakAnimationLength.get() : CommonConfig.visuals.appearAnimationLength.get();
int totalTime = placed.breaking ? ClientConfig.visuals.breakAnimationLength.get() : ClientConfig.visuals.appearAnimationLength.get();
return placed.time + totalTime < ClientEvents.ticksInGame;
});
}
@@ -228,7 +228,7 @@ public class BlockPreviews {
placedBlocksList.add(new PlacedBlocksEntry(ClientEvents.ticksInGame, false, new BlockSet(blocks)));
CreateClient.OUTLINER.keep(blocks.firstPos, CommonConfig.visuals.appearAnimationLength.get());
CreateClient.OUTLINER.keep(blocks.firstPos, ClientConfig.visuals.appearAnimationLength.get());
}
public void onBlocksBroken(BlockSet blocks) {
@@ -237,7 +237,7 @@ public class BlockPreviews {
placedBlocksList.add(new PlacedBlocksEntry(ClientEvents.ticksInGame, true, new BlockSet(blocks)));
CreateClient.OUTLINER.keep(blocks.firstPos, CommonConfig.visuals.breakAnimationLength.get());
CreateClient.OUTLINER.keep(blocks.firstPos, ClientConfig.visuals.breakAnimationLength.get());
}
private void sortOnDistanceToPlayer(List<BlockPos> coordinates, Player player) {

View File

@@ -17,6 +17,7 @@ import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.ClientConfig;
import nl.requios.effortlessbuilding.ClientEvents;
import nl.requios.effortlessbuilding.CommonConfig;
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
@@ -87,7 +88,8 @@ public class BuilderChain {
player.swing(InteractionHand.MAIN_HAND);
blocks.skipFirst = buildMode == BuildModeEnum.DISABLED;
long placeTime = player.level.getGameTime() + CommonConfig.visuals.appearAnimationLength.get() - 3;
long placeTime = player.level.getGameTime();
if (blocks.size() > 1) placeTime += ClientConfig.visuals.appearAnimationLength.get();
PacketHandler.INSTANCE.sendToServer(new ServerPlaceBlocksPacket(blocks, placeTime));
}
}

View File

@@ -1,26 +1,17 @@
package nl.requios.effortlessbuilding.systems;
import com.google.common.collect.Lists;
import net.minecraft.core.Direction;
import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.ChatFormatting;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.stats.Stats;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BucketItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.pattern.BlockInWorld;
import net.minecraftforge.common.util.BlockSnapshot;
import net.minecraftforge.event.ForgeEventFactory;
import net.minecraftforge.items.ItemHandlerHelper;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.ServerConfig;
import nl.requios.effortlessbuilding.create.foundation.utility.BlockHelper;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import nl.requios.effortlessbuilding.utilities.BlockUtilities;
import java.util.*;
@@ -31,6 +22,7 @@ public class ServerBlockPlacer {
private boolean isPlacingOrBreakingBlocks = false;
public void placeBlocksDelayed(Player player, BlockSet blocks, long placeTime) {
if (!checkAndNotifyAllowedToUseMod(player)) return;
delayedEntries.add(new DelayedEntry(player, blocks, placeTime));
}
@@ -46,6 +38,7 @@ public class ServerBlockPlacer {
}
public void placeBlocks(Player player, BlockSet blocks) {
if (!checkAndNotifyAllowedToUseMod(player)) return;
// EffortlessBuilding.log(player, "Placing " + blocks.size() + " blocks");
for (BlockEntry block : blocks) {
@@ -54,16 +47,17 @@ public class ServerBlockPlacer {
}
}
public void placeBlock(Player player, BlockEntry block) {
private void placeBlock(Player player, BlockEntry block) {
Level world = player.level;
if (!world.isLoaded(block.blockPos)) return;
isPlacingOrBreakingBlocks = true;
boolean placedBlock = onPlaceItemIntoWorld(player, block) == InteractionResult.SUCCESS;
boolean placedBlock = BlockUtilities.placeBlockEntry(player, block) == InteractionResult.SUCCESS;
isPlacingOrBreakingBlocks = false;
}
public void breakBlocks(Player player, BlockSet blocks) {
if (!checkAndNotifyAllowedToUseMod(player)) return;
// EffortlessBuilding.log(player, "Breaking " + blocks.size() + " blocks");
for (BlockEntry block : blocks) {
@@ -72,7 +66,7 @@ public class ServerBlockPlacer {
}
}
public void breakBlock(Player player, BlockEntry block) {
private void breakBlock(Player player, BlockEntry block) {
ServerLevel world = (ServerLevel) player.level;
if (!world.isLoaded(block.blockPos) || world.isEmptyBlock(block.blockPos)) return;
@@ -85,6 +79,24 @@ public class ServerBlockPlacer {
isPlacingOrBreakingBlocks = false;
}
public boolean checkAndNotifyAllowedToUseMod(Player player) {
if (!isAllowedToUseMod(player)) {
EffortlessBuilding.log(player, ChatFormatting.RED + "You are not allowed to use Effortless Building.");
return false;
}
return true;
}
public boolean isAllowedToUseMod(Player player) {
if (!ServerConfig.validation.allowInSurvival.get() && !player.isCreative()) return false;
if (ServerConfig.validation.useWhitelist.get()) {
return ServerConfig.validation.whitelist.get().contains(player.getGameProfile().getName());
}
return true;
}
public Set<DelayedEntry> getDelayedEntries() {
return delayedEntriesView;
}
@@ -95,96 +107,4 @@ public class ServerBlockPlacer {
public record DelayedEntry(Player player, BlockSet blocks, long placeTime) {}
//ForgeHooks::onPlaceItemIntoWorld
private InteractionResult onPlaceItemIntoWorld(Player player, BlockEntry block) {
ItemStack itemstack = block.itemStack;
Level level = player.level;
if (player != null && !player.getAbilities().mayBuild && !itemstack.hasAdventureModePlaceTagForBlock(level.registryAccess().registryOrThrow(Registry.BLOCK_REGISTRY), new BlockInWorld(level, block.blockPos, false)))
return InteractionResult.PASS;
// handle all placement events here
Item item = itemstack.getItem();
int size = itemstack.getCount();
CompoundTag nbt = null;
if (itemstack.getTag() != null)
nbt = itemstack.getTag().copy();
if (!(itemstack.getItem() instanceof BucketItem)) // if not bucket
level.captureBlockSnapshots = true;
ItemStack copy = itemstack.copy();
////
BlockHelper.placeSchematicBlock(level, player, block.newBlockState, block.blockPos, block.itemStack, null);
////
InteractionResult ret = InteractionResult.SUCCESS;
if (itemstack.isEmpty())
ForgeEventFactory.onPlayerDestroyItem(player, copy, InteractionHand.MAIN_HAND);
level.captureBlockSnapshots = false;
if (ret.consumesAction())
{
// save new item data
int newSize = itemstack.getCount();
CompoundTag newNBT = null;
if (itemstack.getTag() != null)
{
newNBT = itemstack.getTag().copy();
}
@SuppressWarnings("unchecked")
List<BlockSnapshot> blockSnapshots = (List<BlockSnapshot>)level.capturedBlockSnapshots.clone();
level.capturedBlockSnapshots.clear();
// make sure to set pre-placement item data for event
itemstack.setCount(size);
itemstack.setTag(nbt);
Direction side = Direction.UP;
boolean eventResult = false;
if (blockSnapshots.size() > 1)
{
eventResult = ForgeEventFactory.onMultiBlockPlace(player, blockSnapshots, side);
}
else if (blockSnapshots.size() == 1)
{
eventResult = ForgeEventFactory.onBlockPlace(player, blockSnapshots.get(0), side);
}
if (eventResult)
{
ret = InteractionResult.FAIL; // cancel placement
// revert back all captured blocks
for (BlockSnapshot blocksnapshot : Lists.reverse(blockSnapshots))
{
level.restoringBlockSnapshots = true;
blocksnapshot.restore(true, false);
level.restoringBlockSnapshots = false;
}
}
else
{
// Change the stack to its new content
itemstack.setCount(newSize);
itemstack.setTag(newNBT);
for (BlockSnapshot snap : blockSnapshots)
{
int updateFlag = snap.getFlag();
BlockState oldBlock = snap.getReplacedBlock();
BlockState newBlock = level.getBlockState(snap.getPos());
newBlock.onPlace(level, snap.getPos(), oldBlock, false);
level.markAndNotifyBlock(snap.getPos(), level.getChunkAt(snap.getPos()), oldBlock, newBlock, updateFlag, 512);
}
if (player != null)
player.awardStat(Stats.ITEM_USED.get(item));
}
}
level.capturedBlockSnapshots.clear();
return ret;
}
}

View File

@@ -10,6 +10,7 @@ import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import nl.requios.effortlessbuilding.CommonConfig;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.ServerConfig;
import nl.requios.effortlessbuilding.utilities.UndoRedoBlockSet;
import nl.requios.effortlessbuilding.utilities.FixedStack;
import nl.requios.effortlessbuilding.utilities.InventoryHelper;
@@ -49,7 +50,7 @@ public class UndoRedo {
//If no stack exists, make one
if (!undoStacks.containsKey(player.getUUID())) {
undoStacks.put(player.getUUID(), new FixedStack<>(new UndoRedoBlockSet[CommonConfig.survivalBalancers.undoStackSize.get()]));
undoStacks.put(player.getUUID(), new FixedStack<>(new UndoRedoBlockSet[ServerConfig.memory.undoStackSize.get()]));
}
undoStacks.get(player.getUUID()).push(blockSet);
@@ -62,7 +63,7 @@ public class UndoRedo {
//If no stack exists, make one
if (!redoStacks.containsKey(player.getUUID())) {
redoStacks.put(player.getUUID(), new FixedStack<>(new UndoRedoBlockSet[CommonConfig.survivalBalancers.undoStackSize.get()]));
redoStacks.put(player.getUUID(), new FixedStack<>(new UndoRedoBlockSet[ServerConfig.memory.undoStackSize.get()]));
}
redoStacks.get(player.getUUID()).push(blockSet);

View File

@@ -1,22 +1,35 @@
package nl.requios.effortlessbuilding.utilities;
import com.google.common.collect.Lists;
import net.minecraft.client.Minecraft;
import net.minecraft.core.Direction;
import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
import net.minecraft.stats.Stats;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BucketItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.pattern.BlockInWorld;
import net.minecraft.world.level.block.state.properties.Half;
import net.minecraft.world.level.block.state.properties.SlabType;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.util.BlockSnapshot;
import net.minecraftforge.event.ForgeEventFactory;
import nl.requios.effortlessbuilding.create.foundation.utility.BlockHelper;
import java.util.List;
//Common
public class BlockUtilities {
@@ -47,6 +60,98 @@ public class BlockUtilities {
return result;
}
//ForgeHooks::onPlaceItemIntoWorld
public static InteractionResult placeBlockEntry(Player player, BlockEntry block) {
ItemStack itemstack = block.itemStack;
Level level = player.level;
if (player != null && !player.getAbilities().mayBuild && !itemstack.hasAdventureModePlaceTagForBlock(level.registryAccess().registryOrThrow(Registry.BLOCK_REGISTRY), new BlockInWorld(level, block.blockPos, false)))
return InteractionResult.PASS;
// handle all placement events here
Item item = itemstack.getItem();
int size = itemstack.getCount();
CompoundTag nbt = null;
if (itemstack.getTag() != null)
nbt = itemstack.getTag().copy();
if (!(itemstack.getItem() instanceof BucketItem)) // if not bucket
level.captureBlockSnapshots = true;
ItemStack copy = itemstack.copy();
////
BlockHelper.placeSchematicBlock(level, player, block.newBlockState, block.blockPos, block.itemStack, null);
////
InteractionResult ret = InteractionResult.SUCCESS;
if (itemstack.isEmpty())
ForgeEventFactory.onPlayerDestroyItem(player, copy, InteractionHand.MAIN_HAND);
level.captureBlockSnapshots = false;
if (ret.consumesAction())
{
// save new item data
int newSize = itemstack.getCount();
CompoundTag newNBT = null;
if (itemstack.getTag() != null)
{
newNBT = itemstack.getTag().copy();
}
@SuppressWarnings("unchecked")
List<BlockSnapshot> blockSnapshots = (List<BlockSnapshot>)level.capturedBlockSnapshots.clone();
level.capturedBlockSnapshots.clear();
// make sure to set pre-placement item data for event
itemstack.setCount(size);
itemstack.setTag(nbt);
Direction side = Direction.UP;
boolean eventResult = false;
if (blockSnapshots.size() > 1)
{
eventResult = ForgeEventFactory.onMultiBlockPlace(player, blockSnapshots, side);
}
else if (blockSnapshots.size() == 1)
{
eventResult = ForgeEventFactory.onBlockPlace(player, blockSnapshots.get(0), side);
}
if (eventResult)
{
ret = InteractionResult.FAIL; // cancel placement
// revert back all captured blocks
for (BlockSnapshot blocksnapshot : Lists.reverse(blockSnapshots))
{
level.restoringBlockSnapshots = true;
blocksnapshot.restore(true, false);
level.restoringBlockSnapshots = false;
}
}
else
{
// Change the stack to its new content
itemstack.setCount(newSize);
itemstack.setTag(newNBT);
for (BlockSnapshot snap : blockSnapshots)
{
int updateFlag = snap.getFlag();
BlockState oldBlock = snap.getReplacedBlock();
BlockState newBlock = level.getBlockState(snap.getPos());
newBlock.onPlace(level, snap.getPos(), oldBlock, false);
level.markAndNotifyBlock(snap.getPos(), level.getChunkAt(snap.getPos()), oldBlock, newBlock, updateFlag, 512);
}
if (player != null)
player.awardStat(Stats.ITEM_USED.get(item));
}
}
level.capturedBlockSnapshots.clear();
return ret;
}
public static void playSoundIfFurtherThanNormal(Player player, BlockEntry blockEntry, boolean breaking) {
if (Minecraft.getInstance().hitResult != null && Minecraft.getInstance().hitResult.getType() == HitResult.Type.BLOCK)