WIP UndoRedo overhaul continued.
More validation in ServerBlockPlacer. Added BlockPlacerHelper.
This commit is contained in:
@@ -31,7 +31,7 @@ public class ClientConfig {
|
|||||||
maxBlockPreviews = builder
|
maxBlockPreviews = builder
|
||||||
.comment("Don't show block previews when placing more than this many blocks. " +
|
.comment("Don't show block previews when placing more than this many blocks. " +
|
||||||
"The outline will always be rendered.")
|
"The outline will always be rendered.")
|
||||||
.defineInRange("maxBlockPreviews", 500, 0, 5000);
|
.defineInRange("maxBlockPreviews", 400, 0, 5000);
|
||||||
|
|
||||||
appearAnimationLength = builder
|
appearAnimationLength = builder
|
||||||
.comment("How long it takes for a block to appear when placed in ticks.",
|
.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 allowInSurvival;
|
||||||
public final BooleanValue useWhitelist;
|
public final BooleanValue useWhitelist;
|
||||||
public final ConfigValue<List<? extends String>> whitelist;
|
public final ConfigValue<List<? extends String>> whitelist;
|
||||||
|
public final IntValue maxBlocksPlacedAtOnce;
|
||||||
|
|
||||||
public Validation(Builder builder) {
|
public Validation(Builder builder) {
|
||||||
builder.push("Validation");
|
builder.push("Validation");
|
||||||
@@ -33,6 +34,10 @@ public class ServerConfig {
|
|||||||
.comment("List of player names that can use the mod.")
|
.comment("List of player names that can use the mod.")
|
||||||
.defineList("whitelist", Arrays.asList("Player1", "Player2"), o -> true);
|
.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();
|
builder.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -262,6 +262,7 @@ public class BuilderChain {
|
|||||||
var clickedFace = lookingAt.getDirection();
|
var clickedFace = lookingAt.getDirection();
|
||||||
Vec3 relativeHitVec = lookingAt.getLocation().subtract(Vec3.atLowerCornerOf(lookingAt.getBlockPos()));
|
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) {
|
if (itemStack.getItem() instanceof BlockItem) {
|
||||||
|
|
||||||
for (BlockEntry blockEntry : blocks) {
|
for (BlockEntry blockEntry : blocks) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import nl.requios.effortlessbuilding.EffortlessBuilding;
|
|||||||
import nl.requios.effortlessbuilding.ServerConfig;
|
import nl.requios.effortlessbuilding.ServerConfig;
|
||||||
import nl.requios.effortlessbuilding.create.foundation.utility.BlockHelper;
|
import nl.requios.effortlessbuilding.create.foundation.utility.BlockHelper;
|
||||||
import nl.requios.effortlessbuilding.utilities.BlockEntry;
|
import nl.requios.effortlessbuilding.utilities.BlockEntry;
|
||||||
|
import nl.requios.effortlessbuilding.utilities.BlockPlacerHelper;
|
||||||
import nl.requios.effortlessbuilding.utilities.BlockSet;
|
import nl.requios.effortlessbuilding.utilities.BlockSet;
|
||||||
import nl.requios.effortlessbuilding.utilities.BlockUtilities;
|
import nl.requios.effortlessbuilding.utilities.BlockUtilities;
|
||||||
|
|
||||||
@@ -17,12 +18,15 @@ import java.util.*;
|
|||||||
|
|
||||||
// Receives block placement requests from the client and places them
|
// Receives block placement requests from the client and places them
|
||||||
public class ServerBlockPlacer {
|
public class ServerBlockPlacer {
|
||||||
|
private boolean isPlacingOrBreakingBlocks = false;
|
||||||
|
|
||||||
|
//region Delays
|
||||||
private final Set<DelayedEntry> delayedEntries = Collections.synchronizedSet(new HashSet<>());
|
private final Set<DelayedEntry> delayedEntries = Collections.synchronizedSet(new HashSet<>());
|
||||||
private final Set<DelayedEntry> delayedEntriesView = Collections.unmodifiableSet(delayedEntries);
|
private final Set<DelayedEntry> delayedEntriesView = Collections.unmodifiableSet(delayedEntries);
|
||||||
private boolean isPlacingOrBreakingBlocks = false;
|
|
||||||
|
|
||||||
public void placeBlocksDelayed(Player player, BlockSet blocks, long placeTime) {
|
public void placeBlocksDelayed(Player player, BlockSet blocks, long placeTime) {
|
||||||
if (!checkAndNotifyAllowedToUseMod(player)) return;
|
if (!checkAndNotifyAllowedToUseMod(player)) return;
|
||||||
|
if (!validateBlockSet(player, blocks)) return;
|
||||||
|
|
||||||
delayedEntries.add(new DelayedEntry(player, blocks, placeTime));
|
delayedEntries.add(new DelayedEntry(player, blocks, placeTime));
|
||||||
}
|
}
|
||||||
@@ -33,71 +37,103 @@ public class ServerBlockPlacer {
|
|||||||
DelayedEntry entry = iterator.next();
|
DelayedEntry entry = iterator.next();
|
||||||
long gameTime = entry.player.level.getGameTime();
|
long gameTime = entry.player.level.getGameTime();
|
||||||
if (gameTime >= entry.placeTime) {
|
if (gameTime >= entry.placeTime) {
|
||||||
placeBlocks(entry.player, entry.blocks);
|
applyBlockSet(entry.player, entry.blocks);
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void placeBlocks(Player player, BlockSet blocks) {
|
public Set<DelayedEntry> getDelayedEntries() {
|
||||||
if (!checkAndNotifyAllowedToUseMod(player)) return;
|
return delayedEntriesView;
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean placeBlock(Player player, BlockEntry block) {
|
public record DelayedEntry(Player player, BlockSet blocks, long placeTime) {}
|
||||||
Level world = player.level;
|
//endregion
|
||||||
if (!world.isLoaded(block.blockPos)) return false;
|
|
||||||
|
|
||||||
isPlacingOrBreakingBlocks = true;
|
|
||||||
boolean placedBlock = BlockUtilities.placeBlockEntry(player, block) == InteractionResult.SUCCESS;
|
|
||||||
isPlacingOrBreakingBlocks = false;
|
|
||||||
return placedBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void breakBlocks(Player player, BlockSet blocks) {
|
public void breakBlocks(Player player, BlockSet blocks) {
|
||||||
|
applyBlockSet(player, blocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applyBlockSet(Player player, BlockSet blocks) {
|
||||||
if (!checkAndNotifyAllowedToUseMod(player)) return;
|
if (!checkAndNotifyAllowedToUseMod(player)) return;
|
||||||
// EffortlessBuilding.log(player, "Breaking " + blocks.size() + " blocks");
|
if (!validateBlockSet(player, blocks)) return;
|
||||||
|
|
||||||
var undoSet = new BlockSet();
|
var undoSet = new BlockSet();
|
||||||
for (BlockEntry block : blocks) {
|
for (BlockEntry block : blocks) {
|
||||||
if (blocks.skipFirst && block.blockPos == blocks.firstPos) continue;
|
if (blocks.skipFirst && block.blockPos == blocks.firstPos) continue;
|
||||||
if (breakBlock(player, block)) {
|
|
||||||
|
if (applyBlockEntry(player, block)) {
|
||||||
undoSet.add(block);
|
undoSet.add(block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EffortlessBuilding.UNDO_REDO.addUndo(player, undoSet);
|
EffortlessBuilding.UNDO_REDO.addUndo(player, undoSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean breakBlock(Player player, BlockEntry block) {
|
public void undoBlockSet(Player player, BlockSet blocks) {
|
||||||
ServerLevel world = (ServerLevel) player.level;
|
if (!isAllowedToUndo(player)) return;
|
||||||
if (!world.isLoaded(block.blockPos) || world.isEmptyBlock(block.blockPos)) return false;
|
|
||||||
|
|
||||||
|
var redoSet = new BlockSet();
|
||||||
|
for (BlockEntry block : blocks) {
|
||||||
|
if (blocks.skipFirst && block.blockPos == blocks.firstPos) continue;
|
||||||
|
|
||||||
|
if (undoBlockEntry(player, block)) {
|
||||||
|
redoSet.add(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EffortlessBuilding.UNDO_REDO.addRedo(player, redoSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
isPlacingOrBreakingBlocks = true;
|
||||||
boolean brokeBlock = BlockHelper.destroyBlockAs(world, block.blockPos, player, player.getMainHandItem(), 0f, stack -> {
|
if (breaking) {
|
||||||
if (!player.isCreative()) {
|
success = BlockPlacerHelper.breakBlock(player, block);
|
||||||
ItemHandlerHelper.giveItemToPlayer(player, stack);
|
} else {
|
||||||
|
success = BlockPlacerHelper.placeBlock(player, block);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
isPlacingOrBreakingBlocks = false;
|
isPlacingOrBreakingBlocks = false;
|
||||||
return brokeBlock;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean checkAndNotifyAllowedToUseMod(Player player) {
|
private boolean undoBlockEntry(Player player, BlockEntry block) {
|
||||||
//TODO TEMP
|
//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()) {
|
if (!player.isCreative()) {
|
||||||
EffortlessBuilding.log(player, ChatFormatting.RED + "Effortless Building is not yet supported in survival mode.");
|
EffortlessBuilding.log(player, ChatFormatting.RED + "Effortless Building is not yet supported in survival mode.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!player.getAbilities().mayBuild) {
|
||||||
|
EffortlessBuilding.log(player, ChatFormatting.RED + "You are not allowed to build.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isAllowedToUseMod(player)) {
|
if (!isAllowedToUseMod(player)) {
|
||||||
EffortlessBuilding.log(player, ChatFormatting.RED + "You are not allowed to use Effortless Building.");
|
EffortlessBuilding.log(player, ChatFormatting.RED + "You are not allowed to use Effortless Building.");
|
||||||
return false;
|
return false;
|
||||||
@@ -105,7 +141,7 @@ public class ServerBlockPlacer {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAllowedToUseMod(Player player) {
|
private boolean isAllowedToUseMod(Player player) {
|
||||||
if (!ServerConfig.validation.allowInSurvival.get() && !player.isCreative()) return false;
|
if (!ServerConfig.validation.allowInSurvival.get() && !player.isCreative()) return false;
|
||||||
|
|
||||||
if (ServerConfig.validation.useWhitelist.get()) {
|
if (ServerConfig.validation.useWhitelist.get()) {
|
||||||
@@ -115,14 +151,52 @@ public class ServerBlockPlacer {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<DelayedEntry> getDelayedEntries() {
|
private boolean isAllowedToUndo(Player player) {
|
||||||
return delayedEntriesView;
|
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() {
|
public boolean isPlacingOrBreakingBlocks() {
|
||||||
return 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);
|
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 (blockSet.isEmpty()) return;
|
||||||
|
|
||||||
//If no stack exists, make one
|
//If no stack exists, make one
|
||||||
@@ -49,10 +49,7 @@ public class UndoRedo {
|
|||||||
if (undoStack.isEmpty()) return false;
|
if (undoStack.isEmpty()) return false;
|
||||||
|
|
||||||
BlockSet blockSet = undoStack.pop();
|
BlockSet blockSet = undoStack.pop();
|
||||||
// blockSet.undo(player.level);
|
EffortlessBuilding.SERVER_BLOCK_PLACER.undoBlockSet(player, blockSet);
|
||||||
|
|
||||||
BlockSet redoSet = new BlockSet();
|
|
||||||
addRedo(player, redoSet);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -64,8 +61,7 @@ public class UndoRedo {
|
|||||||
if (redoStack.isEmpty()) return false;
|
if (redoStack.isEmpty()) return false;
|
||||||
|
|
||||||
BlockSet blockSet = redoStack.pop();
|
BlockSet blockSet = redoStack.pop();
|
||||||
// blockSet.redo(player.level);
|
EffortlessBuilding.SERVER_BLOCK_PLACER.applyBlockSet(player, blockSet);
|
||||||
addUndo(player, blockSet);
|
|
||||||
|
|
||||||
return true;
|
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
|
//Common
|
||||||
public class BlockUtilities {
|
public class BlockUtilities {
|
||||||
|
|
||||||
|
public static boolean isNullOrAir(BlockState blockState) {
|
||||||
|
return blockState == null || blockState.isAir();
|
||||||
|
}
|
||||||
|
|
||||||
@Deprecated //Use BlockEntry.setItemStackAndFindNewBlockState instead
|
@Deprecated //Use BlockEntry.setItemStackAndFindNewBlockState instead
|
||||||
public static BlockState getBlockState(Player player, InteractionHand hand, ItemStack blockItemStack, BlockEntry blockEntry, Vec3 relativeHitVec, Direction sideHit) {
|
public static BlockState getBlockState(Player player, InteractionHand hand, ItemStack blockItemStack, BlockEntry blockEntry, Vec3 relativeHitVec, Direction sideHit) {
|
||||||
Block block = Block.byItem(blockItemStack.getItem());
|
Block block = Block.byItem(blockItemStack.getItem());
|
||||||
@@ -60,98 +64,6 @@ public class BlockUtilities {
|
|||||||
return result;
|
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) {
|
public static void playSoundIfFurtherThanNormal(Player player, BlockEntry blockEntry, boolean breaking) {
|
||||||
|
|
||||||
if (Minecraft.getInstance().hitResult != null && Minecraft.getInstance().hitResult.getType() == HitResult.Type.BLOCK)
|
if (Minecraft.getInstance().hitResult != null && Minecraft.getInstance().hitResult.getType() == HitResult.Type.BLOCK)
|
||||||
|
|||||||
Reference in New Issue
Block a user