6197fd81c1
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
545 Zeilen
25 KiB
Java
545 Zeilen
25 KiB
Java
/*
|
|
* This file is a part of the SteamWar software.
|
|
*
|
|
* Copyright (C) 2023 SteamWar.de-Serverteam
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package de.steamwar.bausystem.utils;
|
|
|
|
import com.comphenix.tinyprotocol.Reflection;
|
|
import lombok.AllArgsConstructor;
|
|
import lombok.Getter;
|
|
import lombok.experimental.UtilityClass;
|
|
import org.bukkit.*;
|
|
import org.bukkit.block.*;
|
|
import org.bukkit.block.data.*;
|
|
import org.bukkit.block.data.type.Hopper;
|
|
import org.bukkit.block.data.type.Observer;
|
|
import org.bukkit.block.data.type.*;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.event.block.BlockCanBuildEvent;
|
|
import org.bukkit.event.block.BlockPlaceEvent;
|
|
import org.bukkit.inventory.EquipmentSlot;
|
|
import org.bukkit.inventory.ItemStack;
|
|
import org.bukkit.inventory.meta.*;
|
|
import org.bukkit.util.RayTraceResult;
|
|
import org.bukkit.util.Vector;
|
|
|
|
import java.util.*;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.util.stream.Collectors;
|
|
|
|
@UtilityClass
|
|
public class PlaceItemUtils {
|
|
|
|
// https://github.com/Articdive/ArticData/blob/1.20.1/1_20_1_tags/1_20_1_block_tags.json
|
|
// #minecraft:replaceable
|
|
private static final Set<String> replaceables;
|
|
|
|
static {
|
|
replaceables = new HashSet<>(Arrays.asList(
|
|
"minecraft:air",
|
|
"minecraft:water",
|
|
"minecraft:lava",
|
|
"minecraft:grass",
|
|
"minecraft:fern",
|
|
"minecraft:dead_bush",
|
|
"minecraft:seagrass",
|
|
"minecraft:tall_seagrass",
|
|
"minecraft:fire",
|
|
"minecraft:soul_fire",
|
|
"minecraft:snow",
|
|
"minecraft:vine",
|
|
"minecraft:glow_lichen",
|
|
"minecraft:light",
|
|
"minecraft:tall_grass",
|
|
"minecraft:large_fern",
|
|
"minecraft:structure_void",
|
|
"minecraft:void_air",
|
|
"minecraft:cave_air",
|
|
"minecraft:bubble_column",
|
|
"minecraft:warped_roots",
|
|
"minecraft:nether_sprouts",
|
|
"minecraft:crimson_roots",
|
|
"minecraft:hanging_roots"))
|
|
.stream()
|
|
.map(s -> s.substring(10))
|
|
.map(String::toUpperCase)
|
|
.collect(Collectors.toSet());
|
|
}
|
|
|
|
private static final Class<?> blockPosition = Reflection.getClass("{nms.core}.BlockPosition");
|
|
private static final Reflection.ConstructorInvoker blockPositionConstructor = Reflection.getConstructor(blockPosition, int.class, int.class, int.class);
|
|
private static final Class<?> craftBlock = Reflection.getClass("{obc}.block.CraftBlockState");
|
|
private static final Class<?> craftWorld = Reflection.getClass("{obc}.CraftWorld");
|
|
private static final Reflection.FieldAccessor<?> positionAccessor = Reflection.getField(craftBlock, blockPosition, 0);
|
|
private static final Reflection.FieldAccessor<?> worldAccessor = Reflection.getField(craftBlock, craftWorld, 0);
|
|
|
|
/**
|
|
* Attempt to place an {@link ItemStack} the {@link Player} is holding against a {@link Block} inside the World.
|
|
* This can be easily used inside the {@link org.bukkit.event.player.PlayerInteractEvent} to mimik placing a
|
|
* block without any minecraft related GUI's etc. executing.
|
|
*
|
|
* @param player the Player placing the block
|
|
* @param itemStack the ItemStack to be placed
|
|
* @param against the Block at which the player aims (does not need to be in range of the player)
|
|
* @param againstSide the BlockFace the player aims
|
|
* @param hand the Hand the player is using
|
|
* @param force allow illegal states to be created by placing the block
|
|
* @param applyPhysics apply physics while placing the block
|
|
* @param rotateAway rotate everything in the opposite direction, so a block facing the Player will face away, and the other way round
|
|
* @param playSound enables sound of placing
|
|
*/
|
|
public PlaceItemResult placeItem(Player player, ItemStack itemStack, Block against, BlockFace againstSide, EquipmentSlot hand, boolean force, boolean applyPhysics, boolean rotateAway, boolean playSound) {
|
|
// If the ItemStack is null or air we cannot place it
|
|
if (itemStack == null) return PlaceItemResult.NO_ITEM_HELD;
|
|
if (itemStack.getType().isAir()) return PlaceItemResult.NO_ITEM_HELD;
|
|
|
|
// This Block should be replaced by the new Block
|
|
Block block = against.getRelative(againstSide);
|
|
|
|
// We cannot place any Item that is not also a Block, this is checked by testing for the ItemMeta
|
|
ItemMeta itemMeta = itemStack.getItemMeta();
|
|
if (!(itemMeta instanceof BlockDataMeta)) {
|
|
return PlaceItemResult.NO_BLOCK_ITEM_HELD;
|
|
}
|
|
|
|
BlockDataMeta blockDataMeta = (BlockDataMeta) itemMeta;
|
|
|
|
// Converting the Item Material to a Block Material
|
|
// e.g. Material.REDSTONE -> Material.REDSTONE_WIRE
|
|
Material typeToPlace = PlaceItemWrapper.ITEM_MATERIAL_TO_BLOCK_MATERIAL.getOrDefault(itemStack.getType(), itemStack.getType());
|
|
|
|
BlockData blockData = null;
|
|
AtomicBoolean usedForcePlace = new AtomicBoolean();
|
|
if (againstSide == BlockFace.NORTH || againstSide == BlockFace.SOUTH || againstSide == BlockFace.EAST || againstSide == BlockFace.WEST) {
|
|
// Try Wall Placement first
|
|
blockData = toBlockData(player, blockDataMeta, PlaceItemWrapper.BLOCK_MATERIAL_TO_WALL_BLOCK_MATERIAL.getOrDefault(typeToPlace, typeToPlace));
|
|
if (blockData != null && !canPlace(block, blockData, force, usedForcePlace)) {
|
|
// Check if default Rotation from input could be valid
|
|
BlockFace rotation = getRotation(blockData);
|
|
setRotation(blockData, againstSide);
|
|
if (!canPlace(block, blockData, force, usedForcePlace)) {
|
|
setRotation(blockData, rotation);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Try default Placement
|
|
if (blockData == null || !canPlace(block, blockData, force, usedForcePlace)) {
|
|
blockData = toBlockData(player, blockDataMeta, typeToPlace);
|
|
}
|
|
|
|
if (blockData != null && !canPlace(block, blockData, force, usedForcePlace)) {
|
|
if (blockData instanceof FaceAttachable) {
|
|
// FaceAttachable Blocks should be placed on the Ceiling/Floor if possible
|
|
// This applies mainly to Lever and Buttons
|
|
FaceAttachable faceAttachable = (FaceAttachable) blockData;
|
|
boolean topFirst = isHitHalfTop(player);
|
|
faceAttachable.setAttachedFace(topFirst ? FaceAttachable.AttachedFace.CEILING : FaceAttachable.AttachedFace.FLOOR);
|
|
if (!canPlace(block, blockData, force, usedForcePlace)) {
|
|
faceAttachable.setAttachedFace(topFirst ? FaceAttachable.AttachedFace.FLOOR : FaceAttachable.AttachedFace.CEILING);
|
|
}
|
|
if (!canPlace(block, blockData, force, usedForcePlace)) {
|
|
return PlaceItemResult.NO_VALID_PLACEMENT;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (blockData == null) return PlaceItemResult.NO_BLOCK_ITEM_HELD;
|
|
|
|
// Placing a Block inside of Water should set it to Waterlogged
|
|
if (blockData instanceof Waterlogged && block.getType() == Material.WATER) {
|
|
Levelled levelled = (Levelled) block.getBlockData();
|
|
((Waterlogged) blockData).setWaterlogged(levelled.getLevel() == levelled.getMaximumLevel());
|
|
}
|
|
|
|
if (blockData instanceof Slab) {
|
|
// Slabs can be set at Top or Bottom
|
|
((Slab) blockData).setType(isHitHalfTop(player) ? Slab.Type.TOP : Slab.Type.BOTTOM);
|
|
if (againstSide == BlockFace.DOWN) {
|
|
((Slab) blockData).setType(Slab.Type.TOP);
|
|
}
|
|
} else if (blockData instanceof Stairs) {
|
|
// Stairs can be set at Top or Bottom
|
|
((Stairs) blockData).setHalf(isHitHalfTop(player) ? Bisected.Half.TOP : Bisected.Half.BOTTOM);
|
|
if (againstSide == BlockFace.DOWN) {
|
|
((Stairs) blockData).setHalf(Bisected.Half.TOP);
|
|
}
|
|
} else if (blockData instanceof TrapDoor) {
|
|
// TrapDoors can be set at Top or Bottom
|
|
((TrapDoor) blockData).setHalf(isHitHalfTop(player) ? Bisected.Half.TOP : Bisected.Half.BOTTOM);
|
|
if (againstSide == BlockFace.DOWN) {
|
|
((TrapDoor) blockData).setHalf(Bisected.Half.TOP);
|
|
}
|
|
} else if (blockData instanceof Chain) {
|
|
// Chains are always rotated against the block you place against
|
|
Orientable orientable = (Orientable) blockData;
|
|
switch (againstSide) {
|
|
case EAST:
|
|
case WEST:
|
|
orientable.setAxis(Axis.X);
|
|
break;
|
|
case UP:
|
|
case DOWN:
|
|
orientable.setAxis(Axis.Y);
|
|
break;
|
|
case NORTH:
|
|
case SOUTH:
|
|
orientable.setAxis(Axis.Z);
|
|
break;
|
|
}
|
|
} else if (blockData instanceof Hopper && (againstSide == BlockFace.UP || againstSide == BlockFace.DOWN)) {
|
|
// Placing at the Top or Bottom of a Block result in a downwards facing Hopper
|
|
((Hopper) blockData).setFacing(BlockFace.DOWN);
|
|
} else if (blockData instanceof Directional && (blockData.getMaterial().name().endsWith("_HEAD") || blockData.getMaterial().name().endsWith("_SKULL")) && againstSide != BlockFace.DOWN && againstSide != BlockFace.UP) {
|
|
// Skulls and Heads are always rotated towards you if not in Wall variant
|
|
((Directional) blockData).setFacing(againstSide);
|
|
} else if (blockData instanceof LightningRod) {
|
|
// Lightning Rod is always rotated against the block you place against
|
|
((Directional) blockData).setFacing(againstSide);
|
|
}
|
|
if (force && blockData instanceof FaceAttachable) {
|
|
// Forcing to Place a FaceAttachable against the Block you specified. Needs the force flag to be set
|
|
FaceAttachable faceAttachable = (FaceAttachable) blockData;
|
|
if (blockData instanceof Switch && againstSide == BlockFace.DOWN) {
|
|
faceAttachable.setAttachedFace(FaceAttachable.AttachedFace.CEILING);
|
|
} else if (againstSide == BlockFace.UP) {
|
|
faceAttachable.setAttachedFace(FaceAttachable.AttachedFace.FLOOR);
|
|
} else if (blockData instanceof Directional) {
|
|
((Directional) blockData).setFacing(againstSide);
|
|
}
|
|
if (blockData instanceof Switch) {
|
|
// Levers and Buttons are always Rotated the other way
|
|
Switch switchType = (Switch) blockData;
|
|
switch (switchType.getAttachedFace()) {
|
|
case FLOOR:
|
|
case CEILING:
|
|
switchType.setFacing(switchType.getFacing().getOppositeFace());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (force && blockData instanceof Directional && !(blockData instanceof FaceAttachable) && PlaceItemWrapper.BLOCK_MATERIAL_TO_WALL_BLOCK_MATERIAL.containsValue(blockData.getMaterial())) {
|
|
Directional directional = (Directional) blockData;
|
|
if (directional.getFaces().contains(againstSide)) {
|
|
directional.setFacing(againstSide);
|
|
}
|
|
}
|
|
if (force && blockData instanceof Rotatable && !(blockData instanceof FaceAttachable)) {
|
|
Rotatable rotatable = (Rotatable) blockData;
|
|
if (againstSide != BlockFace.UP && againstSide != BlockFace.DOWN) {
|
|
rotatable.setRotation(againstSide);
|
|
}
|
|
}
|
|
|
|
if (blockData instanceof RedstoneWire) {
|
|
// Redstone Wire is connected to every Side by default
|
|
RedstoneWire redstoneWire = (RedstoneWire) blockData;
|
|
redstoneWire.setFace(BlockFace.NORTH, RedstoneWire.Connection.SIDE);
|
|
redstoneWire.setFace(BlockFace.SOUTH, RedstoneWire.Connection.SIDE);
|
|
redstoneWire.setFace(BlockFace.EAST, RedstoneWire.Connection.SIDE);
|
|
redstoneWire.setFace(BlockFace.WEST, RedstoneWire.Connection.SIDE);
|
|
}
|
|
|
|
if (rotateAway) {
|
|
// Rotate the other way if rotateAway is set to true
|
|
BlockFace blockFace = getRotation(blockData);
|
|
if (blockFace != null) {
|
|
blockFace = blockFace.getOppositeFace();
|
|
if (blockData instanceof Hopper && (blockFace == BlockFace.UP || blockFace == BlockFace.DOWN)) {
|
|
((Hopper) blockData).setFacing(BlockFace.DOWN);
|
|
} else {
|
|
setRotation(blockData, blockFace);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if the Block can be build
|
|
BlockCanBuildEvent blockCanBuildEvent = new BlockCanBuildEvent(against, player, blockData, canPlace(block, blockData, force, usedForcePlace));
|
|
Bukkit.getPluginManager().callEvent(blockCanBuildEvent);
|
|
if (!blockCanBuildEvent.isBuildable()) return PlaceItemResult.NO_BUILD;
|
|
|
|
BlockState oldState = block.getState();
|
|
|
|
// Retrieve the BlockState of the ItemStack if present
|
|
BlockState blockState = null;
|
|
if (itemMeta instanceof BlockStateMeta) {
|
|
blockState = ((BlockStateMeta) itemMeta).getBlockState();
|
|
}
|
|
|
|
if (blockState == null) {
|
|
// If no BlockState is present use the BlockState of the Block you want to edit
|
|
blockState = block.getState();
|
|
} else {
|
|
// If a BlockState is present set the Position and World to the Block you want to place
|
|
Location blockLocation = block.getLocation();
|
|
positionAccessor.set(blockState, blockPositionConstructor.invoke(blockLocation.getBlockX(), blockLocation.getBlockY(), blockLocation.getBlockZ()));
|
|
worldAccessor.set(blockState, blockLocation.getWorld());
|
|
}
|
|
|
|
if (blockData.getMaterial().isSolid()) {
|
|
for (Player p : Bukkit.getOnlinePlayers()) {
|
|
Location min = p.getLocation().add(-0.3, 0, -0.3);
|
|
Location max = p.getLocation().add(0.3, p.isSneaking() ? 1.5 : 1.8, 0.3);
|
|
|
|
Location blockmin = block.getLocation();
|
|
Location blockmax = block.getLocation().add(1.0, 1.0, 1.0);
|
|
if (
|
|
!(max.getX() <= blockmin.getX() || min.getX() >= blockmax.getX() ||
|
|
max.getY() <= blockmin.getY() || min.getY() >= blockmax.getY() ||
|
|
max.getZ() <= blockmin.getZ() || min.getZ() >= blockmax.getZ())
|
|
) {
|
|
return PlaceItemResult.NO_PLACE_INSIDE_PLAYER;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the generated BlockData to the BlockState and update the world without physics
|
|
blockState.setBlockData(blockData);
|
|
blockState.update(true, false);
|
|
|
|
// Check if the Block is allowed to be placed
|
|
BlockPlaceEvent blockPlaceEvent = new BlockPlaceEvent(block, oldState, against, itemStack, player, true, hand);
|
|
Bukkit.getPluginManager().callEvent(blockCanBuildEvent);
|
|
if (blockPlaceEvent.isCancelled() || !blockPlaceEvent.canBuild()) {
|
|
// Reset world
|
|
oldState.update(true, false);
|
|
return PlaceItemResult.NO_PLACE;
|
|
}
|
|
|
|
if (hasSecondBlock(blockData)) {
|
|
// Place tht second block of a Door or Tallgrass.
|
|
Bisected bisected = (Bisected) blockData;
|
|
Block blockAbove = block.getRelative(0, 1, 0);
|
|
bisected.setHalf(Bisected.Half.TOP);
|
|
if (canPlace(blockAbove, blockData, force, usedForcePlace)) {
|
|
if (blockData instanceof Waterlogged) {
|
|
((Waterlogged) blockData).setWaterlogged(blockAbove.getType() == Material.WATER);
|
|
}
|
|
blockAbove.setBlockData(blockData, applyPhysics);
|
|
} else {
|
|
// If the second Block couldn't be placed remove the first Block as well
|
|
oldState.update(true, false);
|
|
return PlaceItemResult.NO_DOUBLE_HIGH_BLOCK_SPACE;
|
|
}
|
|
}
|
|
if (applyPhysics) {
|
|
// Apply Physics by placing the old State without Physics and setting the new with physics
|
|
oldState.update(true, false);
|
|
blockState.update(true, true);
|
|
}
|
|
|
|
if (itemMeta instanceof BannerMeta) {
|
|
// Apply Banner Patterns to now placed Block in World
|
|
BannerMeta bannerMeta = (BannerMeta) itemMeta;
|
|
Banner banner = (Banner) block.getState();
|
|
banner.setPatterns(bannerMeta.getPatterns());
|
|
banner.update(true, false);
|
|
} else if (itemMeta instanceof SkullMeta) {
|
|
// Apply Skull Data to now placed Block in World
|
|
SkullMeta skullMeta = (SkullMeta) itemMeta;
|
|
Skull skull = (Skull) block.getState();
|
|
skull.setOwnerProfile(skullMeta.getOwnerProfile());
|
|
if (skullMeta.getOwningPlayer() != null) {
|
|
skull.setOwningPlayer(skullMeta.getOwningPlayer());
|
|
}
|
|
skull.update(true, false);
|
|
}
|
|
|
|
if (playSound) {
|
|
// Play the corresponding sound of placing the now placed Block
|
|
SoundGroup soundGroup = blockData.getSoundGroup();
|
|
block.getWorld().playSound(block.getLocation(), soundGroup.getPlaceSound(), soundGroup.getVolume() * 0.8F, soundGroup.getPitch() * 0.8F);
|
|
}
|
|
return usedForcePlace.get() ? PlaceItemResult.SUCCESS_FORCE : PlaceItemResult.SUCCESS;
|
|
}
|
|
|
|
public BlockFace[] axis = { BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST };
|
|
public BlockFace[] radial = { BlockFace.NORTH, BlockFace.NORTH_EAST, BlockFace.EAST, BlockFace.SOUTH_EAST, BlockFace.SOUTH, BlockFace.SOUTH_WEST, BlockFace.WEST, BlockFace.NORTH_WEST };
|
|
|
|
public BlockFace yawToFace(float yaw) {
|
|
return radial[Math.round(yaw / 45f) & 0x7];
|
|
}
|
|
|
|
public BlockFace yawToFaceAxis(float yaw) {
|
|
return axis[Math.round(yaw / 90f) & 0x3];
|
|
}
|
|
|
|
public BlockFace toFace(Set<BlockFace> faces, float pitch, float yaw) {
|
|
if (faces.contains(BlockFace.UP) && pitch >= 45.0) {
|
|
return BlockFace.UP;
|
|
}
|
|
if (faces.contains(BlockFace.DOWN) && pitch <= -45.0) {
|
|
return BlockFace.DOWN;
|
|
}
|
|
return yawToFaceAxis(yaw);
|
|
}
|
|
|
|
public BlockData toBlockData(Player player, BlockDataMeta blockDataMeta, Material material) {
|
|
BlockData blockData;
|
|
try {
|
|
blockData = blockDataMeta.getBlockData(material);
|
|
} catch (NullPointerException e) {
|
|
// Some items have a BlockDataMeta but they cannot be converted to one like ItemFrame, those will be ignored
|
|
return null;
|
|
}
|
|
|
|
if (blockData instanceof Stairs || blockData instanceof Observer || blockData instanceof Hopper || blockData instanceof Door) {
|
|
// Stairs, Observer, Hopper and Doors are placed the opposite way
|
|
Directional directional = (Directional) blockData;
|
|
BlockFace face = toFace(directional.getFaces(), player.getLocation().getPitch(), player.getLocation().getYaw());
|
|
directional.setFacing(face.getOppositeFace());
|
|
} else if (blockData instanceof Orientable) {
|
|
// Orientable only have 3 Axis: X, Y, Z
|
|
Orientable orientable = (Orientable) blockData;
|
|
Set<BlockFace> faces = new HashSet<>();
|
|
Set<Axis> axisSet = orientable.getAxes();
|
|
if (axisSet.contains(Axis.X)) {
|
|
faces.add(BlockFace.EAST);
|
|
faces.add(BlockFace.WEST);
|
|
}
|
|
if (axisSet.contains(Axis.Y)) {
|
|
faces.add(BlockFace.UP);
|
|
faces.add(BlockFace.DOWN);
|
|
}
|
|
if (axisSet.contains(Axis.Z)) {
|
|
faces.add(BlockFace.NORTH);
|
|
faces.add(BlockFace.SOUTH);
|
|
}
|
|
BlockFace face = toFace(faces, player.getLocation().getPitch(), player.getLocation().getYaw());
|
|
switch (face) {
|
|
case EAST:
|
|
case WEST:
|
|
orientable.setAxis(Axis.X);
|
|
break;
|
|
case UP:
|
|
case DOWN:
|
|
orientable.setAxis(Axis.Y);
|
|
break;
|
|
case NORTH:
|
|
case SOUTH:
|
|
orientable.setAxis(Axis.Z);
|
|
break;
|
|
}
|
|
} else if (blockData instanceof Rotatable) {
|
|
Rotatable rotatable = (Rotatable) blockData;
|
|
BlockFace blockeFace = yawToFace(player.getLocation().getYaw());
|
|
if (blockData.getMaterial().name().endsWith("_HEAD") || blockData.getMaterial().name().endsWith("_SKULL")) {
|
|
// Wall Heads and Wall Skulls are placed the opposite way
|
|
rotatable.setRotation(blockeFace.getOppositeFace());
|
|
} else {
|
|
rotatable.setRotation(blockeFace);
|
|
}
|
|
} else if (blockData instanceof Directional) {
|
|
Directional directional = (Directional) blockData;
|
|
directional.setFacing(toFace(directional.getFaces(), player.getLocation().getPitch(), player.getLocation().getYaw()));
|
|
} else if (blockData instanceof Rail) {
|
|
Rail rail = (Rail) blockData;
|
|
BlockFace face = yawToFaceAxis(player.getLocation().getYaw());
|
|
// Rails are only represented by 2 States, North_South or East_West the remaining States will be ignored here
|
|
rail.setShape(face == BlockFace.NORTH || face == BlockFace.SOUTH ? Rail.Shape.NORTH_SOUTH : Rail.Shape.EAST_WEST);
|
|
}
|
|
|
|
return blockData;
|
|
}
|
|
|
|
private BlockFace getRotation(BlockData blockData) {
|
|
if (blockData instanceof Rotatable) {
|
|
return ((Rotatable) blockData).getRotation();
|
|
} else if (blockData instanceof Directional) {
|
|
return ((Directional) blockData).getFacing();
|
|
} else if (blockData instanceof Rail) {
|
|
Rail rail = (Rail) blockData;
|
|
switch (rail.getShape()) {
|
|
case NORTH_SOUTH:
|
|
return BlockFace.NORTH;
|
|
case EAST_WEST:
|
|
return BlockFace.EAST;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void setRotation(BlockData blockData, BlockFace rotation) {
|
|
if (blockData instanceof Rotatable) {
|
|
((Rotatable) blockData).setRotation(rotation);
|
|
} else if (blockData instanceof Directional) {
|
|
((Directional) blockData).setFacing(rotation);
|
|
} else if (blockData instanceof Rail) {
|
|
Rail rail = (Rail) blockData;
|
|
switch (rotation) {
|
|
case NORTH:
|
|
case SOUTH:
|
|
rail.setShape(Rail.Shape.NORTH_SOUTH);
|
|
break;
|
|
case EAST:
|
|
case WEST:
|
|
rail.setShape(Rail.Shape.EAST_WEST);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean isHitHalfTop(Player player) {
|
|
RayTraceResult rayTraceResult = player.rayTraceBlocks(6, FluidCollisionMode.NEVER);
|
|
if (rayTraceResult != null) {
|
|
Vector vector = rayTraceResult.getHitPosition();
|
|
return (vector.getY() - vector.getBlockY()) > 0.5;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private boolean hasSecondBlock(BlockData blockData) {
|
|
if (!(blockData instanceof Bisected)) {
|
|
return false;
|
|
}
|
|
if (blockData instanceof Door) {
|
|
return true;
|
|
}
|
|
return blockData.getClass().getName().contains("Tall");
|
|
}
|
|
|
|
private boolean canPlace(Block block, BlockData blockData, boolean force, AtomicBoolean usedForcePlace) {
|
|
if (!block.canPlace(blockData)) {
|
|
if (force) usedForcePlace.set(true);
|
|
return force;
|
|
}
|
|
return replaceables.contains(block.getType().name());
|
|
}
|
|
|
|
@AllArgsConstructor
|
|
@Getter
|
|
public enum PlaceItemResult {
|
|
NO_ITEM_HELD(false),
|
|
NO_BLOCK_ITEM_HELD(false),
|
|
NO_VALID_PLACEMENT(false),
|
|
NO_BUILD(false),
|
|
NO_PLACE(false),
|
|
NO_DOUBLE_HIGH_BLOCK_SPACE(false),
|
|
NO_PLACE_INSIDE_PLAYER(false),
|
|
SUCCESS(true),
|
|
SUCCESS_FORCE(true),
|
|
;
|
|
|
|
private final boolean success;
|
|
|
|
public boolean wasForced() {
|
|
return this == SUCCESS_FORCE;
|
|
}
|
|
}
|
|
}
|