From bda0908731d983ef881bf42b07769663e9955d8c Mon Sep 17 00:00:00 2001 From: Christian Knaapen Date: Fri, 3 Feb 2023 21:14:29 +0100 Subject: [PATCH] Added server validation: allowInSurvival and whitelist. Instantly placing blocksets of size 1. Added server config and moved some config values around. --- .../effortlessbuilding/ClientConfig.java | 25 +++- .../effortlessbuilding/CommonConfig.java | 60 +++----- .../EffortlessBuilding.java | 1 + .../effortlessbuilding/ServerConfig.java | 55 +++++++ .../render/BlockPreviews.java | 8 +- .../systems/BuilderChain.java | 4 +- .../systems/ServerBlockPlacer.java | 138 ++++-------------- .../effortlessbuilding/systems/UndoRedo.java | 5 +- .../utilities/BlockUtilities.java | 105 +++++++++++++ 9 files changed, 236 insertions(+), 165 deletions(-) create mode 100644 src/main/java/nl/requios/effortlessbuilding/ServerConfig.java diff --git a/src/main/java/nl/requios/effortlessbuilding/ClientConfig.java b/src/main/java/nl/requios/effortlessbuilding/ClientConfig.java index 1dacdd6..8dd432e 100644 --- a/src/main/java/nl/requios/effortlessbuilding/ClientConfig.java +++ b/src/main/java/nl/requios/effortlessbuilding/ClientConfig.java @@ -2,18 +2,22 @@ package nl.requios.effortlessbuilding; import net.minecraftforge.common.ForgeConfigSpec; +import static net.minecraftforge.common.ForgeConfigSpec.*; + public class ClientConfig { - private static final ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder(); + private static final Builder builder = new Builder(); public static final Visuals visuals = new Visuals(builder); public static final ForgeConfigSpec spec = builder.build(); public static class Visuals { - public final ForgeConfigSpec.ConfigValue showBlockPreviews; - public final ForgeConfigSpec.ConfigValue onlyShowBlockPreviewsWhenBuilding; - public final ForgeConfigSpec.ConfigValue maxBlockPreviews; + public final BooleanValue showBlockPreviews; + public final BooleanValue onlyShowBlockPreviewsWhenBuilding; + public final IntValue maxBlockPreviews; + public final IntValue appearAnimationLength; + public final IntValue breakAnimationLength; - public Visuals(ForgeConfigSpec.Builder builder) { + public Visuals(Builder builder) { builder.push("Visuals"); showBlockPreviews = builder @@ -27,8 +31,17 @@ public class ClientConfig { maxBlockPreviews = builder .comment("Don't show block previews when placing more than this many blocks. " + "The outline will always be rendered.") - .define("maxBlockPreviews", 500); + .defineInRange("maxBlockPreviews", 500, 0, 5000); + appearAnimationLength = builder + .comment("How long it takes for a block to appear when placed in ticks.", + "Set to 0 to disable animation.") + .defineInRange("appearAnimationLength", 5, 0, 100); + + breakAnimationLength = builder + .comment("How long the break animation is in ticks.", + "Set to 0 to disable animation.") + .defineInRange("breakAnimationLength", 10, 0, 100); builder.pop(); } diff --git a/src/main/java/nl/requios/effortlessbuilding/CommonConfig.java b/src/main/java/nl/requios/effortlessbuilding/CommonConfig.java index 6bde5f4..0285ed9 100644 --- a/src/main/java/nl/requios/effortlessbuilding/CommonConfig.java +++ b/src/main/java/nl/requios/effortlessbuilding/CommonConfig.java @@ -3,23 +3,24 @@ package nl.requios.effortlessbuilding; import net.minecraftforge.common.ForgeConfigSpec; import nl.requios.effortlessbuilding.create.foundation.render.SuperByteBufferCache; +import static net.minecraftforge.common.ForgeConfigSpec.*; + public class CommonConfig { - private static final ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder(); + private static final Builder builder = new Builder(); public static final Reach reach = new Reach(builder); public static final SurvivalBalancers survivalBalancers = new SurvivalBalancers(builder); - public static final Visuals visuals = new Visuals(builder); public static final ForgeConfigSpec spec = builder.build(); public static class Reach { - public final ForgeConfigSpec.ConfigValue enableReachUpgrades; - public final ForgeConfigSpec.ConfigValue maxReachCreative; - public final ForgeConfigSpec.ConfigValue maxReachLevel0; - public final ForgeConfigSpec.ConfigValue maxReachLevel1; - public final ForgeConfigSpec.ConfigValue maxReachLevel2; - public final ForgeConfigSpec.ConfigValue maxReachLevel3; + public final BooleanValue enableReachUpgrades; + public final IntValue maxReachCreative; + public final IntValue maxReachLevel0; + public final IntValue maxReachLevel1; + public final IntValue maxReachLevel2; + public final IntValue maxReachLevel3; - public Reach(ForgeConfigSpec.Builder builder) { + public Reach(Builder builder) { builder.push("Reach"); enableReachUpgrades = builder .comment("Reach: how far away the player can place blocks using mirror/array etc.", @@ -30,35 +31,34 @@ public class CommonConfig { maxReachCreative = builder .comment("Maximum reach in creative", "Keep in mind that chunks need to be loaded to be able to place blocks inside.") - .define("maxReachCreative", 200); + .defineInRange("maxReachCreative", 200, 0, 1000); maxReachLevel0 = builder .comment("Maximum reach in survival without upgrades", "Reach upgrades are craftable consumables that permanently increase reach.", "Set to 0 to disable Effortless Building until the player has consumed a reach upgrade.") - .define("maxReachLevel0", 20); + .defineInRange("maxReachLevel0", 20, 0, 1000); maxReachLevel1 = builder .comment("Maximum reach in survival with one upgrade") - .define("maxReachLevel1", 50); + .defineInRange("maxReachLevel1", 50, 0, 1000); maxReachLevel2 = builder .comment("Maximum reach in survival with two upgrades") - .define("maxReachLevel2", 100); + .defineInRange("maxReachLevel2", 100, 0, 1000); maxReachLevel3 = builder .comment("Maximum reach in survival with three upgrades") - .define("maxReachLevel3", 200); + .defineInRange("maxReachLevel3", 200, 0, 1000); builder.pop(); } } public static class SurvivalBalancers { - public final ForgeConfigSpec.ConfigValue quickReplaceMiningLevel; - public final ForgeConfigSpec.ConfigValue undoStackSize; + public final IntValue quickReplaceMiningLevel; - public SurvivalBalancers(ForgeConfigSpec.Builder builder) { + public SurvivalBalancers(Builder builder) { builder.push("SurvivalBalancers"); quickReplaceMiningLevel = builder @@ -71,32 +71,6 @@ public class CommonConfig { "4: blocks that can be harvested with netherite tools") .defineInRange("quickReplaceMiningLevel", -1, -1, 3); - undoStackSize = builder - .comment("How many placements are remembered for the undo functionality.") - .worldRestart() - .define("undoStackSize", 50); - - builder.pop(); - } - } - - public static class Visuals { - public final ForgeConfigSpec.ConfigValue appearAnimationLength; - public final ForgeConfigSpec.ConfigValue breakAnimationLength; - - public Visuals(ForgeConfigSpec.Builder builder) { - builder.push("Visuals"); - - appearAnimationLength = builder - .comment("How long it takes for a block to appear when placed in ticks.", - "Set to 0 to disable animation.") - .define("appearAnimationLength", 5); - - breakAnimationLength = builder - .comment("How long the break animation is in ticks.", - "Set to 0 to disable animation.") - .define("breakAnimationLength", 10); - builder.pop(); } } diff --git a/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java b/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java index 42c782d..e509e94 100644 --- a/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java +++ b/src/main/java/nl/requios/effortlessbuilding/EffortlessBuilding.java @@ -74,6 +74,7 @@ public class EffortlessBuilding { //Register config ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, CommonConfig.spec); ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ClientConfig.spec); + ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, ServerConfig.spec); } public static void setup(final FMLCommonSetupEvent event) { diff --git a/src/main/java/nl/requios/effortlessbuilding/ServerConfig.java b/src/main/java/nl/requios/effortlessbuilding/ServerConfig.java new file mode 100644 index 0000000..80eee5e --- /dev/null +++ b/src/main/java/nl/requios/effortlessbuilding/ServerConfig.java @@ -0,0 +1,55 @@ +package nl.requios.effortlessbuilding; + +import net.minecraftforge.common.ForgeConfigSpec; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static net.minecraftforge.common.ForgeConfigSpec.*; + +public class ServerConfig { + private static final Builder builder = new Builder(); + public static final ServerConfig.Validation validation = new ServerConfig.Validation(builder); + public static final ServerConfig.Memory memory = new ServerConfig.Memory(builder); + public static final ForgeConfigSpec spec = builder.build(); + + public static class Validation { + public final BooleanValue allowInSurvival; + public final BooleanValue useWhitelist; + public final ConfigValue> whitelist; + + public Validation(Builder builder) { + builder.push("Validation"); + + allowInSurvival = builder + .comment("Allow use of the mod for players that are in survival mode. Otherwise, only creative mode players can use the mod.") + .define("allowInSurvival", true); + + useWhitelist = builder + .comment("Use a whitelist to determine which players can use the mod. If false, all players can use the mod.") + .define("useWhitelist", false); + + whitelist = builder + .comment("List of player names that can use the mod.") + .defineList("whitelist", Arrays.asList("Player1", "Player2"), o -> true); + + builder.pop(); + } + } + + public static class Memory { + public final IntValue undoStackSize; + + public Memory(Builder builder) { + builder.push("Memory"); + + undoStackSize = builder + .comment("How many placements are remembered for the undo functionality.") + .worldRestart() + .defineInRange("undoStackSize", 50, 10, 200); + + builder.pop(); + } + } +} diff --git a/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviews.java b/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviews.java index ef46124..402ddc0 100644 --- a/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviews.java +++ b/src/main/java/nl/requios/effortlessbuilding/render/BlockPreviews.java @@ -40,7 +40,7 @@ public class BlockPreviews { if (ClientConfig.visuals.showBlockPreviews.get()) { for (PlacedBlocksEntry placed : placedBlocksList) { - int totalTime = placed.breaking ? CommonConfig.visuals.breakAnimationLength.get() : CommonConfig.visuals.appearAnimationLength.get(); + int totalTime = placed.breaking ? ClientConfig.visuals.breakAnimationLength.get() : ClientConfig.visuals.appearAnimationLength.get(); if (totalTime <= 0) continue; float dissolve = (ClientEvents.ticksInGame - placed.time) / (float) totalTime; @@ -50,7 +50,7 @@ public class BlockPreviews { //Expire placedBlocksList.removeIf(placed -> { - int totalTime = placed.breaking ? CommonConfig.visuals.breakAnimationLength.get() : CommonConfig.visuals.appearAnimationLength.get(); + int totalTime = placed.breaking ? ClientConfig.visuals.breakAnimationLength.get() : ClientConfig.visuals.appearAnimationLength.get(); return placed.time + totalTime < ClientEvents.ticksInGame; }); } @@ -228,7 +228,7 @@ public class BlockPreviews { placedBlocksList.add(new PlacedBlocksEntry(ClientEvents.ticksInGame, false, new BlockSet(blocks))); - CreateClient.OUTLINER.keep(blocks.firstPos, CommonConfig.visuals.appearAnimationLength.get()); + CreateClient.OUTLINER.keep(blocks.firstPos, ClientConfig.visuals.appearAnimationLength.get()); } public void onBlocksBroken(BlockSet blocks) { @@ -237,7 +237,7 @@ public class BlockPreviews { placedBlocksList.add(new PlacedBlocksEntry(ClientEvents.ticksInGame, true, new BlockSet(blocks))); - CreateClient.OUTLINER.keep(blocks.firstPos, CommonConfig.visuals.breakAnimationLength.get()); + CreateClient.OUTLINER.keep(blocks.firstPos, ClientConfig.visuals.breakAnimationLength.get()); } private void sortOnDistanceToPlayer(List coordinates, Player player) { diff --git a/src/main/java/nl/requios/effortlessbuilding/systems/BuilderChain.java b/src/main/java/nl/requios/effortlessbuilding/systems/BuilderChain.java index caddbb4..002fea1 100644 --- a/src/main/java/nl/requios/effortlessbuilding/systems/BuilderChain.java +++ b/src/main/java/nl/requios/effortlessbuilding/systems/BuilderChain.java @@ -17,6 +17,7 @@ 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.ClientConfig; import nl.requios.effortlessbuilding.ClientEvents; import nl.requios.effortlessbuilding.CommonConfig; import nl.requios.effortlessbuilding.EffortlessBuildingClient; @@ -87,7 +88,8 @@ public class BuilderChain { player.swing(InteractionHand.MAIN_HAND); blocks.skipFirst = buildMode == BuildModeEnum.DISABLED; - long placeTime = player.level.getGameTime() + CommonConfig.visuals.appearAnimationLength.get() - 3; + long placeTime = player.level.getGameTime(); + if (blocks.size() > 1) placeTime += ClientConfig.visuals.appearAnimationLength.get(); PacketHandler.INSTANCE.sendToServer(new ServerPlaceBlocksPacket(blocks, placeTime)); } } diff --git a/src/main/java/nl/requios/effortlessbuilding/systems/ServerBlockPlacer.java b/src/main/java/nl/requios/effortlessbuilding/systems/ServerBlockPlacer.java index 3258d86..5481ede 100644 --- a/src/main/java/nl/requios/effortlessbuilding/systems/ServerBlockPlacer.java +++ b/src/main/java/nl/requios/effortlessbuilding/systems/ServerBlockPlacer.java @@ -1,26 +1,17 @@ package nl.requios.effortlessbuilding.systems; -import com.google.common.collect.Lists; -import net.minecraft.core.Direction; -import net.minecraft.core.Registry; -import net.minecraft.nbt.CompoundTag; +import net.minecraft.ChatFormatting; 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.ServerConfig; import nl.requios.effortlessbuilding.create.foundation.utility.BlockHelper; import nl.requios.effortlessbuilding.utilities.BlockEntry; import nl.requios.effortlessbuilding.utilities.BlockSet; +import nl.requios.effortlessbuilding.utilities.BlockUtilities; import java.util.*; @@ -31,7 +22,8 @@ public class ServerBlockPlacer { private boolean isPlacingOrBreakingBlocks = false; public void placeBlocksDelayed(Player player, BlockSet blocks, long placeTime) { - + if (!checkAndNotifyAllowedToUseMod(player)) return; + delayedEntries.add(new DelayedEntry(player, blocks, placeTime)); } @@ -46,6 +38,7 @@ public class ServerBlockPlacer { } public void placeBlocks(Player player, BlockSet blocks) { + if (!checkAndNotifyAllowedToUseMod(player)) return; // EffortlessBuilding.log(player, "Placing " + blocks.size() + " blocks"); for (BlockEntry block : blocks) { @@ -54,16 +47,17 @@ public class ServerBlockPlacer { } } - public void placeBlock(Player player, BlockEntry block) { + private void placeBlock(Player player, BlockEntry block) { Level world = player.level; if (!world.isLoaded(block.blockPos)) return; isPlacingOrBreakingBlocks = true; - boolean placedBlock = onPlaceItemIntoWorld(player, block) == InteractionResult.SUCCESS; + boolean placedBlock = BlockUtilities.placeBlockEntry(player, block) == InteractionResult.SUCCESS; isPlacingOrBreakingBlocks = false; } public void breakBlocks(Player player, BlockSet blocks) { + if (!checkAndNotifyAllowedToUseMod(player)) return; // EffortlessBuilding.log(player, "Breaking " + blocks.size() + " blocks"); for (BlockEntry block : blocks) { @@ -72,7 +66,7 @@ public class ServerBlockPlacer { } } - public void breakBlock(Player player, BlockEntry block) { + private void breakBlock(Player player, BlockEntry block) { ServerLevel world = (ServerLevel) player.level; if (!world.isLoaded(block.blockPos) || world.isEmptyBlock(block.blockPos)) return; @@ -84,6 +78,24 @@ public class ServerBlockPlacer { }); isPlacingOrBreakingBlocks = false; } + + public boolean checkAndNotifyAllowedToUseMod(Player player) { + if (!isAllowedToUseMod(player)) { + EffortlessBuilding.log(player, ChatFormatting.RED + "You are not allowed to use Effortless Building."); + return false; + } + return true; + } + + public boolean isAllowedToUseMod(Player player) { + if (!ServerConfig.validation.allowInSurvival.get() && !player.isCreative()) return false; + + if (ServerConfig.validation.useWhitelist.get()) { + return ServerConfig.validation.whitelist.get().contains(player.getGameProfile().getName()); + } + + return true; + } public Set getDelayedEntries() { return delayedEntriesView; @@ -95,96 +107,4 @@ public class ServerBlockPlacer { public record DelayedEntry(Player player, BlockSet blocks, long placeTime) {} - //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 blockSnapshots = (List)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; - } } diff --git a/src/main/java/nl/requios/effortlessbuilding/systems/UndoRedo.java b/src/main/java/nl/requios/effortlessbuilding/systems/UndoRedo.java index 593cd5f..baee0e8 100644 --- a/src/main/java/nl/requios/effortlessbuilding/systems/UndoRedo.java +++ b/src/main/java/nl/requios/effortlessbuilding/systems/UndoRedo.java @@ -10,6 +10,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import nl.requios.effortlessbuilding.CommonConfig; import nl.requios.effortlessbuilding.EffortlessBuilding; +import nl.requios.effortlessbuilding.ServerConfig; import nl.requios.effortlessbuilding.utilities.UndoRedoBlockSet; import nl.requios.effortlessbuilding.utilities.FixedStack; import nl.requios.effortlessbuilding.utilities.InventoryHelper; @@ -49,7 +50,7 @@ public class UndoRedo { //If no stack exists, make one if (!undoStacks.containsKey(player.getUUID())) { - undoStacks.put(player.getUUID(), new FixedStack<>(new UndoRedoBlockSet[CommonConfig.survivalBalancers.undoStackSize.get()])); + undoStacks.put(player.getUUID(), new FixedStack<>(new UndoRedoBlockSet[ServerConfig.memory.undoStackSize.get()])); } undoStacks.get(player.getUUID()).push(blockSet); @@ -62,7 +63,7 @@ public class UndoRedo { //If no stack exists, make one if (!redoStacks.containsKey(player.getUUID())) { - redoStacks.put(player.getUUID(), new FixedStack<>(new UndoRedoBlockSet[CommonConfig.survivalBalancers.undoStackSize.get()])); + redoStacks.put(player.getUUID(), new FixedStack<>(new UndoRedoBlockSet[ServerConfig.memory.undoStackSize.get()])); } redoStacks.get(player.getUUID()).push(blockSet); diff --git a/src/main/java/nl/requios/effortlessbuilding/utilities/BlockUtilities.java b/src/main/java/nl/requios/effortlessbuilding/utilities/BlockUtilities.java index 598a4fd..555c1c7 100644 --- a/src/main/java/nl/requios/effortlessbuilding/utilities/BlockUtilities.java +++ b/src/main/java/nl/requios/effortlessbuilding/utilities/BlockUtilities.java @@ -1,22 +1,35 @@ package nl.requios.effortlessbuilding.utilities; +import com.google.common.collect.Lists; import net.minecraft.client.Minecraft; import net.minecraft.core.Direction; +import net.minecraft.core.Registry; +import net.minecraft.nbt.CompoundTag; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundSource; +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.item.context.BlockPlaceContext; import net.minecraft.world.level.ClipContext; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.pattern.BlockInWorld; import net.minecraft.world.level.block.state.properties.Half; import net.minecraft.world.level.block.state.properties.SlabType; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; +import net.minecraftforge.common.util.BlockSnapshot; +import net.minecraftforge.event.ForgeEventFactory; +import nl.requios.effortlessbuilding.create.foundation.utility.BlockHelper; + +import java.util.List; //Common public class BlockUtilities { @@ -47,6 +60,98 @@ public class BlockUtilities { return result; } + //ForgeHooks::onPlaceItemIntoWorld + public static InteractionResult placeBlockEntry(Player player, BlockEntry block) { + ItemStack itemstack = block.itemStack; + Level level = player.level; + + if (player != null && !player.getAbilities().mayBuild && !itemstack.hasAdventureModePlaceTagForBlock(level.registryAccess().registryOrThrow(Registry.BLOCK_REGISTRY), new BlockInWorld(level, block.blockPos, false))) + return InteractionResult.PASS; + + // handle all placement events here + Item item = itemstack.getItem(); + int size = itemstack.getCount(); + CompoundTag nbt = null; + if (itemstack.getTag() != null) + nbt = itemstack.getTag().copy(); + + if (!(itemstack.getItem() instanceof BucketItem)) // if not bucket + level.captureBlockSnapshots = true; + + ItemStack copy = itemstack.copy(); + //// + BlockHelper.placeSchematicBlock(level, player, block.newBlockState, block.blockPos, block.itemStack, null); + //// + InteractionResult ret = InteractionResult.SUCCESS; + if (itemstack.isEmpty()) + ForgeEventFactory.onPlayerDestroyItem(player, copy, InteractionHand.MAIN_HAND); + + level.captureBlockSnapshots = false; + + if (ret.consumesAction()) + { + // save new item data + int newSize = itemstack.getCount(); + CompoundTag newNBT = null; + if (itemstack.getTag() != null) + { + newNBT = itemstack.getTag().copy(); + } + @SuppressWarnings("unchecked") + List blockSnapshots = (List)level.capturedBlockSnapshots.clone(); + level.capturedBlockSnapshots.clear(); + + // make sure to set pre-placement item data for event + itemstack.setCount(size); + itemstack.setTag(nbt); + + Direction side = Direction.UP; + + boolean eventResult = false; + if (blockSnapshots.size() > 1) + { + eventResult = ForgeEventFactory.onMultiBlockPlace(player, blockSnapshots, side); + } + else if (blockSnapshots.size() == 1) + { + eventResult = ForgeEventFactory.onBlockPlace(player, blockSnapshots.get(0), side); + } + + if (eventResult) + { + ret = InteractionResult.FAIL; // cancel placement + // revert back all captured blocks + for (BlockSnapshot blocksnapshot : Lists.reverse(blockSnapshots)) + { + level.restoringBlockSnapshots = true; + blocksnapshot.restore(true, false); + level.restoringBlockSnapshots = false; + } + } + else + { + // Change the stack to its new content + itemstack.setCount(newSize); + itemstack.setTag(newNBT); + + for (BlockSnapshot snap : blockSnapshots) + { + int updateFlag = snap.getFlag(); + BlockState oldBlock = snap.getReplacedBlock(); + BlockState newBlock = level.getBlockState(snap.getPos()); + newBlock.onPlace(level, snap.getPos(), oldBlock, false); + + level.markAndNotifyBlock(snap.getPos(), level.getChunkAt(snap.getPos()), oldBlock, newBlock, updateFlag, 512); + } + if (player != null) + player.awardStat(Stats.ITEM_USED.get(item)); + } + } + level.capturedBlockSnapshots.clear(); + + return ret; + } + public static void playSoundIfFurtherThanNormal(Player player, BlockEntry blockEntry, boolean breaking) { if (Minecraft.getInstance().hitResult != null && Minecraft.getInstance().hitResult.getType() == HitResult.Type.BLOCK)