From 349c6735539b5e0efdba47c68250283cbd51f631 Mon Sep 17 00:00:00 2001 From: Christian Knaapen Date: Fri, 20 Jan 2023 22:26:18 +0100 Subject: [PATCH] WIP Overhaul Block calculation only on client, then send to server to place. BuilderChain system to decouple buildmodes, buildmodifiers and randomizer bag. --- .../effortlessbuilding/ClientEvents.java | 79 ++----- .../effortlessbuilding/CommonEvents.java | 24 +-- .../EffortlessBuildingClient.java | 8 +- .../buildmode/BaseBuildMode.java | 34 ++- .../buildmode/BuildModes.java | 166 ++------------- .../buildmode/IBuildMode.java | 14 +- .../buildmode/ModeSettingsManager.java | 9 +- .../buildmode/ThreeClicksBuildMode.java | 86 ++++---- .../buildmode/TwoClicksBuildMode.java | 33 ++- .../buildmodifier/BlockSet.java | 1 + .../buildmodifier/BuildModifiers.java | 6 + .../capability/ModeCapabilityManager.java | 5 +- .../gui/buildmode/RadialMenu.java | 1 - .../network/BlockBrokenMessage.java | 92 --------- .../network/BlockPlacedMessage.java | 118 ----------- .../network/CancelModeMessage.java | 33 --- .../network/ModeSettingsMessage.java | 3 +- .../network/OnBlockPlacedMessage.java | 49 +++++ .../network/PacketHandler.java | 6 +- .../network/RequestLookAtMessage.java | 72 ------- .../network/ServerPlaceBlocksMessage.java | 47 +++++ .../render/BlockPreviews.java | 4 +- .../systems/BlockChain.java | 11 - .../systems/BuilderChain.java | 193 ++++++++++++++++++ .../systems/ServerBlockPlacer.java | 47 +++++ .../utilities/BlockEntry.java | 53 +++++ 26 files changed, 530 insertions(+), 664 deletions(-) delete mode 100644 src/main/java/nl/requios/effortlessbuilding/network/BlockBrokenMessage.java delete mode 100644 src/main/java/nl/requios/effortlessbuilding/network/BlockPlacedMessage.java delete mode 100644 src/main/java/nl/requios/effortlessbuilding/network/CancelModeMessage.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/network/OnBlockPlacedMessage.java delete mode 100644 src/main/java/nl/requios/effortlessbuilding/network/RequestLookAtMessage.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/network/ServerPlaceBlocksMessage.java delete mode 100644 src/main/java/nl/requios/effortlessbuilding/systems/BlockChain.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/systems/BuilderChain.java create mode 100644 src/main/java/nl/requios/effortlessbuilding/utilities/BlockEntry.java diff --git a/src/main/java/nl/requios/effortlessbuilding/ClientEvents.java b/src/main/java/nl/requios/effortlessbuilding/ClientEvents.java index 54110ce..804c4da 100644 --- a/src/main/java/nl/requios/effortlessbuilding/ClientEvents.java +++ b/src/main/java/nl/requios/effortlessbuilding/ClientEvents.java @@ -8,17 +8,10 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.renderer.ShaderInstance; -import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceLocation; -import net.minecraft.sounds.SoundSource; -import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.BlockItem; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.ClipContext; 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; @@ -36,7 +29,6 @@ import nl.requios.effortlessbuilding.buildmode.BuildModes; import nl.requios.effortlessbuilding.buildmode.ModeOptions; import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; -import nl.requios.effortlessbuilding.compatibility.CompatHelper; import nl.requios.effortlessbuilding.gui.buildmode.PlayerSettingsGui; import nl.requios.effortlessbuilding.gui.buildmode.RadialMenu; import nl.requios.effortlessbuilding.gui.buildmodifier.ModifierSettingsGui; @@ -92,7 +84,12 @@ public class ClientEvents { @SubscribeEvent public static void onClientTick(TickEvent.ClientTickEvent event) { + if (!isGameActive()) return; + if (event.phase == TickEvent.Phase.START) { + + EffortlessBuildingClient.BUILDER_CHAIN.onTick(); + onMouseInput(); //Update previousLookAt @@ -140,41 +137,11 @@ public class ClientEvents { if (mc.options.keyUse.isDown()) { - //KeyBinding.setKeyBindState(mc.gameSettings.keyBindUseItem.getKeyCode(), false); - if (placeCooldown <= 0) { placeCooldown = 4; - ItemStack currentItemStack = player.getItemInHand(InteractionHand.MAIN_HAND); - if (currentItemStack.getItem() instanceof BlockItem || - (CompatHelper.isItemBlockProxy(currentItemStack) && !player.isShiftKeyDown())) { + EffortlessBuildingClient.BUILDER_CHAIN.onRightClick(); - ItemStack itemStack = CompatHelper.getItemBlockFromStack(currentItemStack); - - //find position in distance - HitResult lookingAt = getLookingAt(player); - if (lookingAt != null && lookingAt.getType() == HitResult.Type.BLOCK) { - BlockHitResult blockLookingAt = (BlockHitResult) lookingAt; - - BuildModes.onBlockPlacedMessage(player, new BlockPlacedMessage(blockLookingAt, true)); - PacketHandler.INSTANCE.sendToServer(new BlockPlacedMessage(blockLookingAt, true)); - - //play sound if further than normal - if ((blockLookingAt.getLocation().subtract(player.getEyePosition(1f))).lengthSqr() > 25f && - itemStack.getItem() instanceof BlockItem) { - - BlockState state = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState(); - BlockPos blockPos = blockLookingAt.getBlockPos(); - SoundType soundType = state.getBlock().getSoundType(state, player.level, blockPos, player); - player.level.playSound(player, player.blockPosition(), soundType.getPlaceSound(), SoundSource.BLOCKS, - 0.4f, soundType.getPitch()); - player.swing(InteractionHand.MAIN_HAND); - } - } else { - BuildModes.onBlockPlacedMessage(player, new BlockPlacedMessage()); - PacketHandler.INSTANCE.sendToServer(new BlockPlacedMessage()); - } - } } else if (buildMode == BuildModes.BuildModeEnum.SINGLE) { placeCooldown--; if (ModeOptions.getBuildSpeed() == ModeOptions.ActionEnum.FAST_SPEED) placeCooldown = 0; @@ -189,34 +156,13 @@ public class ClientEvents { if (breakCooldown <= 0) { breakCooldown = 4; - HitResult lookingAt = getLookingAt(player); - if (lookingAt != null && lookingAt.getType() == HitResult.Type.BLOCK) { - BlockHitResult blockLookingAt = (BlockHitResult) lookingAt; + EffortlessBuildingClient.BUILDER_CHAIN.onLeftClick(); - BuildModes.onBlockBrokenMessage(player, new BlockBrokenMessage(blockLookingAt)); - PacketHandler.INSTANCE.sendToServer(new BlockBrokenMessage(blockLookingAt)); - - //play sound if further than normal - if ((blockLookingAt.getLocation().subtract(player.getEyePosition(1f))).lengthSqr() > 25f) { - - BlockPos blockPos = blockLookingAt.getBlockPos(); - BlockState state = player.level.getBlockState(blockPos); - SoundType soundtype = state.getBlock().getSoundType(state, player.level, blockPos, player); - player.level.playSound(player, player.blockPosition(), soundtype.getBreakSound(), SoundSource.BLOCKS, - 0.4f, soundtype.getPitch()); - player.swing(InteractionHand.MAIN_HAND); - } - } else { - BuildModes.onBlockBrokenMessage(player, new BlockBrokenMessage()); - PacketHandler.INSTANCE.sendToServer(new BlockBrokenMessage()); - } } else if (buildMode == BuildModes.BuildModeEnum.SINGLE) { breakCooldown--; if (ModeOptions.getBuildSpeed() == ModeOptions.ActionEnum.FAST_SPEED) breakCooldown = 0; } - //EffortlessBuilding.packetHandler.sendToServer(new CancelModeMessage()); - } else { breakCooldown = 0; } @@ -310,7 +256,7 @@ public class ClientEvents { public static void onGuiOpen(ScreenEvent event) { Player player = Minecraft.getInstance().player; if (player != null) { - BuildModes.initializeMode(player); + EffortlessBuildingClient.BUILDER_CHAIN.cancel(); } } @@ -320,7 +266,7 @@ public class ClientEvents { keyBindings[2].getKey().getValue()); } - public static HitResult getLookingAt(Player player) { + public static BlockHitResult getLookingAt(Player player) { Level world = player.level; //base distance off of player ability (config) @@ -329,9 +275,12 @@ public class ClientEvents { Vec3 look = player.getLookAngle(); Vec3 start = new Vec3(player.getX(), player.getY() + player.getEyeHeight(), player.getZ()); Vec3 end = new Vec3(player.getX() + look.x * raytraceRange, player.getY() + player.getEyeHeight() + look.y * raytraceRange, player.getZ() + look.z * raytraceRange); -// return player.rayTrace(raytraceRange, 1f, RayTraceFluidMode.NEVER); - //TODO 1.14 check if correct + return world.clip(new ClipContext(start, end, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, player)); } + public static boolean isGameActive() { + return !(Minecraft.getInstance().level == null || Minecraft.getInstance().player == null); + } + } diff --git a/src/main/java/nl/requios/effortlessbuilding/CommonEvents.java b/src/main/java/nl/requios/effortlessbuilding/CommonEvents.java index 60f8192..78114b9 100644 --- a/src/main/java/nl/requios/effortlessbuilding/CommonEvents.java +++ b/src/main/java/nl/requios/effortlessbuilding/CommonEvents.java @@ -12,7 +12,6 @@ import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent; import net.minecraftforge.common.util.FakePlayer; import net.minecraftforge.event.AttachCapabilitiesEvent; import net.minecraftforge.event.TickEvent; -import net.minecraftforge.event.entity.living.LivingEntityUseItemEvent; import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.event.level.BlockEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -29,7 +28,6 @@ import nl.requios.effortlessbuilding.utilities.ReachHelper; import nl.requios.effortlessbuilding.network.AddUndoMessage; import nl.requios.effortlessbuilding.network.ClearUndoMessage; import nl.requios.effortlessbuilding.network.PacketHandler; -import nl.requios.effortlessbuilding.network.RequestLookAtMessage; @EventBusSubscriber public class CommonEvents { @@ -61,18 +59,17 @@ public class CommonEvents { EffortlessBuilding.DELAYED_BLOCK_PLACER.tick(); } + //Cancel event if necessary. Nothing more, rest is handled on mouse right click @SubscribeEvent public static void onBlockPlaced(BlockEvent.EntityPlaceEvent event) { if (event.getLevel().isClientSide()) return; //Never called clientside anyway, but just to be sure - if (!(event.getEntity() instanceof Player player)) return; - if (event.getEntity() instanceof FakePlayer) return; BuildModes.BuildModeEnum buildMode = ModeSettingsManager.getModeSettings(player).getBuildMode(); ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); - if (buildMode != BuildModes.BuildModeEnum.DISABLED) { + if (buildMode != BuildModes.BuildModeEnum.DISABLED || modifierSettings.doQuickReplace()) { //Only cancel if itemblock in hand //Fixed issue with e.g. Create Wrench shift-rightclick disassembling being cancelled. @@ -80,19 +77,10 @@ public class CommonEvents { event.setCanceled(true); } - } else if (modifierSettings.doQuickReplace()) { - //Cancel event and send message if QuickReplace - if (isPlayerHoldingBlock(player)) { - event.setCanceled(true); - } - PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new RequestLookAtMessage(true)); - PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new AddUndoMessage(event.getPos(), event.getBlockSnapshot().getReplacedBlock(), event.getState())); - } else { + } else { //NORMAL mode, let vanilla handle block placing - //But modifiers should still work - //Send message to client, which sends message back with raytrace info - PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new RequestLookAtMessage(false)); + //TODO move UndoRedo to serverside only PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new AddUndoMessage(event.getPos(), event.getBlockSnapshot().getReplacedBlock(), event.getState())); } } @@ -100,7 +88,6 @@ public class CommonEvents { @SubscribeEvent public static void onBlockBroken(BlockEvent.BreakEvent event) { if (event.getLevel().isClientSide()) return; - Player player = event.getPlayer(); if (player instanceof FakePlayer) return; @@ -111,9 +98,6 @@ public class CommonEvents { event.setCanceled(true); } else { //NORMAL mode, let vanilla handle block breaking - //But modifiers and QuickReplace should still work - //Dont break the original block yourself, otherwise Tinkers Hammer and Veinminer won't work - BuildModes.onBlockBroken(player, event.getPos(), false); //Add to undo stack in client if (player instanceof ServerPlayer && event.getState() != null && event.getPos() != null) { diff --git a/src/main/java/nl/requios/effortlessbuilding/EffortlessBuildingClient.java b/src/main/java/nl/requios/effortlessbuilding/EffortlessBuildingClient.java index b7ad4cc..c820199 100644 --- a/src/main/java/nl/requios/effortlessbuilding/EffortlessBuildingClient.java +++ b/src/main/java/nl/requios/effortlessbuilding/EffortlessBuildingClient.java @@ -3,14 +3,18 @@ package nl.requios.effortlessbuilding; import net.minecraft.client.gui.screens.MenuScreens; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +import nl.requios.effortlessbuilding.buildmode.BuildModes; +import nl.requios.effortlessbuilding.buildmodifier.BuildModifiers; import nl.requios.effortlessbuilding.gui.DiamondRandomizerBagScreen; import nl.requios.effortlessbuilding.gui.GoldenRandomizerBagScreen; import nl.requios.effortlessbuilding.gui.RandomizerBagScreen; -import nl.requios.effortlessbuilding.systems.BlockChain; +import nl.requios.effortlessbuilding.systems.BuilderChain; public class EffortlessBuildingClient { - public static final BlockChain BLOCK_CHAIN = new BlockChain(); + 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 void onConstructorClient(IEventBus modEventBus, IEventBus forgeEventBus) { modEventBus.addListener(EffortlessBuildingClient::clientSetup); diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/BaseBuildMode.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/BaseBuildMode.java index 4835b72..feb2f82 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/BaseBuildMode.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/BaseBuildMode.java @@ -4,36 +4,32 @@ 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.Dictionary; -import java.util.Hashtable; -import java.util.UUID; +import java.util.List; public abstract class BaseBuildMode implements IBuildMode { - //In singleplayer client and server variables are shared - //Split everything that needs separate values and may not be called twice in one click - protected Dictionary rightClickClientTable = new Hashtable<>(); - protected Dictionary rightClickServerTable = new Hashtable<>(); - protected Dictionary firstPosTable = new Hashtable<>(); - protected Dictionary sideHitTable = new Hashtable<>(); - protected Dictionary hitVecTable = new Hashtable<>(); + + protected int clicks; @Override - public void initialize(Player player) { - rightClickClientTable.put(player.getUUID(), 0); - rightClickServerTable.put(player.getUUID(), 0); - firstPosTable.put(player.getUUID(), BlockPos.ZERO); - sideHitTable.put(player.getUUID(), Direction.UP); - hitVecTable.put(player.getUUID(), Vec3.ZERO); + public void initialize() { + clicks = 0; } @Override + public boolean onClick(List blocks) { + clicks++; + return false; + } + + @Override @Deprecated public Direction getSideHit(Player player) { - return sideHitTable.get(player.getUUID()); + return Direction.UP; } - @Override + @Override @Deprecated public Vec3 getHitVec(Player player) { - return hitVecTable.get(player.getUUID()); + return new Vec3(0.5, 0.5, 0.5); } } diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java index c35038a..cb1bd90 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/BuildModes.java @@ -2,179 +2,39 @@ package nl.requios.effortlessbuilding.buildmode; import com.mojang.math.Vector4f; import net.minecraft.world.entity.player.Player; -import net.minecraft.core.Direction; import net.minecraft.core.BlockPos; import net.minecraft.world.level.ClipContext; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; -import nl.requios.effortlessbuilding.EffortlessBuilding; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; import nl.requios.effortlessbuilding.buildmode.buildmodes.*; -import nl.requios.effortlessbuilding.buildmodifier.BuildModifiers; -import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; +import nl.requios.effortlessbuilding.utilities.BlockEntry; import nl.requios.effortlessbuilding.utilities.ReachHelper; -import nl.requios.effortlessbuilding.utilities.SurvivalHelper; -import nl.requios.effortlessbuilding.network.BlockBrokenMessage; -import nl.requios.effortlessbuilding.network.BlockPlacedMessage; -import java.util.ArrayList; -import java.util.Dictionary; -import java.util.Hashtable; -import java.util.List; +import java.util.*; import static nl.requios.effortlessbuilding.buildmode.ModeOptions.OptionEnum; +@OnlyIn(Dist.CLIENT) public class BuildModes { - //Static variables are shared between client and server in singleplayer - //We need them separate - public static Dictionary currentlyBreakingClient = new Hashtable<>(); - public static Dictionary currentlyBreakingServer = new Hashtable<>(); - - //Uses a network message to get the previous raytraceresult from the player - //The server could keep track of all raytraceresults but this might lag with many players - //Raytraceresult is needed for sideHit and hitVec - public static void onBlockPlacedMessage(Player player, BlockPlacedMessage message) { - - //Check if not in the middle of breaking - Dictionary currentlyBreaking = player.level.isClientSide ? currentlyBreakingClient : currentlyBreakingServer; - if (currentlyBreaking.get(player) != null && currentlyBreaking.get(player)) { - //Cancel breaking - initializeMode(player); - return; - } - - ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); - ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); - BuildModeEnum buildMode = modeSettings.getBuildMode(); - - BlockPos startPos = null; - - if (message.isBlockHit() && message.getBlockPos() != null) { - startPos = message.getBlockPos(); - - //Offset in direction of sidehit if not quickreplace and not replaceable - boolean replaceable = player.level.getBlockState(startPos).getMaterial().isReplaceable(); - boolean becomesDoubleSlab = SurvivalHelper.doesBecomeDoubleSlab(player, startPos); - if (!modifierSettings.doQuickReplace() && !replaceable && !becomesDoubleSlab) { - startPos = startPos.relative(message.getSideHit()); - } - - //Get under tall grass and other replaceable blocks - if (modifierSettings.doQuickReplace() && replaceable) { - startPos = startPos.below(); - } - - //Check if player reach does not exceed startpos - int maxReach = ReachHelper.getMaxReach(player); - if (buildMode != BuildModeEnum.DISABLED && player.blockPosition().distSqr(startPos) > maxReach * maxReach) { - EffortlessBuilding.log(player, "Placement exceeds your reach."); - return; - } - } - - //Even when no starting block is found, call buildmode instance - //We might want to place things in the air - List coordinates = buildMode.instance.onRightClick(player, startPos, message.getSideHit(), message.getHitVec(), modifierSettings.doQuickReplace()); - - if (coordinates.isEmpty()) { - currentlyBreaking.put(player, false); - return; - } + public void findCoordinates(List blocks, Player player, BuildModeEnum buildMode) { + buildMode.instance.findCoordinates(blocks); //Limit number of blocks you can place int limit = ReachHelper.getMaxBlocksPlacedAtOnce(player); - if (coordinates.size() > limit) { - coordinates = coordinates.subList(0, limit); + while (blocks.size() > limit) { + blocks.remove(blocks.size()-1); } - - Direction sideHit = buildMode.instance.getSideHit(player); - if (sideHit == null) sideHit = message.getSideHit(); - - Vec3 hitVec = buildMode.instance.getHitVec(player); - if (hitVec == null) hitVec = message.getHitVec(); - - BuildModifiers.onBlockPlaced(player, coordinates, sideHit, hitVec, message.getPlaceStartPos()); - - //Only works when finishing a buildmode is equal to placing some blocks - //No intermediate blocks allowed - currentlyBreaking.remove(player); } - //Use a network message to break blocks in the distance using clientside mouse input - public static void onBlockBrokenMessage(Player player, BlockBrokenMessage message) { - BlockPos startPos = message.isBlockHit() ? message.getBlockPos() : null; - onBlockBroken(player, startPos, true); + public BuildModeEnum getBuildMode(Player player) { + return ModeSettingsManager.getModeSettings(player).getBuildMode(); } - public static void onBlockBroken(Player player, BlockPos startPos, boolean breakStartPos) { - - //Check if not in the middle of placing - Dictionary currentlyBreaking = player.level.isClientSide ? currentlyBreakingClient : currentlyBreakingServer; - if (currentlyBreaking.get(player) != null && !currentlyBreaking.get(player)) { - //Cancel placing - initializeMode(player); - return; - } - - if (!ReachHelper.canBreakFar(player)) return; - - //If first click - if (currentlyBreaking.get(player) == null) { - //If startpos is null, dont do anything - if (startPos == null) return; - } - - ModifierSettingsManager.ModifierSettings modifierSettings = ModifierSettingsManager.getModifierSettings(player); - ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); - - //Get coordinates - BuildModeEnum buildMode = modeSettings.getBuildMode(); - List coordinates = buildMode.instance.onRightClick(player, startPos, Direction.UP, Vec3.ZERO, true); - - if (coordinates.isEmpty()) { - currentlyBreaking.put(player, true); - return; - } - - //Let buildmodifiers break blocks - BuildModifiers.onBlockBroken(player, coordinates, breakStartPos); - - //Only works when finishing a buildmode is equal to breaking some blocks - //No intermediate blocks allowed - currentlyBreaking.remove(player); - } - - public static List findCoordinates(Player player, BlockPos startPos, boolean skipRaytrace) { - List coordinates = new ArrayList<>(); - - ModeSettingsManager.ModeSettings modeSettings = ModeSettingsManager.getModeSettings(player); - coordinates.addAll(modeSettings.getBuildMode().instance.findCoordinates(player, startPos, skipRaytrace)); - - return coordinates; - } - - public static void initializeMode(Player player) { - //Resetting mode, so not placing or breaking - Dictionary currentlyBreaking = player.level.isClientSide ? currentlyBreakingClient : currentlyBreakingServer; - currentlyBreaking.remove(player); - - ModeSettingsManager.getModeSettings(player).getBuildMode().instance.initialize(player); - } - - public static boolean isCurrentlyPlacing(Player player) { - Dictionary currentlyBreaking = player.level.isClientSide ? currentlyBreakingClient : currentlyBreakingServer; - return currentlyBreaking.get(player) != null && !currentlyBreaking.get(player); - } - - public static boolean isCurrentlyBreaking(Player player) { - Dictionary currentlyBreaking = player.level.isClientSide ? currentlyBreakingClient : currentlyBreakingServer; - return currentlyBreaking.get(player) != null && currentlyBreaking.get(player); - } - - //Either placing or breaking - public static boolean isActive(Player player) { - Dictionary currentlyBreaking = player.level.isClientSide ? currentlyBreakingClient : currentlyBreakingServer; - return currentlyBreaking.get(player) != null; + public void onCancel(Player player) { + getBuildMode(player).instance.initialize(); } //Find coordinates on a line bound by a plane diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/IBuildMode.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/IBuildMode.java index dd5a82a..0d25f07 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/IBuildMode.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/IBuildMode.java @@ -4,22 +4,32 @@ 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; public interface IBuildMode { //Fired when a player selects a buildmode and when it needs to initializeMode - void initialize(Player player); + void initialize(); //Fired when a block would be placed //Return a list of coordinates where you want to place blocks + @Deprecated List onRightClick(Player player, BlockPos blockPos, Direction sideHit, Vec3 hitVec, boolean skipRaytrace); + //Returns if we should place blocks now + boolean onClick(List blocks); + //Fired continuously for visualization purposes + @Deprecated List findCoordinates(Player player, BlockPos blockPos, boolean skipRaytrace); - Direction getSideHit(Player player); + void findCoordinates(List blocks); + @Deprecated + Direction getSideHit(Player player); + + @Deprecated Vec3 getHitVec(Player player); } diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeSettingsManager.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeSettingsManager.java index 655ab98..3eb5b86 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeSettingsManager.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/ModeSettingsManager.java @@ -6,6 +6,7 @@ import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.network.PacketDistributor; import nl.requios.effortlessbuilding.EffortlessBuilding; +import nl.requios.effortlessbuilding.EffortlessBuildingClient; import nl.requios.effortlessbuilding.capability.ModeCapabilityManager; import nl.requios.effortlessbuilding.utilities.ReachHelper; import nl.requios.effortlessbuilding.network.ModeSettingsMessage; @@ -47,7 +48,9 @@ public class ModeSettingsManager { modeCapability.ifPresent((capability) -> { capability.setModeData(modeSettings); - BuildModes.initializeMode(player); + if (player.level.isClientSide) { + EffortlessBuildingClient.BUILDER_CHAIN.cancel(); + } }); if (!modeCapability.isPresent()) { @@ -56,11 +59,7 @@ public class ModeSettingsManager { } public static String sanitize(ModeSettings modeSettings, Player player) { - int maxReach = ReachHelper.getMaxReach(player); String error = ""; - - //TODO sanitize - return error; } diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/ThreeClicksBuildMode.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/ThreeClicksBuildMode.java index ad7f009..0208d31 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/ThreeClicksBuildMode.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/ThreeClicksBuildMode.java @@ -12,49 +12,6 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode { protected Dictionary secondPosTable = new Hashtable<>(); //Finds height after floor has been chosen in buildmodes with 3 clicks - public static BlockPos findHeight(Player player, BlockPos secondPos, boolean skipRaytrace) { - Vec3 look = BuildModes.getPlayerLookVec(player); - Vec3 start = new Vec3(player.getX(), player.getY() + player.getEyeHeight(), player.getZ()); - - List criteriaList = new ArrayList<>(3); - - //X - Vec3 xBound = BuildModes.findXBound(secondPos.getX(), start, look); - criteriaList.add(new HeightCriteria(xBound, secondPos, start)); - - //Z - Vec3 zBound = BuildModes.findZBound(secondPos.getZ(), start, look); - criteriaList.add(new HeightCriteria(zBound, secondPos, start)); - - //Remove invalid criteria - int reach = ReachHelper.getPlacementReach(player) * 4; //4 times as much as normal placement reach - criteriaList.removeIf(criteria -> !criteria.isValid(start, look, reach, player, skipRaytrace)); - - //If none are valid, return empty list of blocks - if (criteriaList.isEmpty()) return null; - - //If only 1 is valid, choose that one - HeightCriteria selected = criteriaList.get(0); - - //If multiple are valid, choose based on criteria - if (criteriaList.size() > 1) { - //Select the one that is closest (from wall position to its line counterpart) - for (int i = 1; i < criteriaList.size(); i++) { - HeightCriteria criteria = criteriaList.get(i); - if (criteria.distToLineSq < 2.0 && selected.distToLineSq < 2.0) { - //Both very close to line, choose closest to player - if (criteria.distToPlayerSq < selected.distToPlayerSq) - selected = criteria; - } else { - //Pick closest to line - if (criteria.distToLineSq < selected.distToLineSq) - selected = criteria; - } - } - } - return new BlockPos(selected.lineBound); - } - @Override public void initialize(Player player) { super.initialize(player); @@ -172,6 +129,49 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode { return list; } + public static BlockPos findHeight(Player player, BlockPos secondPos, boolean skipRaytrace) { + Vec3 look = BuildModes.getPlayerLookVec(player); + Vec3 start = new Vec3(player.getX(), player.getY() + player.getEyeHeight(), player.getZ()); + + List criteriaList = new ArrayList<>(3); + + //X + Vec3 xBound = BuildModes.findXBound(secondPos.getX(), start, look); + criteriaList.add(new HeightCriteria(xBound, secondPos, start)); + + //Z + Vec3 zBound = BuildModes.findZBound(secondPos.getZ(), start, look); + criteriaList.add(new HeightCriteria(zBound, secondPos, start)); + + //Remove invalid criteria + int reach = ReachHelper.getPlacementReach(player) * 4; //4 times as much as normal placement reach + criteriaList.removeIf(criteria -> !criteria.isValid(start, look, reach, player, skipRaytrace)); + + //If none are valid, return empty list of blocks + if (criteriaList.isEmpty()) return null; + + //If only 1 is valid, choose that one + HeightCriteria selected = criteriaList.get(0); + + //If multiple are valid, choose based on criteria + if (criteriaList.size() > 1) { + //Select the one that is closest (from wall position to its line counterpart) + for (int i = 1; i < criteriaList.size(); i++) { + HeightCriteria criteria = criteriaList.get(i); + if (criteria.distToLineSq < 2.0 && selected.distToLineSq < 2.0) { + //Both very close to line, choose closest to player + if (criteria.distToPlayerSq < selected.distToPlayerSq) + selected = criteria; + } else { + //Pick closest to line + if (criteria.distToLineSq < selected.distToLineSq) + selected = criteria; + } + } + } + return new BlockPos(selected.lineBound); + } + //Finds the place of the second block pos protected abstract BlockPos findSecondPos(Player player, BlockPos firstPos, boolean skipRaytrace); diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmode/TwoClicksBuildMode.java b/src/main/java/nl/requios/effortlessbuilding/buildmode/TwoClicksBuildMode.java index 7bb99cf..3e5f366 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmode/TwoClicksBuildMode.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmode/TwoClicksBuildMode.java @@ -4,6 +4,7 @@ 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 nl.requios.effortlessbuilding.utilities.ReachHelper; import java.util.ArrayList; @@ -13,34 +14,28 @@ import java.util.UUID; public abstract class TwoClicksBuildMode extends BaseBuildMode { + protected BlockEntry firstBlockEntry; + @Override - public List onRightClick(Player player, BlockPos blockPos, Direction sideHit, Vec3 hitVec, boolean skipRaytrace) { - List list = new ArrayList<>(); + public boolean onClick(List blocks) { + super.onClick(blocks); - Dictionary rightClickTable = player.level.isClientSide ? rightClickClientTable : rightClickServerTable; - int rightClickNr = rightClickTable.get(player.getUUID()); - rightClickNr++; - rightClickTable.put(player.getUUID(), rightClickNr); + if (clicks == 1) { + //First click, remember starting position - if (rightClickNr == 1) { //If clicking in air, reset and try again - if (blockPos == null) { - rightClickTable.put(player.getUUID(), 0); - return list; + if (blocks.size() == 0) { + clicks = 0; + return false; } - //First click, remember starting position - firstPosTable.put(player.getUUID(), blockPos); - sideHitTable.put(player.getUUID(), sideHit); - hitVecTable.put(player.getUUID(), hitVec); - //Keep list empty, dont place any blocks yet + firstBlockEntry = blocks.get(0); } else { //Second click, place blocks - list = findCoordinates(player, blockPos, skipRaytrace); - rightClickTable.put(player.getUUID(), 0); + clicks = 0; + return true; } - - return list; + return false; } @Override diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BlockSet.java b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BlockSet.java index cc9d992..a145646 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BlockSet.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BlockSet.java @@ -6,6 +6,7 @@ import net.minecraft.world.phys.Vec3; import java.util.List; +//Used only for Undo public class BlockSet { private final List coordinates; private final List previousBlockStates; diff --git a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BuildModifiers.java b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BuildModifiers.java index 1febfec..9fbc08b 100644 --- a/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BuildModifiers.java +++ b/src/main/java/nl/requios/effortlessbuilding/buildmodifier/BuildModifiers.java @@ -1,5 +1,6 @@ package nl.requios.effortlessbuilding.buildmodifier; +import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.Blocks; @@ -18,6 +19,7 @@ import nl.requios.effortlessbuilding.CommonConfig; 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.SurvivalHelper; import nl.requios.effortlessbuilding.item.AbstractRandomizerBagItem; import nl.requios.effortlessbuilding.render.BlockPreviews; @@ -28,6 +30,10 @@ import java.util.List; public class BuildModifiers { + public void findCoordinates(List blocks, LocalPlayer player, ModifierSettingsManager.ModifierSettings modifierSettings) { + + } + //Called from BuildModes public static void onBlockPlaced(Player player, List startCoordinates, Direction sideHit, Vec3 hitVec, boolean placeStartPos) { Level world = player.level; diff --git a/src/main/java/nl/requios/effortlessbuilding/capability/ModeCapabilityManager.java b/src/main/java/nl/requios/effortlessbuilding/capability/ModeCapabilityManager.java index 6942e87..19afd49 100644 --- a/src/main/java/nl/requios/effortlessbuilding/capability/ModeCapabilityManager.java +++ b/src/main/java/nl/requios/effortlessbuilding/capability/ModeCapabilityManager.java @@ -9,11 +9,12 @@ import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import nl.requios.effortlessbuilding.buildmode.BuildModes; +import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager.ModeSettings; +import static nl.requios.effortlessbuilding.buildmode.ModeSettingsManager.ModeSettings; @Mod.EventBusSubscriber public class ModeCapabilityManager { @@ -83,7 +84,7 @@ public class ModeCapabilityManager { public Tag serializeNBT() { CompoundTag compound = new CompoundTag(); ModeSettings modeSettings = instance.getModeData(); - if (modeSettings == null) modeSettings = new ModeSettings(); + if (modeSettings == null) modeSettings = new ModeSettingsManager.ModeSettings(); //compound.putInteger("buildMode", modeSettings.getBuildMode().ordinal()); diff --git a/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/RadialMenu.java b/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/RadialMenu.java index 67d3cc3..3c924c7 100644 --- a/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/RadialMenu.java +++ b/src/main/java/nl/requios/effortlessbuilding/gui/buildmode/RadialMenu.java @@ -25,7 +25,6 @@ import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager; import nl.requios.effortlessbuilding.network.ModeActionMessage; import nl.requios.effortlessbuilding.network.ModeSettingsMessage; import nl.requios.effortlessbuilding.network.PacketHandler; -import nl.requios.effortlessbuilding.proxy.ClientProxy; import org.apache.commons.lang3.text.WordUtils; import org.lwjgl.opengl.GL11; diff --git a/src/main/java/nl/requios/effortlessbuilding/network/BlockBrokenMessage.java b/src/main/java/nl/requios/effortlessbuilding/network/BlockBrokenMessage.java deleted file mode 100644 index 2f03c86..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/network/BlockBrokenMessage.java +++ /dev/null @@ -1,92 +0,0 @@ -package nl.requios.effortlessbuilding.network; - -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.core.Direction; -import net.minecraft.core.BlockPos; -import net.minecraft.world.phys.BlockHitResult; -import net.minecraft.world.phys.HitResult; -import net.minecraft.world.phys.Vec3; -import net.minecraftforge.fml.LogicalSide; -import net.minecraftforge.network.NetworkEvent; -import nl.requios.effortlessbuilding.buildmode.BuildModes; - -import java.util.function.Supplier; - -/*** - * Sends a message to the server indicating that a player wants to break a block - */ -public class BlockBrokenMessage { - - private final boolean blockHit; - private final BlockPos blockPos; - private final Direction sideHit; - private final Vec3 hitVec; - - public BlockBrokenMessage() { - this.blockHit = false; - this.blockPos = BlockPos.ZERO; - this.sideHit = Direction.UP; - this.hitVec = new Vec3(0, 0, 0); - } - - public BlockBrokenMessage(BlockHitResult result) { - this.blockHit = result.getType() == HitResult.Type.BLOCK; - this.blockPos = result.getBlockPos(); - this.sideHit = result.getDirection(); - this.hitVec = result.getLocation(); - } - - public BlockBrokenMessage(boolean blockHit, BlockPos blockPos, Direction sideHit, Vec3 hitVec) { - this.blockHit = blockHit; - this.blockPos = blockPos; - this.sideHit = sideHit; - this.hitVec = hitVec; - } - - public static void encode(BlockBrokenMessage message, FriendlyByteBuf buf) { - buf.writeBoolean(message.blockHit); - buf.writeInt(message.blockPos.getX()); - buf.writeInt(message.blockPos.getY()); - buf.writeInt(message.blockPos.getZ()); - buf.writeInt(message.sideHit.get3DDataValue()); - buf.writeDouble(message.hitVec.x); - buf.writeDouble(message.hitVec.y); - buf.writeDouble(message.hitVec.z); - } - - public static BlockBrokenMessage decode(FriendlyByteBuf buf) { - boolean blockHit = buf.readBoolean(); - BlockPos blockPos = new BlockPos(buf.readInt(), buf.readInt(), buf.readInt()); - Direction sideHit = Direction.from3DDataValue(buf.readInt()); - Vec3 hitVec = new Vec3(buf.readDouble(), buf.readDouble(), buf.readDouble()); - return new BlockBrokenMessage(blockHit, blockPos, sideHit, hitVec); - } - - public boolean isBlockHit() { - return blockHit; - } - - public BlockPos getBlockPos() { - return blockPos; - } - - public Direction getSideHit() { - return sideHit; - } - - public Vec3 getHitVec() { - return hitVec; - } - - public static class Handler { - public static void handle(BlockBrokenMessage message, Supplier ctx) { - ctx.get().enqueueWork(() -> { - if (ctx.get().getDirection().getReceptionSide() == LogicalSide.SERVER) { - //Received serverside - BuildModes.onBlockBrokenMessage(ctx.get().getSender(), message); - } - }); - ctx.get().setPacketHandled(true); - } - } -} diff --git a/src/main/java/nl/requios/effortlessbuilding/network/BlockPlacedMessage.java b/src/main/java/nl/requios/effortlessbuilding/network/BlockPlacedMessage.java deleted file mode 100644 index f588936..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/network/BlockPlacedMessage.java +++ /dev/null @@ -1,118 +0,0 @@ -package nl.requios.effortlessbuilding.network; - -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.core.Direction; -import net.minecraft.core.BlockPos; -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.api.distmarker.OnlyIn; -import net.minecraftforge.fml.DistExecutor; -import net.minecraftforge.fml.LogicalSide; -import net.minecraftforge.network.NetworkEvent; -import nl.requios.effortlessbuilding.buildmode.BuildModes; -import nl.requios.effortlessbuilding.render.BlockPreviews; - -import java.util.function.Supplier; - -/*** - * Sends a message to the server indicating that a player wants to place a block. - * Received clientside: server has placed blocks and its letting the client know. - */ -public class BlockPlacedMessage { - - private final boolean blockHit; - private final BlockPos blockPos; - private final Direction sideHit; - private final Vec3 hitVec; - private final boolean placeStartPos; //prevent double placing in normal mode - - public BlockPlacedMessage() { - this.blockHit = false; - this.blockPos = BlockPos.ZERO; - this.sideHit = Direction.UP; - this.hitVec = new Vec3(0, 0, 0); - this.placeStartPos = true; - } - - public BlockPlacedMessage(BlockHitResult result, boolean placeStartPos) { - this.blockHit = result.getType() == HitResult.Type.BLOCK; - this.blockPos = result.getBlockPos(); - this.sideHit = result.getDirection(); - this.hitVec = result.getLocation(); - this.placeStartPos = placeStartPos; - } - - public BlockPlacedMessage(boolean blockHit, BlockPos blockPos, Direction sideHit, Vec3 hitVec, boolean placeStartPos) { - this.blockHit = blockHit; - this.blockPos = blockPos; - this.sideHit = sideHit; - this.hitVec = hitVec; - this.placeStartPos = placeStartPos; - } - - public static void encode(BlockPlacedMessage message, FriendlyByteBuf buf) { - buf.writeBoolean(message.blockHit); - buf.writeInt(message.blockPos.getX()); - buf.writeInt(message.blockPos.getY()); - buf.writeInt(message.blockPos.getZ()); - buf.writeInt(message.sideHit.get3DDataValue()); - buf.writeDouble(message.hitVec.x); - buf.writeDouble(message.hitVec.y); - buf.writeDouble(message.hitVec.z); - buf.writeBoolean(message.placeStartPos); - } - - public static BlockPlacedMessage decode(FriendlyByteBuf buf) { - boolean blockHit = buf.readBoolean(); - BlockPos blockPos = new BlockPos(buf.readInt(), buf.readInt(), buf.readInt()); - Direction sideHit = Direction.from3DDataValue(buf.readInt()); - Vec3 hitVec = new Vec3(buf.readDouble(), buf.readDouble(), buf.readDouble()); - boolean placeStartPos = buf.readBoolean(); - return new BlockPlacedMessage(blockHit, blockPos, sideHit, hitVec, placeStartPos); - } - - public boolean isBlockHit() { - return blockHit; - } - - public BlockPos getBlockPos() { - return blockPos; - } - - public Direction getSideHit() { - return sideHit; - } - - public Vec3 getHitVec() { - return hitVec; - } - - public boolean getPlaceStartPos() { - return placeStartPos; - } - - public static class Handler { - public static void handle(BlockPlacedMessage message, Supplier ctx) { - ctx.get().enqueueWork(() -> { - if (ctx.get().getDirection().getReceptionSide() == LogicalSide.CLIENT) { - //Received clientside - DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> ClientHandler.handle(message, ctx)); - } else { - //Received serverside - BuildModes.onBlockPlacedMessage(ctx.get().getSender(), message); - } - }); - ctx.get().setPacketHandled(true); - } - } - - @OnlyIn(Dist.CLIENT) - public static class ClientHandler { - public static void handle(BlockPlacedMessage message, Supplier ctx) { - //Nod RenderHandler to do the dissolve shader effect - BlockPreviews.onBlocksPlaced(); - } - } -} diff --git a/src/main/java/nl/requios/effortlessbuilding/network/CancelModeMessage.java b/src/main/java/nl/requios/effortlessbuilding/network/CancelModeMessage.java deleted file mode 100644 index eef4b14..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/network/CancelModeMessage.java +++ /dev/null @@ -1,33 +0,0 @@ -package nl.requios.effortlessbuilding.network; - -import net.minecraft.world.entity.player.Player; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraftforge.network.NetworkEvent; -import nl.requios.effortlessbuilding.EffortlessBuilding; -import nl.requios.effortlessbuilding.buildmode.BuildModes; - -import java.util.function.Supplier; - -/** - * Sends a message to the server indicating that a buildmode needs to be canceled for a player - */ -public class CancelModeMessage { - - public static void encode(CancelModeMessage message, FriendlyByteBuf buf) { - } - - public static CancelModeMessage decode(FriendlyByteBuf buf) { - return new CancelModeMessage(); - } - - public static class Handler { - public static void handle(CancelModeMessage message, Supplier ctx) { - ctx.get().enqueueWork(() -> { - Player player = EffortlessBuilding.proxy.getPlayerEntityFromContext(ctx); - - BuildModes.initializeMode(player); - }); - ctx.get().setPacketHandled(true); - } - } -} diff --git a/src/main/java/nl/requios/effortlessbuilding/network/ModeSettingsMessage.java b/src/main/java/nl/requios/effortlessbuilding/network/ModeSettingsMessage.java index e8f0a1d..a4ae9d2 100644 --- a/src/main/java/nl/requios/effortlessbuilding/network/ModeSettingsMessage.java +++ b/src/main/java/nl/requios/effortlessbuilding/network/ModeSettingsMessage.java @@ -6,10 +6,11 @@ import net.minecraftforge.network.NetworkEvent; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.buildmode.BuildModes; import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager; -import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager.ModeSettings; import java.util.function.Supplier; +import static nl.requios.effortlessbuilding.buildmode.ModeSettingsManager.ModeSettings; + /** * Shares mode settings (see ModeSettingsManager) between server and client */ diff --git a/src/main/java/nl/requios/effortlessbuilding/network/OnBlockPlacedMessage.java b/src/main/java/nl/requios/effortlessbuilding/network/OnBlockPlacedMessage.java new file mode 100644 index 0000000..4b83130 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/network/OnBlockPlacedMessage.java @@ -0,0 +1,49 @@ +package nl.requios.effortlessbuilding.network; + +import net.minecraft.network.FriendlyByteBuf; +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.EffortlessBuildingClient; + +import java.util.function.Supplier; + +/*** + * Sends a message to the client indicating that a block has been placed. + * Necessary because Forge's onBlockPlaced event is only called on the server. + */ +public class OnBlockPlacedMessage { + + public OnBlockPlacedMessage() { + } + + public static void encode(OnBlockPlacedMessage message, FriendlyByteBuf buf) { + + } + + public static OnBlockPlacedMessage decode(FriendlyByteBuf buf) { + return new OnBlockPlacedMessage(); + } + + public static class Handler { + public static void handle(OnBlockPlacedMessage message, Supplier ctx) { + ctx.get().enqueueWork(() -> { + if (ctx.get().getDirection().getReceptionSide() == LogicalSide.CLIENT) { + //Received clientside + DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> ClientHandler.handle(message, ctx)); + } + }); + ctx.get().setPacketHandled(true); + } + } + + @OnlyIn(Dist.CLIENT) + public static class ClientHandler { + public static void handle(OnBlockPlacedMessage message, Supplier ctx) { + + EffortlessBuildingClient.BUILDER_CHAIN.onRightClick(); + } + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/network/PacketHandler.java b/src/main/java/nl/requios/effortlessbuilding/network/PacketHandler.java index f5c044d..672ae02 100644 --- a/src/main/java/nl/requios/effortlessbuilding/network/PacketHandler.java +++ b/src/main/java/nl/requios/effortlessbuilding/network/PacketHandler.java @@ -20,12 +20,10 @@ public class PacketHandler { INSTANCE.registerMessage(id++, ModifierSettingsMessage.class, ModifierSettingsMessage::encode, ModifierSettingsMessage::decode, ModifierSettingsMessage.Handler::handle); INSTANCE.registerMessage(id++, ModeSettingsMessage.class, ModeSettingsMessage::encode, ModeSettingsMessage::decode, ModeSettingsMessage.Handler::handle); INSTANCE.registerMessage(id++, ModeActionMessage.class, ModeActionMessage::encode, ModeActionMessage::decode, ModeActionMessage.Handler::handle); - INSTANCE.registerMessage(id++, BlockPlacedMessage.class, BlockPlacedMessage::encode, BlockPlacedMessage::decode, BlockPlacedMessage.Handler::handle); - INSTANCE.registerMessage(id++, BlockBrokenMessage.class, BlockBrokenMessage::encode, BlockBrokenMessage::decode, BlockBrokenMessage.Handler::handle); - INSTANCE.registerMessage(id++, CancelModeMessage.class, CancelModeMessage::encode, CancelModeMessage::decode, CancelModeMessage.Handler::handle); - INSTANCE.registerMessage(id++, RequestLookAtMessage.class, RequestLookAtMessage::encode, RequestLookAtMessage::decode, RequestLookAtMessage.Handler::handle); + INSTANCE.registerMessage(id++, OnBlockPlacedMessage.class, OnBlockPlacedMessage::encode, OnBlockPlacedMessage::decode, OnBlockPlacedMessage.Handler::handle); INSTANCE.registerMessage(id++, AddUndoMessage.class, AddUndoMessage::encode, AddUndoMessage::decode, AddUndoMessage.Handler::handle); INSTANCE.registerMessage(id++, ClearUndoMessage.class, ClearUndoMessage::encode, ClearUndoMessage::decode, ClearUndoMessage.Handler::handle); + INSTANCE.registerMessage(id++, ServerPlaceBlocksMessage.class, ServerPlaceBlocksMessage::encode, ServerPlaceBlocksMessage::decode, ServerPlaceBlocksMessage.Handler::handle); } diff --git a/src/main/java/nl/requios/effortlessbuilding/network/RequestLookAtMessage.java b/src/main/java/nl/requios/effortlessbuilding/network/RequestLookAtMessage.java deleted file mode 100644 index 093dcce..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/network/RequestLookAtMessage.java +++ /dev/null @@ -1,72 +0,0 @@ -package nl.requios.effortlessbuilding.network; - -import net.minecraft.world.entity.player.Player; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.world.phys.BlockHitResult; -import net.minecraft.world.phys.HitResult; -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.ClientEvents; -import nl.requios.effortlessbuilding.EffortlessBuilding; - -import java.util.function.Supplier; - -/*** - * Sends a message to the client asking for its lookat (objectmouseover) data. - * This is then sent back with a BlockPlacedMessage. - */ -public class RequestLookAtMessage { - private final boolean placeStartPos; - - public RequestLookAtMessage() { - placeStartPos = false; - } - - public RequestLookAtMessage(boolean placeStartPos) { - this.placeStartPos = placeStartPos; - } - - public static void encode(RequestLookAtMessage message, FriendlyByteBuf buf) { - buf.writeBoolean(message.placeStartPos); - } - - public static RequestLookAtMessage decode(FriendlyByteBuf buf) { - boolean placeStartPos = buf.readBoolean(); - return new RequestLookAtMessage(placeStartPos); - } - - public boolean getPlaceStartPos() { - return placeStartPos; - } - - public static class Handler { - public static void handle(RequestLookAtMessage message, Supplier ctx) { - ctx.get().enqueueWork(() -> { - if (ctx.get().getDirection().getReceptionSide() == LogicalSide.CLIENT) { - //Received clientside - DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> ClientHandler.handle(message, ctx)); - } - }); - ctx.get().setPacketHandled(true); - } - } - - @OnlyIn(Dist.CLIENT) - public static class ClientHandler { - public static void handle(RequestLookAtMessage message, Supplier ctx) { - //Send back your info - Player player = EffortlessBuilding.proxy.getPlayerEntityFromContext(ctx); - - //Prevent double placing in normal mode with placeStartPos false - //Unless QuickReplace is on, then we do need to place start pos. - if (ClientEvents.previousLookAt.getType() == HitResult.Type.BLOCK) { - PacketHandler.INSTANCE.sendToServer(new BlockPlacedMessage((BlockHitResult) ClientEvents.previousLookAt, message.getPlaceStartPos())); - } else { - PacketHandler.INSTANCE.sendToServer(new BlockPlacedMessage()); - } - } - } -} diff --git a/src/main/java/nl/requios/effortlessbuilding/network/ServerPlaceBlocksMessage.java b/src/main/java/nl/requios/effortlessbuilding/network/ServerPlaceBlocksMessage.java new file mode 100644 index 0000000..e085214 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/network/ServerPlaceBlocksMessage.java @@ -0,0 +1,47 @@ +package nl.requios.effortlessbuilding.network; + +import net.minecraft.nbt.NbtUtils; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.entity.player.Player; +import net.minecraftforge.network.NetworkEvent; +import nl.requios.effortlessbuilding.EffortlessBuilding; +import nl.requios.effortlessbuilding.utilities.BlockEntry; + +import java.util.List; +import java.util.function.Supplier; + +/** + * Sends a message to the server to place multiple blocks + */ +public class ServerPlaceBlocksMessage { + + private List blocks; + + public ServerPlaceBlocksMessage() {} + + public ServerPlaceBlocksMessage(List blocks) { + this.blocks = blocks; + } + + public static void encode(ServerPlaceBlocksMessage message, FriendlyByteBuf buf) { + buf.writeCollection(message.blocks, BlockEntry::encode); + } + + public static ServerPlaceBlocksMessage decode(FriendlyByteBuf buf) { + ServerPlaceBlocksMessage message = new ServerPlaceBlocksMessage(); + message.blocks = buf.readList(BlockEntry::decode); + return new ServerPlaceBlocksMessage(); + } + + + public static class Handler { + public static void handle(ServerPlaceBlocksMessage message, Supplier ctx) { + ctx.get().enqueueWork(() -> { + Player player = EffortlessBuilding.proxy.getPlayerEntityFromContext(ctx); + + EffortlessBuilding.SERVER_BLOCK_PLACER.placeBlocks(player, message.blocks); + }); + ctx.get().setPacketHandled(true); + } + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviews.java b/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviews.java index 93b9134..7f73c72 100644 --- a/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviews.java +++ b/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviews.java @@ -21,8 +21,6 @@ import nl.requios.effortlessbuilding.CommonConfig; import nl.requios.effortlessbuilding.EffortlessBuilding; import nl.requios.effortlessbuilding.buildmode.BuildModes; import nl.requios.effortlessbuilding.buildmode.IBuildMode; -import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager; -import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager.ModeSettings; import nl.requios.effortlessbuilding.buildmodifier.BuildModifiers; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager.ModifierSettings; @@ -38,6 +36,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import static nl.requios.effortlessbuilding.buildmode.ModeSettingsManager.ModeSettings; + @OnlyIn(Dist.CLIENT) public class BlockPreviews { private static final List placedDataList = new ArrayList<>(); diff --git a/src/main/java/nl/requios/effortlessbuilding/systems/BlockChain.java b/src/main/java/nl/requios/effortlessbuilding/systems/BlockChain.java deleted file mode 100644 index 4340792..0000000 --- a/src/main/java/nl/requios/effortlessbuilding/systems/BlockChain.java +++ /dev/null @@ -1,11 +0,0 @@ -package nl.requios.effortlessbuilding.systems; - -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; - -// Receives block placed events, then finds additional blocks we want to place through various systems, -// and then sends them to the server to be placed -@OnlyIn(Dist.CLIENT) -public class BlockChain { - -} diff --git a/src/main/java/nl/requios/effortlessbuilding/systems/BuilderChain.java b/src/main/java/nl/requios/effortlessbuilding/systems/BuilderChain.java new file mode 100644 index 0000000..11cd076 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/systems/BuilderChain.java @@ -0,0 +1,193 @@ +package nl.requios.effortlessbuilding.systems; + +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BlockItem; +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; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import nl.requios.effortlessbuilding.ClientEvents; +import nl.requios.effortlessbuilding.EffortlessBuildingClient; +import nl.requios.effortlessbuilding.buildmode.ModeSettingsManager; +import nl.requios.effortlessbuilding.buildmodifier.ModifierSettingsManager; +import nl.requios.effortlessbuilding.compatibility.CompatHelper; +import nl.requios.effortlessbuilding.network.PacketHandler; +import nl.requios.effortlessbuilding.network.ServerPlaceBlocksMessage; +import nl.requios.effortlessbuilding.utilities.BlockEntry; +import nl.requios.effortlessbuilding.utilities.ReachHelper; +import nl.requios.effortlessbuilding.utilities.SurvivalHelper; + +import java.util.ArrayList; +import java.util.List; + +// Receives block placed events, then finds additional blocks we want to place through various systems, +// and then sends them to the server to be placed +// Uses chain of responsibility pattern +@OnlyIn(Dist.CLIENT) +public class BuilderChain { + + private final List blocks = new ArrayList<>(); + + public enum State { + IDLE, + PLACING, + BREAKING + } + + private State state = State.IDLE; + + + public void onRightClick() { + if (state == State.BREAKING) { + cancel(); + return; + } + + if (state == State.IDLE) { + state = State.PLACING; + } + + var player = Minecraft.getInstance().player; + var buildMode = ModeSettingsManager.getModeSettings(player).getBuildMode(); + + //Find out if we should place blocks now + if (buildMode.instance.onClick()) { + state = State.IDLE; + PacketHandler.INSTANCE.sendToServer(new ServerPlaceBlocksMessage(blocks)); + } + } + + public void onLeftClick() { + var player = Minecraft.getInstance().player; + + if (state == State.PLACING) { + cancel(); + return; + } + + if (!ReachHelper.canBreakFar(player)) return; + + if (state == State.IDLE){ + state = State.BREAKING; + } + + var buildMode = ModeSettingsManager.getModeSettings(player).getBuildMode(); + + //Find out if we should break blocks now + if (buildMode.instance.onClick()) { + state = State.IDLE; + PacketHandler.INSTANCE.sendToServer(new ServerPlaceBlocksMessage(blocks)); + } + } + + public void onTick() { + blocks.clear(); + + var mc = Minecraft.getInstance(); + var player = mc.player; + var level = mc.level; + + //Check if we have a BlockItem in hand + var itemStack = player.getItemInHand(InteractionHand.MAIN_HAND); + boolean blockInHand = CompatHelper.isItemBlockProxy(itemStack); + +// if (!blockInHand && state == State.PLACING) { +// state = State.IDLE; +// } + + var modifierSettings = ModifierSettingsManager.getModifierSettings(player); + var buildMode = ModeSettingsManager.getModeSettings(player).getBuildMode(); + + + BlockHitResult lookingAt = ClientEvents.getLookingAt(player); + BlockEntry startEntry = findStartPosition(player, lookingAt, modifierSettings.doQuickReplace()); + if (startEntry != null) { + blocks.add(startEntry); + } + + EffortlessBuildingClient.BUILD_MODES.findCoordinates(blocks, player, buildMode); + EffortlessBuildingClient.BUILD_MODIFIERS.findCoordinates(blocks, player, modifierSettings); + + if (state == State.PLACING) { + //TODO find block states + } + } + + public void cancel() { + var player = Minecraft.getInstance().player; + + state = State.IDLE; + EffortlessBuildingClient.BUILD_MODES.onCancel(player); + } + + private BlockEntry findStartPosition(Player player, BlockHitResult lookingAt, boolean doingQuickReplace) { + if (lookingAt == null || lookingAt.getType() == HitResult.Type.MISS) return null; + + var startPos = lookingAt.getBlockPos(); + + //Check if out of reach + int maxReach = ReachHelper.getMaxReach(player); + if (player.blockPosition().distSqr(startPos) > maxReach * maxReach) return null; + + //Offset in direction of sidehit if not quickreplace and not replaceable + boolean replaceable = player.level.getBlockState(startPos).getMaterial().isReplaceable(); + boolean becomesDoubleSlab = SurvivalHelper.doesBecomeDoubleSlab(player, startPos); + if (!doingQuickReplace && !replaceable && !becomesDoubleSlab) { + startPos = startPos.relative(lookingAt.getDirection()); + } + + //Get under tall grass and other replaceable blocks + if (doingQuickReplace && replaceable) { + startPos = startPos.below(); + } + + var blockEntry = new BlockEntry(startPos); + + //Place upside-down stairs if we aim high at block + var hitVec = lookingAt.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) { + blockEntry.mirrorY = true; + } + + return blockEntry; + } + + private void playPlacingSoundIfFurtherThanNormal(Player player, Vec3 location, BlockItem blockItem) { + + if ((location.subtract(player.getEyePosition(1f))).lengthSqr() > 25f) { + BlockPos blockPos = new BlockPos(location); + BlockState state = blockItem.getBlock().defaultBlockState(); + SoundType soundType = state.getBlock().getSoundType(state, player.level, blockPos, player); + player.level.playSound(player, player.blockPosition(), soundType.getPlaceSound(), SoundSource.BLOCKS, + 0.4f, soundType.getPitch()); + } + } + + private void playBreakingSoundIfFurtherThanNormal(Player player, Vec3 location) { + + if ((location.subtract(player.getEyePosition(1f))).lengthSqr() > 25f) { + BlockPos blockPos = new BlockPos(location); + BlockState state = player.level.getBlockState(blockPos); + SoundType soundtype = state.getBlock().getSoundType(state, player.level, blockPos, player); + player.level.playSound(player, player.blockPosition(), soundtype.getBreakSound(), SoundSource.BLOCKS, + 0.4f, soundtype.getPitch()); + } + } + + private void swingHand(Player player, InteractionHand hand) { + player.swing(hand); + } + + public State getState() { + return state; + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/systems/ServerBlockPlacer.java b/src/main/java/nl/requios/effortlessbuilding/systems/ServerBlockPlacer.java index b6e350f..cc591af 100644 --- a/src/main/java/nl/requios/effortlessbuilding/systems/ServerBlockPlacer.java +++ b/src/main/java/nl/requios/effortlessbuilding/systems/ServerBlockPlacer.java @@ -1,6 +1,53 @@ package nl.requios.effortlessbuilding.systems; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import nl.requios.effortlessbuilding.utilities.BlockEntry; + +import java.util.List; + // Receives block placement requests from the client and places them public class ServerBlockPlacer { + public void placeBlocks(Player player, List blocks) { + for (BlockEntry block : blocks) { + placeBlock(player, block); + } + } + + public void placeBlock(Player player, BlockEntry block) { + Level world = player.level; + if (!world.isLoaded(block.blockPos)) return; + + if (block.meansBreakBlock()) { + breakBlock(player, block.blockPos); + } + + boolean success = world.setBlock(block.blockPos, block.blockState, 3); + } + + public void breakBlock(Player player, BlockPos pos) { + ServerLevel world = (ServerLevel) player.level; + if (!world.isLoaded(pos) || world.isEmptyBlock(pos)) return; + + //Held tool + + if (!player.getAbilities().instabuild) { + + //Drops + BlockState blockState = world.getBlockState(pos); + List drops = Block.getDrops(blockState, world, pos, world.getBlockEntity(pos), player, player.getMainHandItem()); + for (ItemStack drop : drops) { + player.addItem(drop); + } + } + + world.removeBlock(pos, false); + } + } diff --git a/src/main/java/nl/requios/effortlessbuilding/utilities/BlockEntry.java b/src/main/java/nl/requios/effortlessbuilding/utilities/BlockEntry.java new file mode 100644 index 0000000..54d2804 --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/utilities/BlockEntry.java @@ -0,0 +1,53 @@ +package nl.requios.effortlessbuilding.utilities; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.BitSet; + +public class BlockEntry { + public final BlockPos blockPos; + public boolean mirrorX; + public boolean mirrorY; + public boolean mirrorZ; + public BlockState blockState; + + public BlockEntry(BlockPos blockPos) { + this.blockPos = blockPos; + } + + public BitSet getMirrorBitSet() { + BitSet bitSet = new BitSet(3); + bitSet.set(0, mirrorX); + bitSet.set(1, mirrorY); + bitSet.set(2, mirrorZ); + return bitSet; + } + + public void setMirrorBitSet(BitSet bitSet) { + mirrorX = bitSet.get(0); + mirrorY = bitSet.get(1); + mirrorZ = bitSet.get(2); + } + + public boolean meansBreakBlock() { + return blockState == null || blockState.isAir(); + } + + public static void encode(FriendlyByteBuf buf, BlockEntry block) { + buf.writeBlockPos(block.blockPos); + buf.writeBitSet(block.getMirrorBitSet()); + buf.writeNbt(NbtUtils.writeBlockState(block.blockState)); + } + + public static BlockEntry decode(FriendlyByteBuf buf) { + BlockEntry block = new BlockEntry(buf.readBlockPos()); + block.setMirrorBitSet(buf.readBitSet()); + block.blockState = NbtUtils.readBlockState(buf.readNbt()); + return block; + } + +}