Using BlockSets. Added filtering and several replace settings.

Overhauled build.gradle based on Create. Added flywheel as jarInJar, no external dependency anymore. New mappings channel from parchment.
Added replace modes: air, air and solid, solid, offhand filtered. Offhand filtering looks in randomizer bag as well.
Protecting tile entities by default.
Other mods can cancel placement through BlockEvent.OnBlockPlaced, such as FTBChunks.
BlockPreviews.drawOutlinesIfNoBlockInHand overhaul.
This commit is contained in:
Christian Knaapen
2023-01-22 20:36:48 +01:00
parent ae75e48c01
commit af4ef73375
36 changed files with 858 additions and 424 deletions

View File

@@ -2,7 +2,6 @@ package nl.requios.effortlessbuilding;
import com.mojang.blaze3d.platform.InputConstants;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import net.minecraft.ChatFormatting;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
@@ -13,7 +12,6 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.InputEvent;
@@ -27,12 +25,9 @@ import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import nl.requios.effortlessbuilding.buildmode.BuildModeEnum;
import nl.requios.effortlessbuilding.buildmode.ModeOptions;
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
import nl.requios.effortlessbuilding.gui.buildmode.PlayerSettingsGui;
import nl.requios.effortlessbuilding.gui.buildmode.RadialMenu;
import nl.requios.effortlessbuilding.gui.buildmodifier.ModifierSettingsGui;
import nl.requios.effortlessbuilding.render.BlockPreviews;
import nl.requios.effortlessbuilding.render.RenderHandler;
import nl.requios.effortlessbuilding.utilities.ReachHelper;
import nl.requios.effortlessbuilding.network.*;
import nl.requios.effortlessbuilding.render.BuildRenderTypes;
@@ -161,7 +156,7 @@ public class ClientEvents {
//QuickReplace toggle
if (keyBindings[1].consumeClick()) {
EffortlessBuildingClient.QUICK_REPLACE.toggleQuickReplacing();
EffortlessBuildingClient.BUILD_SETTINGS.toggleQuickReplace();
}
//Radial menu
@@ -241,7 +236,7 @@ public class ClientEvents {
keyBindings[keybindIndex].getKey().getValue());
}
public static BlockHitResult getLookingAt(Player player) {
public static BlockHitResult getLookingAtFar(Player player) {
Level world = player.level;
//base distance off of player ability (config)

View File

@@ -17,11 +17,11 @@ import net.minecraftforge.event.level.BlockEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.network.PacketDistributor;
import nl.requios.effortlessbuilding.buildmode.BuildModeEnum;
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo;
import nl.requios.effortlessbuilding.capability.ModifierCapabilityManager;
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
import nl.requios.effortlessbuilding.systems.ServerBuildState;
import nl.requios.effortlessbuilding.utilities.ReachHelper;
import nl.requios.effortlessbuilding.network.AddUndoMessage;
import nl.requios.effortlessbuilding.network.ClearUndoMessage;
@@ -55,7 +55,7 @@ public class CommonEvents {
EffortlessBuilding.DELAYED_BLOCK_PLACER.tick();
}
//Cancel event if necessary. Nothing more, rest is handled on mouse right click
//Cancel event if necessary. Nothing more, rest is handled on mouseclick
@SubscribeEvent
public static void onBlockPlaced(BlockEvent.EntityPlaceEvent event) {
if (event.getLevel().isClientSide()) return; //Never called clientside anyway, but just to be sure
@@ -65,9 +65,7 @@ public class CommonEvents {
//Don't cancel event if our custom logic is breaking blocks
if (EffortlessBuilding.SERVER_BLOCK_PLACER.isPlacingOrBreakingBlocks()) return;
BuildModeEnum buildMode = EffortlessBuildingClient.BUILD_MODES.getBuildMode();
if (buildMode != BuildModeEnum.DISABLED || EffortlessBuildingClient.QUICK_REPLACE.isQuickReplacing()) {
if (!ServerBuildState.isLikeVanilla(player)) {
//Only cancel if itemblock in hand
//Fixed issue with e.g. Create Wrench shift-rightclick disassembling being cancelled.
@@ -83,6 +81,7 @@ public class CommonEvents {
}
}
//Cancel event if necessary. Nothing more, rest is handled on mouseclick
@SubscribeEvent
public static void onBlockBroken(BlockEvent.BreakEvent event) {
if (event.getLevel().isClientSide()) return;
@@ -92,10 +91,7 @@ public class CommonEvents {
//Don't cancel event if our custom logic is breaking blocks
if (EffortlessBuilding.SERVER_BLOCK_PLACER.isPlacingOrBreakingBlocks()) return;
//Cancel event if necessary
//If cant break far then dont cancel event ever
BuildModeEnum buildMode = EffortlessBuildingClient.BUILD_MODES.getBuildMode();
if (buildMode != BuildModeEnum.DISABLED && ReachHelper.canBreakFar(player)) {
if (!ServerBuildState.isLikeVanilla(player) && ReachHelper.canBreakFar(player)) {
event.setCanceled(true);
} else {
//NORMAL mode, let vanilla handle block breaking

View File

@@ -10,14 +10,14 @@ import nl.requios.effortlessbuilding.gui.GoldenRandomizerBagScreen;
import nl.requios.effortlessbuilding.gui.RandomizerBagScreen;
import nl.requios.effortlessbuilding.render.BlockPreviews;
import nl.requios.effortlessbuilding.systems.BuilderChain;
import nl.requios.effortlessbuilding.systems.QuickReplace;
import nl.requios.effortlessbuilding.systems.BuildSettings;
public class EffortlessBuildingClient {
public static final BuilderChain BUILDER_CHAIN = new BuilderChain();
public static final BuildModes BUILD_MODES = new BuildModes();
public static final BuildModifiers BUILD_MODIFIERS = new BuildModifiers();
public static final QuickReplace QUICK_REPLACE = new QuickReplace();
public static final BuildSettings BUILD_SETTINGS = new BuildSettings();
public static final BlockPreviews BLOCK_PREVIEWS = new BlockPreviews();
public static void onConstructorClient(IEventBus modEventBus, IEventBus forgeEventBus) {

View File

@@ -1,12 +1,6 @@
package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.Direction;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import java.util.List;
import nl.requios.effortlessbuilding.utilities.BlockSet;
public abstract class BaseBuildMode implements IBuildMode {
@@ -18,7 +12,7 @@ public abstract class BaseBuildMode implements IBuildMode {
}
@Override
public boolean onClick(List<BlockEntry> blocks) {
public boolean onClick(BlockSet blocks) {
clicks++;
return false;
}

View File

@@ -9,6 +9,7 @@ import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.network.IsUsingBuildModePacket;
import nl.requios.effortlessbuilding.network.PacketHandler;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import nl.requios.effortlessbuilding.utilities.ReachHelper;
import java.util.*;
@@ -17,14 +18,8 @@ import java.util.*;
public class BuildModes {
private BuildModeEnum buildMode = BuildModeEnum.DISABLED;
public void findCoordinates(List<BlockEntry> blocks, Player player, BuildModeEnum buildMode) {
public void findCoordinates(BlockSet blocks, Player player, BuildModeEnum buildMode) {
buildMode.instance.findCoordinates(blocks);
//Limit number of blocks you can place
int limit = ReachHelper.getMaxBlocksPlacedAtOnce(player);
while (blocks.size() > limit) {
blocks.remove(blocks.size()-1);
}
}
public BuildModeEnum getBuildMode() {

View File

@@ -1,12 +1,6 @@
package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.Direction;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import java.util.List;
import nl.requios.effortlessbuilding.utilities.BlockSet;
public interface IBuildMode {
@@ -14,7 +8,7 @@ public interface IBuildMode {
void initialize();
//Returns if we should place blocks now
boolean onClick(List<BlockEntry> blocks);
boolean onClick(BlockSet blocks);
void findCoordinates(List<BlockEntry> blocks);
void findCoordinates(BlockSet blocks);
}

View File

@@ -1,11 +1,9 @@
package nl.requios.effortlessbuilding.buildmode;
import net.minecraft.world.entity.player.Player;
import net.minecraft.ChatFormatting;
import nl.requios.effortlessbuilding.ClientEvents;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo;
public class ModeOptions {
@@ -73,7 +71,7 @@ public class ModeOptions {
break;
case REPLACE:
if (player.level.isClientSide)
EffortlessBuildingClient.QUICK_REPLACE.toggleQuickReplacing();
EffortlessBuildingClient.BUILD_SETTINGS.toggleQuickReplace();
break;
case OPEN_MODIFIER_SETTINGS:
if (player.level.isClientSide)

View File

@@ -5,6 +5,7 @@ import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import nl.requios.effortlessbuilding.utilities.ReachHelper;
import java.util.ArrayList;
@@ -23,7 +24,7 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode {
}
@Override
public boolean onClick(List<BlockEntry> blocks) {
public boolean onClick(BlockSet blocks) {
super.onClick(blocks);
if (clicks == 1) {
@@ -35,7 +36,7 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode {
return false;
}
firstBlockEntry = blocks.get(0);
firstBlockEntry = blocks.getFirstBlockEntry();
} else if (clicks == 2) {
//Second click, find second position
@@ -57,7 +58,7 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode {
}
@Override
public void findCoordinates(List<BlockEntry> blocks) {
public void findCoordinates(BlockSet blocks) {
if (clicks == 0) return;
if (clicks == 1) {
@@ -85,6 +86,8 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode {
for (BlockPos pos : getIntermediateBlocks(player, x1, y1, z1, x2, y2, z2)) {
blocks.add(new BlockEntry(pos));
}
blocks.firstPos = firstPos;
blocks.lastPos = secondPos;
} else {
var player = Minecraft.getInstance().player;
BlockPos firstPos = firstBlockEntry.blockPos;
@@ -118,6 +121,8 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode {
for (BlockPos pos : getFinalBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3)) {
blocks.add(new BlockEntry(pos));
}
blocks.firstPos = firstPos;
blocks.lastPos = thirdPos;
}
}

View File

@@ -4,6 +4,7 @@ import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import nl.requios.effortlessbuilding.utilities.ReachHelper;
import java.util.List;
@@ -19,7 +20,7 @@ public abstract class TwoClicksBuildMode extends BaseBuildMode {
}
@Override
public boolean onClick(List<BlockEntry> blocks) {
public boolean onClick(BlockSet blocks) {
super.onClick(blocks);
if (clicks == 1) {
@@ -31,7 +32,7 @@ public abstract class TwoClicksBuildMode extends BaseBuildMode {
return false;
}
firstBlockEntry = blocks.get(0);
firstBlockEntry = blocks.getFirstBlockEntry();
} else {
//Second click, place blocks
clicks = 0;
@@ -41,7 +42,7 @@ public abstract class TwoClicksBuildMode extends BaseBuildMode {
}
@Override
public void findCoordinates(List<BlockEntry> blocks) {
public void findCoordinates(BlockSet blocks) {
if (clicks == 0) return;
var player = Minecraft.getInstance().player;
@@ -68,6 +69,8 @@ public abstract class TwoClicksBuildMode extends BaseBuildMode {
for (BlockPos pos : getAllBlocks(player, x1, y1, z1, x2, y2, z2)) {
blocks.add(new BlockEntry(pos));
}
blocks.firstPos = firstPos;
blocks.lastPos = secondPos;
}
//Finds the place of the second block pos based on criteria (floor must be on same height as first click, wall on same plane etc)

View File

@@ -1,9 +1,7 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import nl.requios.effortlessbuilding.buildmode.IBuildMode;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import java.util.List;
import nl.requios.effortlessbuilding.utilities.BlockSet;
public class Disabled implements IBuildMode {
@@ -13,12 +11,12 @@ public class Disabled implements IBuildMode {
}
@Override
public boolean onClick(List<BlockEntry> blocks) {
public boolean onClick(BlockSet blocks) {
return true;
}
@Override
public void findCoordinates(List<BlockEntry> blocks) {
public void findCoordinates(BlockSet blocks) {
//Do nothing
}
}

View File

@@ -1,9 +1,7 @@
package nl.requios.effortlessbuilding.buildmode.buildmodes;
import nl.requios.effortlessbuilding.buildmode.IBuildMode;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import java.util.List;
import nl.requios.effortlessbuilding.utilities.BlockSet;
public class Single implements IBuildMode {
@@ -13,12 +11,12 @@ public class Single implements IBuildMode {
}
@Override
public boolean onClick(List<BlockEntry> blocks) {
public boolean onClick(BlockSet blocks) {
return true;
}
@Override
public void findCoordinates(List<BlockEntry> blocks) {
public void findCoordinates(BlockSet blocks) {
//Do nothing
}
}

View File

@@ -20,9 +20,9 @@ import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
import nl.requios.effortlessbuilding.systems.DelayedBlockPlacer;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import nl.requios.effortlessbuilding.utilities.SurvivalHelper;
import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
import nl.requios.effortlessbuilding.render.BlockPreviews;
import java.util.ArrayList;
import java.util.Collections;
@@ -30,7 +30,7 @@ import java.util.List;
public class BuildModifiers {
public void findCoordinates(List<BlockEntry> blocks, LocalPlayer player, ModifierSettingsManager.ModifierSettings modifierSettings) {
public void findCoordinates(BlockSet blocks, LocalPlayer player, ModifierSettingsManager.ModifierSettings modifierSettings) {
}
@@ -115,7 +115,7 @@ public class BuildModifiers {
//add to undo stack
BlockPos firstPos = startCoordinates.get(0);
BlockPos secondPos = startCoordinates.get(startCoordinates.size() - 1);
UndoRedo.addUndo(player, new BlockSet(coordinates, previousBlockStates, newBlockStates, firstPos, secondPos));
UndoRedo.addUndo(player, new UndoRedoBlockSet(coordinates, previousBlockStates, newBlockStates, firstPos, secondPos));
}

View File

@@ -6,16 +6,13 @@ import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.core.Direction;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import net.minecraft.server.level.ServerLevel;
import nl.requios.effortlessbuilding.CommonConfig;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.utilities.FixedStack;
import nl.requios.effortlessbuilding.utilities.InventoryHelper;
import nl.requios.effortlessbuilding.utilities.SurvivalHelper;
import nl.requios.effortlessbuilding.render.BlockPreviews;
import java.util.*;
@@ -23,14 +20,14 @@ public class UndoRedo {
//Undo and redo stacks per player
//Gets added to twice in singleplayer (server and client) if not careful. So separate stacks.
private static final Map<UUID, FixedStack<BlockSet>> undoStacksClient = new HashMap<>();
private static final Map<UUID, FixedStack<BlockSet>> undoStacksServer = new HashMap<>();
private static final Map<UUID, FixedStack<BlockSet>> redoStacksClient = new HashMap<>();
private static final Map<UUID, FixedStack<BlockSet>> redoStacksServer = new HashMap<>();
private static final Map<UUID, FixedStack<UndoRedoBlockSet>> undoStacksClient = new HashMap<>();
private static final Map<UUID, FixedStack<UndoRedoBlockSet>> undoStacksServer = new HashMap<>();
private static final Map<UUID, FixedStack<UndoRedoBlockSet>> redoStacksClient = new HashMap<>();
private static final Map<UUID, FixedStack<UndoRedoBlockSet>> redoStacksServer = new HashMap<>();
//add to undo stack
public static void addUndo(Player player, BlockSet blockSet) {
Map<UUID, FixedStack<BlockSet>> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer;
public static void addUndo(Player player, UndoRedoBlockSet blockSet) {
Map<UUID, FixedStack<UndoRedoBlockSet>> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer;
//Assert coordinates is as long as previous and new blockstate lists
if (blockSet.getCoordinates().size() != blockSet.getPreviousBlockStates().size() ||
@@ -50,35 +47,35 @@ public class UndoRedo {
//If no stack exists, make one
if (!undoStacks.containsKey(player.getUUID())) {
undoStacks.put(player.getUUID(), new FixedStack<>(new BlockSet[CommonConfig.survivalBalancers.undoStackSize.get()]));
undoStacks.put(player.getUUID(), new FixedStack<>(new UndoRedoBlockSet[CommonConfig.survivalBalancers.undoStackSize.get()]));
}
undoStacks.get(player.getUUID()).push(blockSet);
}
private static void addRedo(Player player, BlockSet blockSet) {
Map<UUID, FixedStack<BlockSet>> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer;
private static void addRedo(Player player, UndoRedoBlockSet blockSet) {
Map<UUID, FixedStack<UndoRedoBlockSet>> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer;
//(No asserts necessary, it's private)
//If no stack exists, make one
if (!redoStacks.containsKey(player.getUUID())) {
redoStacks.put(player.getUUID(), new FixedStack<>(new BlockSet[CommonConfig.survivalBalancers.undoStackSize.get()]));
redoStacks.put(player.getUUID(), new FixedStack<>(new UndoRedoBlockSet[CommonConfig.survivalBalancers.undoStackSize.get()]));
}
redoStacks.get(player.getUUID()).push(blockSet);
}
public static boolean undo(Player player) {
Map<UUID, FixedStack<BlockSet>> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer;
Map<UUID, FixedStack<UndoRedoBlockSet>> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer;
if (!undoStacks.containsKey(player.getUUID())) return false;
FixedStack<BlockSet> undoStack = undoStacks.get(player.getUUID());
FixedStack<UndoRedoBlockSet> undoStack = undoStacks.get(player.getUUID());
if (undoStack.isEmpty()) return false;
BlockSet blockSet = undoStack.pop();
UndoRedoBlockSet blockSet = undoStack.pop();
List<BlockPos> coordinates = blockSet.getCoordinates();
List<BlockState> previousBlockStates = blockSet.getPreviousBlockStates();
List<BlockState> newBlockStates = blockSet.getNewBlockStates();
@@ -129,15 +126,15 @@ public class UndoRedo {
}
public static boolean redo(Player player) {
Map<UUID, FixedStack<BlockSet>> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer;
Map<UUID, FixedStack<UndoRedoBlockSet>> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer;
if (!redoStacks.containsKey(player.getUUID())) return false;
FixedStack<BlockSet> redoStack = redoStacks.get(player.getUUID());
FixedStack<UndoRedoBlockSet> redoStack = redoStacks.get(player.getUUID());
if (redoStack.isEmpty()) return false;
BlockSet blockSet = redoStack.pop();
UndoRedoBlockSet blockSet = redoStack.pop();
List<BlockPos> coordinates = blockSet.getCoordinates();
List<BlockState> previousBlockStates = blockSet.getPreviousBlockStates();
List<BlockState> newBlockStates = blockSet.getNewBlockStates();
@@ -187,8 +184,8 @@ public class UndoRedo {
}
public static void clear(Player player) {
Map<UUID, FixedStack<BlockSet>> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer;
Map<UUID, FixedStack<BlockSet>> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer;
Map<UUID, FixedStack<UndoRedoBlockSet>> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer;
Map<UUID, FixedStack<UndoRedoBlockSet>> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer;
if (undoStacks.containsKey(player.getUUID())) {
undoStacks.get(player.getUUID()).clear();
}

View File

@@ -2,20 +2,19 @@ package nl.requios.effortlessbuilding.buildmodifier;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import java.util.List;
//Used only for Undo
public class BlockSet {
public class UndoRedoBlockSet {
private final List<BlockPos> coordinates;
private final List<BlockState> previousBlockStates;
private final List<BlockState> newBlockStates;
private final BlockPos firstPos;
private final BlockPos secondPos;
public BlockSet(List<BlockPos> coordinates, List<BlockState> previousBlockStates, List<BlockState> newBlockStates,
BlockPos firstPos, BlockPos secondPos) {
public UndoRedoBlockSet(List<BlockPos> coordinates, List<BlockState> previousBlockStates, List<BlockState> newBlockStates,
BlockPos firstPos, BlockPos secondPos) {
this.coordinates = coordinates;
this.previousBlockStates = previousBlockStates;
this.newBlockStates = newBlockStates;

View File

@@ -1,10 +1,14 @@
package nl.requios.effortlessbuilding.compatibility;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import nl.requios.effortlessbuilding.create.foundation.item.ItemHelper;
import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
public class CompatHelper {
@@ -64,4 +68,21 @@ public class CompatHelper {
return ItemStack.EMPTY;
}
public static boolean containsBlock(ItemStack stack, Block block) {
if (stack == null || stack.isEmpty() || !isItemBlockProxy(stack)) {
return block == null || block == Blocks.AIR;
}
if (stack.getItem() instanceof BlockItem) {
return ((BlockItem) stack.getItem()).getBlock() == block;
}
if (stack.getItem() instanceof AbstractRandomizerBagItem randomizerBagItem) {
IItemHandler bagInventory = randomizerBagItem.getBagInventory(stack);
ItemStack firstMatch = ItemHelper.findFirstMatch(bagInventory, s -> s.getItem() instanceof BlockItem);
return firstMatch != null && !firstMatch.isEmpty();
}
return false;
}
}

View File

@@ -32,6 +32,7 @@ import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Material;
import net.minecraftforge.common.IPlantable;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.util.BlockSnapshot;
import net.minecraftforge.event.level.BlockEvent;
import javax.annotation.Nullable;
@@ -144,7 +145,7 @@ public class BlockHelper {
destroyBlockAs(world, pos, null, ItemStack.EMPTY, effectChance, droppedItemCallback);
}
public static void destroyBlockAs(Level world, BlockPos pos, @Nullable Player player, ItemStack usedTool,
public static boolean destroyBlockAs(Level world, BlockPos pos, @Nullable Player player, ItemStack usedTool,
float effectChance, Consumer<ItemStack> droppedItemCallback) {
FluidState fluidState = world.getFluidState(pos);
BlockState state = world.getBlockState(pos);
@@ -157,7 +158,7 @@ public class BlockHelper {
BlockEvent.BreakEvent event = new BlockEvent.BreakEvent(world, pos, state, player);
MinecraftForge.EVENT_BUS.post(event);
if (event.isCanceled())
return;
return false;
if (event.getExpToDrop() > 0 && world instanceof ServerLevel)
state.getBlock()
@@ -178,19 +179,20 @@ public class BlockHelper {
if (state.getBlock() instanceof IceBlock && usedTool.getEnchantmentLevel(Enchantments.SILK_TOUCH) == 0) {
if (world.dimensionType()
.ultraWarm())
return;
return false;
Material material = world.getBlockState(pos.below())
.getMaterial();
if (material.blocksMotion() || material.isLiquid())
world.setBlockAndUpdate(pos, Blocks.WATER.defaultBlockState());
return;
return true;
}
state.spawnAfterBreak((ServerLevel) world, pos, ItemStack.EMPTY, true);
}
world.setBlockAndUpdate(pos, fluidState.createLegacyBlock());
return true;
}
public static boolean isSolidWall(BlockGetter reader, BlockPos fromPos, Direction toDirection) {
@@ -223,7 +225,7 @@ public class BlockHelper {
.getBlock(), target.below());
}
public static void placeSchematicBlock(Level world, BlockState state, BlockPos target, ItemStack stack,
public static boolean placeSchematicBlock(Level world, Player player, BlockState state, BlockPos target, ItemStack stack,
@Nullable CompoundTag data) {
BlockEntity existingTile = world.getBlockEntity(target);
@@ -253,7 +255,7 @@ public class BlockHelper {
0.0D, 0.0D, 0.0D);
}
Block.dropResources(state, world, target);
return;
return true;
}
if (state.getBlock() instanceof BaseRailBlock) {
@@ -287,6 +289,7 @@ public class BlockHelper {
.setPlacedBy(world, target, state, null, stack);
} catch (Exception e) {
}
return true;
}
public static double getBounceMultiplier(Block block) {

View File

@@ -23,7 +23,6 @@ import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.network.NetworkHooks;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
import nl.requios.effortlessbuilding.systems.ServerBuildState;
import nl.requios.effortlessbuilding.capability.ItemHandlerCapabilityProvider;
import nl.requios.effortlessbuilding.utilities.SurvivalHelper;
@@ -133,7 +132,7 @@ public abstract class AbstractRandomizerBagItem extends Item {
if (world.isClientSide) return InteractionResult.SUCCESS;
//Only place manually if in normal vanilla mode
if (ServerBuildState.isUsingBuildMode(player) || ServerBuildState.isQuickReplacing(player)) {
if (!ServerBuildState.isLikeVanilla(player)) {
return InteractionResult.FAIL;
}

View File

@@ -5,14 +5,13 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.entity.player.Player;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.LogicalSide;
import net.minecraftforge.network.NetworkEvent;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.buildmodifier.BlockSet;
import nl.requios.effortlessbuilding.buildmodifier.UndoRedoBlockSet;
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo;
import java.util.ArrayList;
@@ -84,7 +83,7 @@ public class AddUndoMessage {
Player player = EffortlessBuilding.proxy.getPlayerEntityFromContext(ctx);
//Add to undo stack clientside
//Only the appropriate player that needs to add this to the undo stack gets this message
UndoRedo.addUndo(player, new BlockSet(
UndoRedo.addUndo(player, new UndoRedoBlockSet(
new ArrayList<BlockPos>() {{
add(message.getCoordinate());
}},

View File

@@ -5,6 +5,7 @@ import net.minecraft.world.entity.player.Player;
import net.minecraftforge.network.NetworkEvent;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import java.util.List;
import java.util.function.Supplier;
@@ -14,25 +15,24 @@ import java.util.function.Supplier;
*/
public class ServerBreakBlocksPacket {
private List<BlockEntry> blocks;
private BlockSet blocks;
public ServerBreakBlocksPacket() {}
public ServerBreakBlocksPacket(List<BlockEntry> blocks) {
public ServerBreakBlocksPacket(BlockSet blocks) {
this.blocks = blocks;
}
public static void encode(ServerBreakBlocksPacket message, FriendlyByteBuf buf) {
buf.writeCollection(message.blocks, BlockEntry::encode);
BlockSet.encode(buf, message.blocks);
}
public static ServerBreakBlocksPacket decode(FriendlyByteBuf buf) {
ServerBreakBlocksPacket message = new ServerBreakBlocksPacket();
message.blocks = buf.readList(BlockEntry::decode);
message.blocks = BlockSet.decode(buf);
return message;
}
public static class Handler {
public static void handle(ServerBreakBlocksPacket message, Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> {

View File

@@ -5,6 +5,7 @@ import net.minecraft.world.entity.player.Player;
import net.minecraftforge.network.NetworkEvent;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import java.util.List;
import java.util.function.Supplier;
@@ -14,25 +15,24 @@ import java.util.function.Supplier;
*/
public class ServerPlaceBlocksPacket {
private List<BlockEntry> blocks;
private BlockSet blocks;
public ServerPlaceBlocksPacket() {}
public ServerPlaceBlocksPacket(List<BlockEntry> blocks) {
public ServerPlaceBlocksPacket(BlockSet blocks) {
this.blocks = blocks;
}
public static void encode(ServerPlaceBlocksPacket message, FriendlyByteBuf buf) {
buf.writeCollection(message.blocks, BlockEntry::encode);
BlockSet.encode(buf, message.blocks);
}
public static ServerPlaceBlocksPacket decode(FriendlyByteBuf buf) {
ServerPlaceBlocksPacket message = new ServerPlaceBlocksPacket();
message.blocks = buf.readList(BlockEntry::decode);
message.blocks = BlockSet.decode(buf);
return message;
}
public static class Handler {
public static void handle(ServerPlaceBlocksPacket message, Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> {

View File

@@ -1,15 +1,8 @@
package nl.requios.effortlessbuilding.render;
import net.minecraft.network.chat.Component;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
@@ -17,20 +10,17 @@ import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.*;
import nl.requios.effortlessbuilding.buildmode.BuildModeEnum;
import nl.requios.effortlessbuilding.buildmodifier.BuildModifiers;
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
import nl.requios.effortlessbuilding.create.AllSpecialTextures;
import nl.requios.effortlessbuilding.create.CreateClient;
import nl.requios.effortlessbuilding.create.foundation.utility.Color;
import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
import nl.requios.effortlessbuilding.systems.BuilderChain;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.ReachHelper;
import nl.requios.effortlessbuilding.utilities.SurvivalHelper;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@OnlyIn(Dist.CLIENT)
public class BlockPreviews {
@@ -69,16 +59,15 @@ public class BlockPreviews {
!ClientConfig.visuals.alwaysShowBlockPreview.get()) return;
var blocks = EffortlessBuildingClient.BUILDER_CHAIN.getBlocks();
var state = EffortlessBuildingClient.BUILDER_CHAIN.getState();
if (blocks.size() == 0) return;
var coordinates = EffortlessBuildingClient.BUILDER_CHAIN.getCoordinates();
var coordinates = blocks.getCoordinates();
var state = EffortlessBuildingClient.BUILDER_CHAIN.getState();
//Dont fade out the outline if we are still determining where to place
//Every outline with same ID will not fade out (because it gets replaced)
Object outlineID = "single";
if (blocks.size() > 1) outlineID = blocks.get(0).blockPos;
if (blocks.size() > 1) outlineID = blocks.firstPos;
if (state != BuilderChain.State.BREAKING) {
//Use fancy shader if config allows, otherwise outlines
@@ -138,57 +127,44 @@ public class BlockPreviews {
}
public void drawOutlinesIfNoBlockInHand(Player player) {
ItemStack mainhand = player.getMainHandItem();
HitResult lookingAt = ClientEvents.getLookingAt(player);
if (EffortlessBuildingClient.BUILD_MODES.getBuildMode() == BuildModeEnum.DISABLED)
lookingAt = Minecraft.getInstance().hitResult;
var builderChain = EffortlessBuildingClient.BUILDER_CHAIN;
if (builderChain.isBlockInHand()) return;
if (builderChain.getState() != BuilderChain.State.IDLE) return;
boolean noBlockInHand = !(!mainhand.isEmpty() && CompatHelper.isItemBlockProxy(mainhand));
if (!noBlockInHand) return;
var blocks = new ArrayList<>(builderChain.getBlocks().values());
if (blocks.size() == 0) return;
//Draw outlines if no block in hand
//Find proper raytrace: either normal range or increased range depending on canBreakFar
HitResult objectMouseOver = Minecraft.getInstance().hitResult;
HitResult breakingRaytrace = ReachHelper.canBreakFar(player) ? lookingAt : objectMouseOver;
//Only render first outline if further than normal reach
var lookingAtNear = Minecraft.getInstance().hitResult;
if (lookingAtNear != null && lookingAtNear.getType() == HitResult.Type.BLOCK)
blocks.remove(0);
if (player.isCreative() && breakingRaytrace != null && breakingRaytrace.getType() == HitResult.Type.BLOCK) {
BlockHitResult blockBreakingRaytrace = (BlockHitResult) breakingRaytrace;
List<BlockPos> breakCoordinates = BuildModifiers.findCoordinates(player, blockBreakingRaytrace.getBlockPos());
//Only render outlines if there is a block, like vanilla
blocks.removeIf(blockEntry -> blockEntry.existingBlockState == null ||
blockEntry.existingBlockState.isAir() ||
blockEntry.existingBlockState.getMaterial().isLiquid());
//Only render first outline if further than normal reach
if (objectMouseOver != null && objectMouseOver.getType() == HitResult.Type.BLOCK)
breakCoordinates.remove(0);
breakCoordinates.removeIf(pos -> {
BlockState blockState = player.level.getBlockState(pos);
if (blockState.isAir() || blockState.getMaterial().isLiquid()) return true;
return !SurvivalHelper.canBreak(player.level, player, pos);
});
if (!breakCoordinates.isEmpty()) {
CreateClient.OUTLINER.showCluster("break", breakCoordinates)
.disableNormals()
.lineWidth(1 / 64f)
.colored(0x222222);
}
if (!blocks.isEmpty()) {
var coordinates = blocks.stream().map(blockEntry -> blockEntry.blockPos).collect(Collectors.toList());
CreateClient.OUTLINER.showCluster("break", coordinates)
.disableNormals()
.lineWidth(1 / 64f)
.colored(0x222222);
}
}
protected void renderBlockPreviews(List<BlockEntry> blocks, boolean breaking, float dissolve) {
var firstPos = blocks.get(0).blockPos;
var lastPos = blocks.get(blocks.size() - 1).blockPos;
protected void renderBlockPreviews(BlockSet blocks, boolean breaking, float dissolve) {
for (BlockEntry blockEntry : blocks) {
renderBlockPreview(blockEntry, breaking, dissolve, firstPos, lastPos);
renderBlockPreview(blockEntry, breaking, dissolve, blocks.firstPos, blocks.lastPos);
}
}
protected void renderBlockPreview(BlockEntry blockEntry, boolean breaking, float dissolve, BlockPos firstPos, BlockPos lastPos) {
if (blockEntry.blockState == null) return;
if (blockEntry.newBlockState == null) return;
var blockPos = blockEntry.blockPos;
var blockState = blockEntry.blockState;
var blockState = blockEntry.newBlockState;
float scale = 0.5f;
float alpha = 0.7f;
@@ -247,22 +223,22 @@ public class BlockPreviews {
return (ay * t3) + (by * t2) + (cy * t) + 0;
}
public void onBlocksPlaced(List<BlockEntry> blocks) {
public void onBlocksPlaced(BlockSet blocks) {
if (!ClientConfig.visuals.showBlockPreviews.get()) return;
if (blocks.size() <= 1 || blocks.size() > ClientConfig.visuals.maxBlockPreviews.get()) return;
placedBlocksList.add(new PlacedBlocksEntry(ClientEvents.ticksInGame, false, new ArrayList<>(blocks)));
placedBlocksList.add(new PlacedBlocksEntry(ClientEvents.ticksInGame, false, new BlockSet(blocks)));
CreateClient.OUTLINER.keep(blocks.get(0).blockPos, CommonConfig.visuals.appearAnimationLength.get());
CreateClient.OUTLINER.keep(blocks.firstPos, CommonConfig.visuals.appearAnimationLength.get());
}
public void onBlocksBroken(List<BlockEntry> blocks) {
public void onBlocksBroken(BlockSet blocks) {
if (!ClientConfig.visuals.showBlockPreviews.get()) return;
if (blocks.size() <= 1 || blocks.size() > ClientConfig.visuals.maxBlockPreviews.get()) return;
placedBlocksList.add(new PlacedBlocksEntry(ClientEvents.ticksInGame, true, new ArrayList<>(blocks)));
placedBlocksList.add(new PlacedBlocksEntry(ClientEvents.ticksInGame, true, new BlockSet(blocks)));
CreateClient.OUTLINER.keep(blocks.get(0).blockPos, CommonConfig.visuals.breakAnimationLength.get());
CreateClient.OUTLINER.keep(blocks.firstPos, CommonConfig.visuals.breakAnimationLength.get());
}
private void sortOnDistanceToPlayer(List<BlockPos> coordinates, Player player) {
@@ -279,9 +255,9 @@ public class BlockPreviews {
public static class PlacedBlocksEntry {
float time;
boolean breaking;
List<BlockEntry> blocks;
BlockSet blocks;
public PlacedBlocksEntry(float time, boolean breaking, List<BlockEntry> blocks) {
public PlacedBlocksEntry(float time, boolean breaking, BlockSet blocks) {
this.time = time;
this.breaking = breaking;
this.blocks = blocks;

View File

@@ -0,0 +1,67 @@
package nl.requios.effortlessbuilding.systems;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.network.IsQuickReplacingPacket;
import nl.requios.effortlessbuilding.network.PacketHandler;
@OnlyIn(Dist.CLIENT)
public class BuildSettings {
public enum ReplaceMode {
ONLY_AIR,
SOLID_AND_AIR,
SOLID_ONLY,
FILTERED_BY_OFFHAND
}
private boolean quickReplace = false;
public ReplaceMode replaceMode = ReplaceMode.ONLY_AIR;
private boolean replaceTileEntities;
public boolean isQuickReplacing() {
return quickReplace;
}
public void toggleQuickReplace() {
setQuickReplace(!quickReplace);
}
public void setQuickReplace(boolean quickReplace) {
this.quickReplace = quickReplace;
EffortlessBuilding.log(Minecraft.getInstance().player, "Set " + ChatFormatting.GOLD + "Quick Replace " +
ChatFormatting.RESET + (this.quickReplace ? "on" : "off"));
PacketHandler.INSTANCE.sendToServer(new IsQuickReplacingPacket(this.quickReplace));
}
public void setReplaceMode(ReplaceMode replaceMode) {
this.replaceMode = replaceMode;
}
public void setReplaceTileEntities(boolean replaceTileEntities) {
this.replaceTileEntities = replaceTileEntities;
}
public boolean shouldReplaceAir() {
return replaceMode == ReplaceMode.ONLY_AIR || replaceMode == ReplaceMode.SOLID_AND_AIR;
}
public boolean shouldReplaceSolid() {
return replaceMode == ReplaceMode.SOLID_ONLY || replaceMode == ReplaceMode.SOLID_AND_AIR;
}
public boolean shouldReplaceFiltered() {
return replaceMode == ReplaceMode.FILTERED_BY_OFFHAND;
}
public boolean shouldReplaceTileEntities() {
return replaceTileEntities;
}
public boolean shouldOffsetStartPosition() {
return shouldReplaceSolid() || shouldReplaceFiltered();
}
}

View File

@@ -1,9 +1,7 @@
package nl.requios.effortlessbuilding.systems;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
@@ -12,10 +10,8 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
@@ -29,11 +25,10 @@ import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem;
import nl.requios.effortlessbuilding.network.PacketHandler;
import nl.requios.effortlessbuilding.network.ServerBreakBlocksPacket;
import nl.requios.effortlessbuilding.network.ServerPlaceBlocksPacket;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.ReachHelper;
import nl.requios.effortlessbuilding.utilities.SurvivalHelper;
import nl.requios.effortlessbuilding.utilities.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
// Receives block placed events, then finds additional blocks we want to place through various systems,
@@ -42,10 +37,11 @@ import java.util.List;
@OnlyIn(Dist.CLIENT)
public class BuilderChain {
private final List<BlockEntry> blocks = new ArrayList<>();
private final List<BlockPos> coordinates = new ArrayList<>();
private int soundTime = 0;
private final BlockSet blocks = new BlockSet();
private boolean blockInHand;
private boolean lookingAtInteractiveObject;
private Item previousHeldItem;
private int soundTime = 0;
public enum State {
IDLE,
@@ -56,6 +52,7 @@ public class BuilderChain {
private State state = State.IDLE;
public void onRightClick() {
if (lookingAtInteractiveObject) return;
var player = Minecraft.getInstance().player;
if (state == State.BREAKING) {
@@ -63,9 +60,6 @@ public class BuilderChain {
return;
}
//Check if we have a BlockItem in hand
var itemStack = player.getItemInHand(InteractionHand.MAIN_HAND);
boolean blockInHand = CompatHelper.isItemBlockProxy(itemStack);
if (!blockInHand) {
if (state == State.PLACING) cancel();
return;
@@ -83,13 +77,15 @@ public class BuilderChain {
if (!blocks.isEmpty()) {
EffortlessBuildingClient.BLOCK_PREVIEWS.onBlocksPlaced(blocks);
playSoundIfFurtherThanNormal(player, blocks.get(0), false);
BlockUtilities.playSoundIfFurtherThanNormal(player, blocks.getLastBlockEntry(), false);
player.swing(InteractionHand.MAIN_HAND);
PacketHandler.INSTANCE.sendToServer(new ServerPlaceBlocksPacket(blocks));
}
}
}
public void onLeftClick() {
if (lookingAtInteractiveObject) return;
var player = Minecraft.getInstance().player;
if (state == State.PLACING) {
@@ -114,53 +110,55 @@ public class BuilderChain {
if (!blocks.isEmpty()) {
EffortlessBuildingClient.BLOCK_PREVIEWS.onBlocksBroken(blocks);
playSoundIfFurtherThanNormal(player, blocks.get(0), true);
BlockUtilities.playSoundIfFurtherThanNormal(player, blocks.getLastBlockEntry(), true);
player.swing(InteractionHand.MAIN_HAND);
PacketHandler.INSTANCE.sendToServer(new ServerBreakBlocksPacket(blocks));
}
}
}
public void onTick() {
var previousCoordinates = new ArrayList<>(coordinates);
var previousCoordinates = new HashSet<>(blocks.getCoordinates());
blocks.clear();
var mc = Minecraft.getInstance();
var player = mc.player;
var level = mc.level;
var world = mc.level;
//Check if we have a BlockItem in hand
var itemStack = player.getItemInHand(InteractionHand.MAIN_HAND);
boolean blockInHand = CompatHelper.isItemBlockProxy(itemStack);
blockInHand = CompatHelper.isItemBlockProxy(itemStack);
//Cancel placing as soon as we aren't holding a block anymore
// if (!blockInHand && state == State.PLACING) {
// state = State.IDLE;
// }
lookingAtInteractiveObject = BlockUtilities.determineIfLookingAtInteractiveObject(mc, world);
if (lookingAtInteractiveObject) return;
var modifierSettings = ModifierSettingsManager.getModifierSettings(player);
var buildMode = EffortlessBuildingClient.BUILD_MODES.getBuildMode();
var modifierSettings = ModifierSettingsManager.getModifierSettings(player);
BlockHitResult lookingAt = ClientEvents.getLookingAt(player);
BlockEntry startEntry = findStartPosition(player, lookingAt);
if (startEntry != null) {
blocks.add(startEntry);
if (state == State.IDLE) {
//Find start position
BlockHitResult lookingAt = ClientEvents.getLookingAtFar(player);
BlockEntry startEntry = findStartPosition(player, lookingAt);
if (startEntry != null) {
blocks.add(startEntry);
blocks.firstPos = startEntry.blockPos;
blocks.lastPos = startEntry.blockPos;
}
}
EffortlessBuildingClient.BUILD_MODES.findCoordinates(blocks, player, buildMode);
EffortlessBuildingClient.BUILD_MODIFIERS.findCoordinates(blocks, player, modifierSettings);
removeDuplicateCoordinates();
BuilderFilter.filterOnCoordinates(blocks, player);
coordinates.clear();
for (BlockEntry blockEntry : blocks) {
coordinates.add(blockEntry.blockPos);
}
findExistingBlockStates(world);
BuilderFilter.filterOnExistingBlockStates(blocks, player);
findBlockStates(player, itemStack);
findNewBlockStates(player, itemStack);
BuilderFilter.filterOnNewBlockStates(blocks, player);
//Check if any changes are made
if (previousHeldItem != itemStack.getItem() || !previousCoordinates.equals(coordinates)) {
if (previousHeldItem != itemStack.getItem() || !previousCoordinates.equals(blocks.getCoordinates())) {
onBlocksChanged(player);
}
@@ -176,9 +174,9 @@ public class BuilderChain {
if (blocks.size() > 1 && soundTime < ClientEvents.ticksInGame) {
soundTime = ClientEvents.ticksInGame;
var firstBlockState = blocks.get(0).blockState;
if (firstBlockState != null) {
SoundType soundType = firstBlockState.getBlock().getSoundType(firstBlockState, player.level, blocks.get(0).blockPos, player);
if (blocks.getLastBlockEntry() != null && blocks.getLastBlockEntry().newBlockState != null) {
var lastBlockState = blocks.getLastBlockEntry().newBlockState;
SoundType soundType = lastBlockState.getBlock().getSoundType(lastBlockState, player.level, blocks.lastPos, player);
SoundEvent soundEvent = state == BuilderChain.State.BREAKING ? soundType.getBreakSound() : soundType.getPlaceSound();
player.level.playSound(player, player.blockPosition(), soundEvent, SoundSource.BLOCKS, 0.3f, 0.8f);
}
@@ -192,34 +190,45 @@ public class BuilderChain {
Minecraft.getInstance().player.playSound(SoundEvents.UI_TOAST_OUT, 4, 1);
}
private BlockEntry findStartPosition(Player player, BlockHitResult lookingAt) {
if (lookingAt == null || lookingAt.getType() == HitResult.Type.MISS) return null;
private BlockEntry findStartPosition(Player player, BlockHitResult lookingAtFar) {
if (lookingAtFar == null || lookingAtFar.getType() == HitResult.Type.MISS) return null;
var startPos = lookingAt.getBlockPos();
var startPos = lookingAtFar.getBlockPos();
//Check if out of reach
int maxReach = ReachHelper.getMaxReach(player);
if (player.blockPosition().distSqr(startPos) > maxReach * maxReach) return null;
//TODO we are always at IDLE state here, find another way to check if we are breaking
if (state != State.BREAKING) {
//Offset in direction of sidehit if not quickreplace and not replaceable
boolean isQuickReplacing = EffortlessBuildingClient.QUICK_REPLACE.isQuickReplacing();
boolean shouldOffsetStartPosition = EffortlessBuildingClient.BUILD_SETTINGS.shouldOffsetStartPosition();
boolean replaceable = player.level.getBlockState(startPos).getMaterial().isReplaceable();
boolean becomesDoubleSlab = SurvivalHelper.doesBecomeDoubleSlab(player, startPos);
if (!isQuickReplacing && !replaceable && !becomesDoubleSlab) {
startPos = startPos.relative(lookingAt.getDirection());
if (!shouldOffsetStartPosition && !replaceable && !becomesDoubleSlab) {
startPos = startPos.relative(lookingAtFar.getDirection());
}
//Get under tall grass and other replaceable blocks
if (isQuickReplacing && replaceable) {
if (shouldOffsetStartPosition && replaceable) {
startPos = startPos.below();
}
} else {
//Do not break far if we are not allowed to
if (!ReachHelper.canBreakFar(player)) {
boolean startPosIsNear = false;
var lookingAtNear = Minecraft.getInstance().hitResult;
if (lookingAtNear != null && lookingAtNear.getType() == HitResult.Type.BLOCK) {
startPosIsNear = ((BlockHitResult) lookingAtNear).getBlockPos().equals(startPos);
}
if (!startPosIsNear) return null;
}
}
var blockEntry = new BlockEntry(startPos);
//Place upside-down stairs if we aim high at block
var hitVec = lookingAt.getLocation();
var hitVec = lookingAtFar.getLocation();
//Format hitvec to 0.x
hitVec = new Vec3(Math.abs(hitVec.x - ((int) hitVec.x)), Math.abs(hitVec.y - ((int) hitVec.y)), Math.abs(hitVec.z - ((int) hitVec.z)));
if (hitVec.y > 0.5) {
@@ -229,32 +238,19 @@ public class BuilderChain {
return blockEntry;
}
private void removeDuplicateCoordinates() {
for (int i = 0; i < blocks.size(); i++) {
BlockEntry blockEntry = blocks.get(i);
for (int j = i + 1; j < blocks.size(); j++) {
BlockEntry blockEntry2 = blocks.get(j);
if (blockEntry.blockPos.equals(blockEntry2.blockPos)) {
blocks.remove(j);
j--;
}
}
private void findExistingBlockStates(Level world) {
for (BlockEntry blockEntry : blocks) {
blockEntry.existingBlockState = world.getBlockState(blockEntry.blockPos);
}
}
private void findBlockStates(Player player, ItemStack itemStack) {
if (state == State.BREAKING) {
for (BlockEntry blockEntry : blocks) {
blockEntry.blockState = Minecraft.getInstance().level.getBlockState(blockEntry.blockPos);
}
return;
}
private void findNewBlockStates(Player player, ItemStack itemStack) {
if (state == State.BREAKING) return;
if (itemStack.getItem() instanceof BlockItem) {
for (BlockEntry blockEntry : blocks) {
blockEntry.blockState = getBlockState(player, InteractionHand.MAIN_HAND, itemStack, blockEntry);
blockEntry.newBlockState = BlockUtilities.getBlockState(player, InteractionHand.MAIN_HAND, itemStack, blockEntry);
}
} else if (CompatHelper.isItemBlockProxy(itemStack, false)) {
@@ -263,44 +259,25 @@ public class BuilderChain {
for (BlockEntry blockEntry : blocks) {
ItemStack itemBlockStack = CompatHelper.getItemBlockFromStack(itemStack);
if (itemBlockStack == null || itemBlockStack.isEmpty()) continue;
blockEntry.blockState = getBlockState(player, InteractionHand.MAIN_HAND, itemBlockStack, blockEntry);
blockEntry.newBlockState = BlockUtilities.getBlockState(player, InteractionHand.MAIN_HAND, itemBlockStack, blockEntry);
}
}
}
public BlockState getBlockState(Player player, InteractionHand hand, ItemStack blockItemStack, BlockEntry blockEntry) {
Block block = Block.byItem(blockItemStack.getItem());
//TODO convert lookingAt hitvec to relative hitvec
var blockHitResult = new BlockHitResult(Vec3.ZERO, Direction.UP, blockEntry.blockPos, false);
return block.getStateForPlacement(new BlockPlaceContext(player, hand, blockItemStack, blockHitResult));
}
private void playSoundIfFurtherThanNormal(Player player, BlockEntry blockEntry, boolean breaking) {
if (Minecraft.getInstance().hitResult != null && Minecraft.getInstance().hitResult.getType() == HitResult.Type.BLOCK)
return;
if (blockEntry == null || blockEntry.blockState == null)
return;
SoundType soundType = blockEntry.blockState.getBlock().getSoundType(blockEntry.blockState, player.level, blockEntry.blockPos, player);
SoundEvent soundEvent = breaking ? soundType.getBreakSound() : soundType.getPlaceSound();
player.level.playSound(player, player.blockPosition(), soundEvent, SoundSource.BLOCKS, 0.6f, soundType.getPitch());
}
private void swingHand(Player player, InteractionHand hand) {
player.swing(hand);
}
public State getState() {
return state;
}
public List<BlockEntry> getBlocks() {
public BlockSet getBlocks() {
return blocks;
}
public List<BlockPos> getCoordinates() {
return coordinates;
public boolean isBlockInHand() {
return blockInHand;
}
public boolean isLookingAtInteractiveObject() {
return lookingAtInteractiveObject;
}
}

View File

@@ -0,0 +1,57 @@
package nl.requios.effortlessbuilding.systems;
import net.minecraft.world.entity.player.Player;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import nl.requios.effortlessbuilding.utilities.PlaceChecker;
@OnlyIn(Dist.CLIENT)
public class BuilderFilter {
public static void filterOnCoordinates(BlockSet blocks, Player player) {
}
public static void filterOnExistingBlockStates(BlockSet blocks, Player player) {
var buildSettings = EffortlessBuildingClient.BUILD_SETTINGS;
boolean placing = EffortlessBuildingClient.BUILDER_CHAIN.getState() == BuilderChain.State.PLACING;
var iter = blocks.entrySet().iterator();
while (iter.hasNext()) {
var blockEntry = iter.next().getValue();
var blockState = blockEntry.existingBlockState;
boolean remove = false;
if (!buildSettings.shouldReplaceTileEntities() && blockState.hasBlockEntity()) remove = true;
if (placing) {
if (!buildSettings.shouldReplaceAir() && blockState.isAir()) remove = true;
boolean isSolid = blockState.isRedstoneConductor(player.level, blockEntry.blockPos);
if (!buildSettings.shouldReplaceSolid() && isSolid) remove = true;
}
if (buildSettings.shouldReplaceFiltered()) {
var offhandItem = player.getOffhandItem();
if (!CompatHelper.containsBlock(offhandItem, blockState.getBlock())) remove = true;
}
if (remove) iter.remove();
}
}
public static void filterOnNewBlockStates(BlockSet blocks, Player player) {
var iter = blocks.entrySet().iterator();
while (iter.hasNext()) {
var blockEntry = iter.next().getValue();
boolean remove = false;
if (!PlaceChecker.shouldPlaceBlock(player.level, blockEntry)) remove = true;
if (remove) iter.remove();
}
}
}

View File

@@ -6,7 +6,7 @@ import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import nl.requios.effortlessbuilding.buildmodifier.BlockSet;
import nl.requios.effortlessbuilding.buildmodifier.UndoRedoBlockSet;
import nl.requios.effortlessbuilding.buildmodifier.UndoRedo;
import nl.requios.effortlessbuilding.utilities.InventoryHelper;
import nl.requios.effortlessbuilding.utilities.SurvivalHelper;
@@ -97,7 +97,7 @@ public class DelayedBlockPlacer {
//add to undo stack
BlockPos firstPos = coordinates.get(0);
BlockPos secondPos = coordinates.get(coordinates.size() - 1);
UndoRedo.addUndo(player, new BlockSet(coordinates, previousBlockStates, newBlockStates, firstPos, secondPos));
UndoRedo.addUndo(player, new UndoRedoBlockSet(coordinates, previousBlockStates, newBlockStates, firstPos, secondPos));
}
}

View File

@@ -1,30 +0,0 @@
package nl.requios.effortlessbuilding.systems;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import nl.requios.effortlessbuilding.network.IsQuickReplacingPacket;
import nl.requios.effortlessbuilding.network.PacketHandler;
@OnlyIn(Dist.CLIENT)
public class QuickReplace {
private boolean isQuickReplacing = false;
public boolean isQuickReplacing() {
return isQuickReplacing;
}
public void toggleQuickReplacing() {
setIsQuickReplacing(!isQuickReplacing);
}
public void setIsQuickReplacing(boolean isQuickReplacing) {
this.isQuickReplacing = isQuickReplacing;
EffortlessBuilding.log(Minecraft.getInstance().player, "Set " + ChatFormatting.GOLD + "Quick Replace " +
ChatFormatting.RESET + (this.isQuickReplacing ? "on" : "off"));
PacketHandler.INSTANCE.sendToServer(new IsQuickReplacingPacket(this.isQuickReplacing));
}
}

View File

@@ -1,13 +1,27 @@
package nl.requios.effortlessbuilding.systems;
import net.minecraft.core.BlockPos;
import com.google.common.collect.Lists;
import net.minecraft.core.Direction;
import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag;
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.create.foundation.utility.BlockHelper;
import nl.requios.effortlessbuilding.utilities.BlockEntry;
import nl.requios.effortlessbuilding.utilities.BlockSet;
import java.util.List;
@@ -15,7 +29,7 @@ import java.util.List;
public class ServerBlockPlacer {
private boolean isPlacingOrBreakingBlocks = false;
public void placeBlocks(Player player, List<BlockEntry> blocks) {
public void placeBlocks(Player player, BlockSet blocks) {
EffortlessBuilding.log(player, "Placing " + blocks.size() + " blocks");
for (BlockEntry block : blocks) {
@@ -28,11 +42,11 @@ public class ServerBlockPlacer {
if (!world.isLoaded(block.blockPos)) return;
isPlacingOrBreakingBlocks = true;
BlockHelper.placeSchematicBlock(world, block.blockState, block.blockPos, block.itemStack, null);
boolean placedBlock = onPlaceItemIntoWorld(player, block) == InteractionResult.SUCCESS;
isPlacingOrBreakingBlocks = false;
}
public void breakBlocks(Player player, List<BlockEntry> blocks) {
public void breakBlocks(Player player, BlockSet blocks) {
EffortlessBuilding.log(player, "Breaking " + blocks.size() + " blocks");
for (BlockEntry block : blocks) {
@@ -45,7 +59,7 @@ public class ServerBlockPlacer {
if (!world.isLoaded(block.blockPos) || world.isEmptyBlock(block.blockPos)) return;
isPlacingOrBreakingBlocks = true;
BlockHelper.destroyBlockAs(world, block.blockPos, player, player.getMainHandItem(), 0f, stack -> {
boolean brokeBlock = BlockHelper.destroyBlockAs(world, block.blockPos, player, player.getMainHandItem(), 0f, stack -> {
if (!player.isCreative()) {
ItemHandlerHelper.giveItemToPlayer(player, stack);
}
@@ -56,4 +70,95 @@ public class ServerBlockPlacer {
public boolean isPlacingOrBreakingBlocks() {
return isPlacingOrBreakingBlocks;
}
//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

@@ -30,4 +30,8 @@ public class ServerBuildState {
player.getPersistentData().remove(IS_QUICK_REPLACING_KEY);
}
}
public static boolean isLikeVanilla(Player player) {
return !isUsingBuildMode(player) && !isQuickReplacing(player);
}
}

View File

@@ -8,7 +8,6 @@ import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.state.BlockState;
import java.util.BitSet;
import java.util.Objects;
public class BlockEntry {
public final BlockPos blockPos;
@@ -16,7 +15,9 @@ public class BlockEntry {
public boolean mirrorY;
public boolean mirrorZ;
public Rotation rotation;
public BlockState blockState;
//BlockState that is currently in the world
public BlockState existingBlockState;
public BlockState newBlockState;
public ItemStack itemStack = ItemStack.EMPTY;
public BlockEntry(BlockPos blockPos) {
@@ -24,7 +25,7 @@ public class BlockEntry {
}
public boolean meansBreakBlock() {
return blockState == null || blockState.isAir();
return newBlockState == null || newBlockState.isAir();
}
public BitSet getMirrorBitSet() {
@@ -43,13 +44,13 @@ public class BlockEntry {
public static void encode(FriendlyByteBuf buf, BlockEntry block) {
buf.writeBlockPos(block.blockPos);
buf.writeNullable(block.blockState, (buffer, blockState) -> buffer.writeNbt(NbtUtils.writeBlockState(blockState)));
buf.writeNullable(block.newBlockState, (buffer, blockState) -> buffer.writeNbt(NbtUtils.writeBlockState(blockState)));
buf.writeItem(block.itemStack);
}
public static BlockEntry decode(FriendlyByteBuf buf) {
BlockEntry block = new BlockEntry(buf.readBlockPos());
block.blockState = buf.readNullable(buffer -> {
block.newBlockState = buf.readNullable(buffer -> {
var nbt = buf.readNbt();
if (nbt == null) return null;
return NbtUtils.readBlockState(nbt);

View File

@@ -0,0 +1,81 @@
package nl.requios.effortlessbuilding.utilities;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.network.FriendlyByteBuf;
import nl.requios.effortlessbuilding.EffortlessBuilding;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
public class BlockSet extends HashMap<BlockPos, BlockEntry> implements Iterable<BlockEntry> {
public static boolean logging = true;
public BlockPos firstPos;
public BlockPos lastPos;
public BlockSet() {
super();
}
public BlockSet(BlockSet blockSet) {
super(blockSet);
this.firstPos = blockSet.firstPos;
this.lastPos = blockSet.lastPos;
}
public BlockSet(List<BlockEntry> blockEntries) {
super();
for (BlockEntry blockEntry : blockEntries) {
add(blockEntry);
}
}
public void add(BlockEntry blockEntry) {
if (!containsKey(blockEntry.blockPos)) {
//Limit number of blocks you can place
int limit = ReachHelper.getMaxBlocksPlacedAtOnce(Minecraft.getInstance().player);
if (size() >= limit) {
if (logging) EffortlessBuilding.log("BlockSet limit reached, not adding block at " + blockEntry.blockPos);
return;
}
put(blockEntry.blockPos, blockEntry);
} else {
if (logging) EffortlessBuilding.log("BlockSet already contains block at " + blockEntry.blockPos);
}
}
public HashSet<BlockPos> getCoordinates() {
return new HashSet<>(keySet());
}
public BlockEntry getFirstBlockEntry() {
return get(firstPos);
}
public BlockEntry getLastBlockEntry() {
return get(lastPos);
}
@NotNull
@Override
public Iterator<BlockEntry> iterator() {
return this.values().iterator();
}
public static void encode(FriendlyByteBuf buf, BlockSet block) {
buf.writeCollection(block.values(), BlockEntry::encode);
}
public static BlockSet decode(FriendlyByteBuf buf) {
return new BlockSet(buf.readList(BlockEntry::decode));
}
}

View File

@@ -0,0 +1,58 @@
package nl.requios.effortlessbuilding.utilities;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.Direction;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
public class BlockUtilities {
public static BlockState getBlockState(Player player, InteractionHand hand, ItemStack blockItemStack, BlockEntry blockEntry) {
Block block = Block.byItem(blockItemStack.getItem());
//TODO convert lookingAt hitvec to relative hitvec
var blockHitResult = new BlockHitResult(Vec3.ZERO, Direction.UP, blockEntry.blockPos, false);
return block.getStateForPlacement(new BlockPlaceContext(player, hand, blockItemStack, blockHitResult));
}
public static boolean determineIfLookingAtInteractiveObject(Minecraft mc, ClientLevel level) {
//Check if we are looking at an interactive object
var result = false;
if (mc.hitResult != null) {
if (mc.hitResult.getType() == HitResult.Type.BLOCK) {
var blockHitResult = (BlockHitResult) mc.hitResult;
var blockState = level.getBlockState(blockHitResult.getBlockPos());
if (blockState.hasBlockEntity()) {
result = true;
}
}
if (mc.hitResult.getType() == HitResult.Type.ENTITY) {
result = true;
}
}
return result;
}
public static void playSoundIfFurtherThanNormal(Player player, BlockEntry blockEntry, boolean breaking) {
if (Minecraft.getInstance().hitResult != null && Minecraft.getInstance().hitResult.getType() == HitResult.Type.BLOCK)
return;
if (blockEntry == null || blockEntry.newBlockState == null)
return;
SoundType soundType = blockEntry.newBlockState.getBlock().getSoundType(blockEntry.newBlockState, player.level, blockEntry.blockPos, player);
SoundEvent soundEvent = breaking ? soundType.getBreakSound() : soundType.getPlaceSound();
player.level.playSound(player, player.blockPosition(), soundEvent, SoundSource.BLOCKS, 0.6f, soundType.getPitch());
}
}

View File

@@ -7,6 +7,7 @@ import net.minecraft.world.item.ItemStack;
public class InventoryHelper {
@Deprecated //Use BlockHelper.findAndRemoveInInventory instead
public static ItemStack findItemStackInInventory(Player player, Block block) {
for (ItemStack invStack : player.getInventory().items) {
if (!invStack.isEmpty() && invStack.getItem() instanceof BlockItem &&

View File

@@ -0,0 +1,105 @@
package nl.requios.effortlessbuilding.utilities;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.piston.PistonHeadBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BedPart;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class PlaceChecker {
//SchematicPrinter::shouldPlaceBlock
public static boolean shouldPlaceBlock(Level world, BlockEntry blockEntry) {
if (world == null)
return false;
var pos = blockEntry.blockPos;
var state = blockEntry.newBlockState;
BlockEntity tileEntity = null;
BlockState toReplace = world.getBlockState(pos);
BlockEntity toReplaceTE = world.getBlockEntity(pos);
BlockState toReplaceOther = null;
if (state.hasProperty(BlockStateProperties.BED_PART) && state.hasProperty(BlockStateProperties.HORIZONTAL_FACING)
&& state.getValue(BlockStateProperties.BED_PART) == BedPart.FOOT)
toReplaceOther = world.getBlockState(pos.relative(state.getValue(BlockStateProperties.HORIZONTAL_FACING)));
if (state.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF)
&& state.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER)
toReplaceOther = world.getBlockState(pos.above());
if (!world.isLoaded(pos))
return false;
if (!world.getWorldBorder().isWithinBounds(pos))
return false;
if (toReplace == state)
return false;
if (toReplace.getDestroySpeed(world, pos) == -1
|| (toReplaceOther != null && toReplaceOther.getDestroySpeed(world, pos) == -1))
return false;
boolean isNormalCube = state.isRedstoneConductor(world, pos);
return shouldPlace(world, pos, state, tileEntity, toReplace, toReplaceOther, isNormalCube);
}
//SchematicannonTileEntity::shouldPlace
private static boolean shouldPlace(Level level, BlockPos pos, BlockState state, BlockEntity tileEntity, BlockState toReplace,
BlockState toReplaceOther, boolean isNormalCube) {
return true;
// if (!replaceTileEntities
// && (toReplace.hasBlockEntity() || (toReplaceOther != null && toReplaceOther.hasBlockEntity())))
// return false;
//
// if (shouldIgnoreBlockState(state))
// return false;
//
// boolean placingAir = state.isAir();
//
// if (replaceMode == 3)
// return true;
// if (replaceMode == 2 && !placingAir)
// return true;
// if (replaceMode == 1 && (isNormalCube || (!toReplace.isRedstoneConductor(level, pos)
// && (toReplaceOther == null || !toReplaceOther.isRedstoneConductor(level, pos)))) && !placingAir)
// return true;
// if (replaceMode == 0 && !toReplace.isRedstoneConductor(level, pos)
// && (toReplaceOther == null || !toReplaceOther.isRedstoneConductor(level, pos)) && !placingAir)
// return true;
//
// return false;
}
//SchematicannonTileEntity::shouldIgnoreBlockState
private static boolean shouldIgnoreBlockState(BlockState state) {
// Block doesn't have a mapping (Water, lava, etc)
if (state.getBlock() == Blocks.STRUCTURE_VOID)
return true;
// ItemRequirement requirement = ItemRequirement.of(state, te);
// if (requirement.isEmpty())
// return false;
// if (requirement.isInvalid())
// return false;
// Block doesn't need to be placed twice (Doors, beds, double plants)
if (state.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF)
&& state.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.UPPER)
return true;
if (state.hasProperty(BlockStateProperties.BED_PART)
&& state.getValue(BlockStateProperties.BED_PART) == BedPart.HEAD)
return true;
if (state.getBlock() instanceof PistonHeadBlock)
return true;
// if (AllBlocks.BELT.has(state))
// return state.getValue(BeltBlock.PART) == BeltPart.MIDDLE;
return false;
}
}

View File

@@ -2,7 +2,6 @@ package nl.requios.effortlessbuilding.utilities;
import net.minecraft.advancements.CriteriaTriggers;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Registry;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundSource;
@@ -20,12 +19,10 @@ import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.pattern.BlockInWorld;
import net.minecraft.world.level.material.Material;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraftforge.event.ForgeEventFactory;
import nl.requios.effortlessbuilding.CommonConfig;
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager;
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
import nl.requios.effortlessbuilding.systems.ServerBuildState;
@@ -273,11 +270,9 @@ public class SurvivalHelper {
}
//Check quickreplace
boolean isQuickReplacing = false;
if (placer instanceof Player player) {
if (world.isClientSide) EffortlessBuildingClient.QUICK_REPLACE.isQuickReplacing();
else isQuickReplacing = ServerBuildState.isQuickReplacing(player);
boolean isQuickReplacing = world.isClientSide ? EffortlessBuildingClient.BUILD_SETTINGS.isQuickReplacing()
: ServerBuildState.isQuickReplacing(player);
if (isQuickReplacing) return true;
}