WIP UndoRedo overhaul continued.
More validation in ServerBlockPlacer. Added BlockPlacerHelper.
This commit is contained in:
@@ -31,7 +31,7 @@ public class ClientConfig {
|
||||
maxBlockPreviews = builder
|
||||
.comment("Don't show block previews when placing more than this many blocks. " +
|
||||
"The outline will always be rendered.")
|
||||
.defineInRange("maxBlockPreviews", 500, 0, 5000);
|
||||
.defineInRange("maxBlockPreviews", 400, 0, 5000);
|
||||
|
||||
appearAnimationLength = builder
|
||||
.comment("How long it takes for a block to appear when placed in ticks.",
|
||||
|
||||
@@ -17,6 +17,7 @@ public class ServerConfig {
|
||||
public final BooleanValue allowInSurvival;
|
||||
public final BooleanValue useWhitelist;
|
||||
public final ConfigValue<List<? extends String>> whitelist;
|
||||
public final IntValue maxBlocksPlacedAtOnce;
|
||||
|
||||
public Validation(Builder builder) {
|
||||
builder.push("Validation");
|
||||
@@ -33,6 +34,10 @@ public class ServerConfig {
|
||||
.comment("List of player names that can use the mod.")
|
||||
.defineList("whitelist", Arrays.asList("Player1", "Player2"), o -> true);
|
||||
|
||||
maxBlocksPlacedAtOnce = builder
|
||||
.comment("Maximum number of blocks that can be placed at once.")
|
||||
.defineInRange("maxBlocksPlacedAtOnce", 1000, 1, 10000);
|
||||
|
||||
builder.pop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,6 +262,7 @@ public class BuilderChain {
|
||||
var clickedFace = lookingAt.getDirection();
|
||||
Vec3 relativeHitVec = lookingAt.getLocation().subtract(Vec3.atLowerCornerOf(lookingAt.getBlockPos()));
|
||||
|
||||
//TODO keep track of count and find different itemstack if necessary
|
||||
if (itemStack.getItem() instanceof BlockItem) {
|
||||
|
||||
for (BlockEntry blockEntry : blocks) {
|
||||
|
||||
@@ -10,6 +10,7 @@ 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.BlockPlacerHelper;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockSet;
|
||||
import nl.requios.effortlessbuilding.utilities.BlockUtilities;
|
||||
|
||||
@@ -17,12 +18,15 @@ import java.util.*;
|
||||
|
||||
// Receives block placement requests from the client and places them
|
||||
public class ServerBlockPlacer {
|
||||
private boolean isPlacingOrBreakingBlocks = false;
|
||||
|
||||
//region Delays
|
||||
private final Set<DelayedEntry> delayedEntries = Collections.synchronizedSet(new HashSet<>());
|
||||
private final Set<DelayedEntry> delayedEntriesView = Collections.unmodifiableSet(delayedEntries);
|
||||
private boolean isPlacingOrBreakingBlocks = false;
|
||||
|
||||
public void placeBlocksDelayed(Player player, BlockSet blocks, long placeTime) {
|
||||
if (!checkAndNotifyAllowedToUseMod(player)) return;
|
||||
if (!validateBlockSet(player, blocks)) return;
|
||||
|
||||
delayedEntries.add(new DelayedEntry(player, blocks, placeTime));
|
||||
}
|
||||
@@ -33,71 +37,103 @@ public class ServerBlockPlacer {
|
||||
DelayedEntry entry = iterator.next();
|
||||
long gameTime = entry.player.level.getGameTime();
|
||||
if (gameTime >= entry.placeTime) {
|
||||
placeBlocks(entry.player, entry.blocks);
|
||||
applyBlockSet(entry.player, entry.blocks);
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void placeBlocks(Player player, BlockSet blocks) {
|
||||
if (!checkAndNotifyAllowedToUseMod(player)) return;
|
||||
// EffortlessBuilding.log(player, "Placing " + blocks.size() + " blocks");
|
||||
|
||||
var undoSet = new BlockSet();
|
||||
for (BlockEntry block : blocks) {
|
||||
if (blocks.skipFirst && block.blockPos == blocks.firstPos) continue;
|
||||
if (placeBlock(player, block)) {
|
||||
undoSet.add(block);
|
||||
}
|
||||
}
|
||||
EffortlessBuilding.UNDO_REDO.addUndo(player, undoSet);
|
||||
public Set<DelayedEntry> getDelayedEntries() {
|
||||
return delayedEntriesView;
|
||||
}
|
||||
|
||||
private boolean placeBlock(Player player, BlockEntry block) {
|
||||
Level world = player.level;
|
||||
if (!world.isLoaded(block.blockPos)) return false;
|
||||
|
||||
isPlacingOrBreakingBlocks = true;
|
||||
boolean placedBlock = BlockUtilities.placeBlockEntry(player, block) == InteractionResult.SUCCESS;
|
||||
isPlacingOrBreakingBlocks = false;
|
||||
return placedBlock;
|
||||
}
|
||||
|
||||
public record DelayedEntry(Player player, BlockSet blocks, long placeTime) {}
|
||||
//endregion
|
||||
|
||||
public void breakBlocks(Player player, BlockSet blocks) {
|
||||
applyBlockSet(player, blocks);
|
||||
}
|
||||
|
||||
public void applyBlockSet(Player player, BlockSet blocks) {
|
||||
if (!checkAndNotifyAllowedToUseMod(player)) return;
|
||||
// EffortlessBuilding.log(player, "Breaking " + blocks.size() + " blocks");
|
||||
if (!validateBlockSet(player, blocks)) return;
|
||||
|
||||
var undoSet = new BlockSet();
|
||||
for (BlockEntry block : blocks) {
|
||||
if (blocks.skipFirst && block.blockPos == blocks.firstPos) continue;
|
||||
if (breakBlock(player, block)) {
|
||||
|
||||
if (applyBlockEntry(player, block)) {
|
||||
undoSet.add(block);
|
||||
}
|
||||
}
|
||||
EffortlessBuilding.UNDO_REDO.addUndo(player, undoSet);
|
||||
}
|
||||
|
||||
private boolean breakBlock(Player player, BlockEntry block) {
|
||||
ServerLevel world = (ServerLevel) player.level;
|
||||
if (!world.isLoaded(block.blockPos) || world.isEmptyBlock(block.blockPos)) return false;
|
||||
|
||||
isPlacingOrBreakingBlocks = true;
|
||||
boolean brokeBlock = BlockHelper.destroyBlockAs(world, block.blockPos, player, player.getMainHandItem(), 0f, stack -> {
|
||||
if (!player.isCreative()) {
|
||||
ItemHandlerHelper.giveItemToPlayer(player, stack);
|
||||
public void undoBlockSet(Player player, BlockSet blocks) {
|
||||
if (!isAllowedToUndo(player)) return;
|
||||
|
||||
var redoSet = new BlockSet();
|
||||
for (BlockEntry block : blocks) {
|
||||
if (blocks.skipFirst && block.blockPos == blocks.firstPos) continue;
|
||||
|
||||
if (undoBlockEntry(player, block)) {
|
||||
redoSet.add(block);
|
||||
}
|
||||
});
|
||||
isPlacingOrBreakingBlocks = false;
|
||||
return brokeBlock;
|
||||
}
|
||||
EffortlessBuilding.UNDO_REDO.addRedo(player, redoSet);
|
||||
}
|
||||
|
||||
public boolean checkAndNotifyAllowedToUseMod(Player player) {
|
||||
//TODO TEMP
|
||||
private boolean applyBlockEntry(Player player, BlockEntry block) {
|
||||
block.existingBlockState = player.level.getBlockState(block.blockPos);
|
||||
boolean breaking = BlockUtilities.isNullOrAir(block.newBlockState);
|
||||
if (!validateBlockEntry(player, block, breaking)) return false;
|
||||
|
||||
boolean success;
|
||||
isPlacingOrBreakingBlocks = true;
|
||||
if (breaking) {
|
||||
success = BlockPlacerHelper.breakBlock(player, block);
|
||||
} else {
|
||||
success = BlockPlacerHelper.placeBlock(player, block);
|
||||
}
|
||||
isPlacingOrBreakingBlocks = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
private boolean undoBlockEntry(Player player, BlockEntry block) {
|
||||
//Update newBlockState for future redo's
|
||||
block.newBlockState = player.level.getBlockState(block.blockPos);
|
||||
boolean breaking = BlockUtilities.isNullOrAir(block.existingBlockState);
|
||||
|
||||
var tempBlockEntry = new BlockEntry(block.blockPos);
|
||||
var newBlockState = block.existingBlockState;
|
||||
tempBlockEntry.existingBlockState = block.newBlockState;
|
||||
tempBlockEntry.newBlockState = newBlockState;
|
||||
|
||||
if (!validateBlockEntry(player, tempBlockEntry, breaking)) return false;
|
||||
|
||||
boolean success;
|
||||
isPlacingOrBreakingBlocks = true;
|
||||
if (breaking) {
|
||||
success = BlockPlacerHelper.placeBlock(player, tempBlockEntry);
|
||||
} else {
|
||||
success = BlockPlacerHelper.breakBlock(player, tempBlockEntry);
|
||||
}
|
||||
isPlacingOrBreakingBlocks = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
private boolean checkAndNotifyAllowedToUseMod(Player player) {
|
||||
//TODO temp no survival allowed
|
||||
if (!player.isCreative()) {
|
||||
EffortlessBuilding.log(player, ChatFormatting.RED + "Effortless Building is not yet supported in survival mode.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!player.getAbilities().mayBuild) {
|
||||
EffortlessBuilding.log(player, ChatFormatting.RED + "You are not allowed to build.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isAllowedToUseMod(player)) {
|
||||
EffortlessBuilding.log(player, ChatFormatting.RED + "You are not allowed to use Effortless Building.");
|
||||
return false;
|
||||
@@ -105,7 +141,7 @@ public class ServerBlockPlacer {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isAllowedToUseMod(Player player) {
|
||||
private boolean isAllowedToUseMod(Player player) {
|
||||
if (!ServerConfig.validation.allowInSurvival.get() && !player.isCreative()) return false;
|
||||
|
||||
if (ServerConfig.validation.useWhitelist.get()) {
|
||||
@@ -114,15 +150,53 @@ public class ServerBlockPlacer {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Set<DelayedEntry> getDelayedEntries() {
|
||||
return delayedEntriesView;
|
||||
|
||||
private boolean isAllowedToUndo(Player player) {
|
||||
if (!player.isCreative()) {
|
||||
EffortlessBuilding.log(player, ChatFormatting.RED + "Undo is not supported in survival mode.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean validateBlockSet(Player player, BlockSet blocks) {
|
||||
if (blocks.size() > ServerConfig.validation.maxBlocksPlacedAtOnce.get()) {
|
||||
EffortlessBuilding.log(player, ChatFormatting.RED + "Too many blocks to place. Max: " + ServerConfig.validation.maxBlocksPlacedAtOnce.get());
|
||||
return false;
|
||||
}
|
||||
|
||||
//Dont allow mixing breaking and placing blocks
|
||||
//TODO fix if skipping first block
|
||||
boolean breaking = blocks.getFirstBlockEntry().newBlockState == null || blocks.getFirstBlockEntry().newBlockState.isAir();
|
||||
for (var iterator = blocks.iterator(); iterator.hasNext(); ) {
|
||||
var block = iterator.next();
|
||||
if (block.newBlockState == null || block.newBlockState.isAir()) {
|
||||
if (!breaking) {
|
||||
EffortlessBuilding.log(player, ChatFormatting.RED + "Cannot mix breaking and placing blocks.");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (breaking) {
|
||||
EffortlessBuilding.log(player, ChatFormatting.RED + "Cannot mix breaking and placing blocks.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean validateBlockEntry(Player player, BlockEntry block, boolean breaking) {
|
||||
if (!player.level.isLoaded(block.blockPos)) return false;
|
||||
|
||||
if (breaking && BlockUtilities.isNullOrAir(block.existingBlockState)) return false;
|
||||
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isPlacingOrBreakingBlocks() {
|
||||
return isPlacingOrBreakingBlocks;
|
||||
}
|
||||
|
||||
public record DelayedEntry(Player player, BlockSet blocks, long placeTime) {}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ public class UndoRedo {
|
||||
undoStacks.get(player.getUUID()).push(blockSet);
|
||||
}
|
||||
|
||||
private void addRedo(Player player, BlockSet blockSet) {
|
||||
public void addRedo(Player player, BlockSet blockSet) {
|
||||
if (blockSet.isEmpty()) return;
|
||||
|
||||
//If no stack exists, make one
|
||||
@@ -49,10 +49,7 @@ public class UndoRedo {
|
||||
if (undoStack.isEmpty()) return false;
|
||||
|
||||
BlockSet blockSet = undoStack.pop();
|
||||
// blockSet.undo(player.level);
|
||||
|
||||
BlockSet redoSet = new BlockSet();
|
||||
addRedo(player, redoSet);
|
||||
EffortlessBuilding.SERVER_BLOCK_PLACER.undoBlockSet(player, blockSet);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -64,8 +61,7 @@ public class UndoRedo {
|
||||
if (redoStack.isEmpty()) return false;
|
||||
|
||||
BlockSet blockSet = redoStack.pop();
|
||||
// blockSet.redo(player.level);
|
||||
addUndo(player, blockSet);
|
||||
EffortlessBuilding.SERVER_BLOCK_PLACER.applyBlockSet(player, blockSet);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
package nl.requios.effortlessbuilding.utilities;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
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.DiggerItem;
|
||||
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.create.foundation.utility.BlockHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
//Server only
|
||||
public class BlockPlacerHelper {
|
||||
|
||||
public static boolean breakBlock(Player player, BlockEntry blockEntry) {
|
||||
ItemStack usedTool = player.getMainHandItem();
|
||||
if (usedTool.isEmpty() || !(usedTool.getItem() instanceof DiggerItem)) {
|
||||
ItemStack offhand = player.getOffhandItem();
|
||||
if (!offhand.isEmpty() && offhand.getItem() instanceof DiggerItem) {
|
||||
usedTool = offhand;
|
||||
}
|
||||
}
|
||||
|
||||
boolean brokeBlock = BlockHelper.destroyBlockAs(player.level, blockEntry.blockPos, player, usedTool, 0f, stack -> {
|
||||
if (!player.isCreative()) {
|
||||
ItemHandlerHelper.giveItemToPlayer(player, stack);
|
||||
}
|
||||
});
|
||||
return brokeBlock;
|
||||
}
|
||||
|
||||
public static boolean placeBlock(Player player, BlockEntry blockEntry) {
|
||||
if (blockEntry.itemStack == null) {
|
||||
return placeBlockWithoutItem(player, blockEntry);
|
||||
} else {
|
||||
var interactionResult = placeItem(player, blockEntry);
|
||||
interactionResult.shouldSwing()
|
||||
return interactionResult == InteractionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean placeBlockWithoutItem(Player player, BlockEntry blockEntry) {
|
||||
Level level = player.level;
|
||||
|
||||
level.captureBlockSnapshots = true;
|
||||
BlockHelper.placeSchematicBlock(level, player, blockEntry.newBlockState, blockEntry.blockPos, blockEntry.itemStack, null);
|
||||
level.captureBlockSnapshots = false;
|
||||
|
||||
//Find out if we get to keep the placed block by sending a forge event
|
||||
@SuppressWarnings("unchecked")
|
||||
List<BlockSnapshot> blockSnapshots = (List<BlockSnapshot>)level.capturedBlockSnapshots.clone();
|
||||
level.capturedBlockSnapshots.clear();
|
||||
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)
|
||||
{
|
||||
// revert back all captured blocks
|
||||
for (BlockSnapshot blocksnapshot : Lists.reverse(blockSnapshots))
|
||||
{
|
||||
level.restoringBlockSnapshots = true;
|
||||
blocksnapshot.restore(true, false);
|
||||
level.restoringBlockSnapshots = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
level.capturedBlockSnapshots.clear();
|
||||
return !eventResult;
|
||||
}
|
||||
|
||||
//ForgeHooks::onPlaceItemIntoWorld
|
||||
private static InteractionResult placeItem(Player player, BlockEntry block) {
|
||||
ItemStack itemstack = block.itemStack;
|
||||
Level level = player.level;
|
||||
|
||||
if (player != null && !player.getAbilities().mayBuild)
|
||||
return InteractionResult.PASS;
|
||||
|
||||
if (itemstack != null && !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;
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,10 @@ import java.util.List;
|
||||
//Common
|
||||
public class BlockUtilities {
|
||||
|
||||
public static boolean isNullOrAir(BlockState blockState) {
|
||||
return blockState == null || blockState.isAir();
|
||||
}
|
||||
|
||||
@Deprecated //Use BlockEntry.setItemStackAndFindNewBlockState instead
|
||||
public static BlockState getBlockState(Player player, InteractionHand hand, ItemStack blockItemStack, BlockEntry blockEntry, Vec3 relativeHitVec, Direction sideHit) {
|
||||
Block block = Block.byItem(blockItemStack.getItem());
|
||||
@@ -60,98 +64,6 @@ 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)
|
||||
|
||||
Reference in New Issue
Block a user