diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/smartplace/SmartPlaceListener.java b/BauSystem_Main/src/de/steamwar/bausystem/features/smartplace/SmartPlaceListener.java index 75f60d53..a3ca2a66 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/smartplace/SmartPlaceListener.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/smartplace/SmartPlaceListener.java @@ -1,103 +1,202 @@ /* - * This file is a part of the SteamWar software. + * This file is a part of the SteamWar software. * - * Copyright (C) 2021 SteamWar.de-Serverteam + * Copyright (C) 2023 SteamWar.de-Serverteam * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . */ package de.steamwar.bausystem.features.smartplace; +import com.comphenix.tinyprotocol.Reflection; +import com.comphenix.tinyprotocol.TinyProtocol; +import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.configplayer.Config; -import de.steamwar.bausystem.utils.PlaceItemUtils; import de.steamwar.linkage.Linked; import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Material; -import org.bukkit.SoundGroup; +import org.bukkit.World; import org.bukkit.block.Block; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.Repeater; -import org.bukkit.block.data.type.Sign; -import org.bukkit.block.data.type.Switch; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.TileState; +import org.bukkit.block.data.*; +import org.bukkit.block.data.type.*; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; -import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; @Linked public class SmartPlaceListener implements Listener { - @EventHandler(priority = EventPriority.HIGHEST) - public void onPlayerInteract(PlayerInteractEvent event) { - if (!Config.getInstance().get(event.getPlayer()).getPlainValueOrDefault("smartPlace", false)) return; - if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; - if (event.getPlayer().getGameMode() == GameMode.SPECTATOR) return; + // TODO: Sneaking not reset sometimes - boolean shouldRotate = event.getPlayer().isSneaking(); - Material blockType = event.getClickedBlock().getType(); - switch (blockType) { - case REPEATER: - if (shouldRotate && (event.getItem() == null || event.getItem().getType() == Material.REPEATER)) { - Repeater repeater = (Repeater) event.getClickedBlock().getBlockData(); - int i = repeater.getDelay() - 1; - if (i <= 0) i += 4; - repeater.setDelay(i); - event.getClickedBlock().setBlockData(repeater); - event.setCancelled(true); - } - return; - } - BlockData blockData = event.getClickedBlock().getBlockData(); - if (blockData instanceof Switch) { - if (!shouldRotate && (event.getItem() == null || event.getItem().getType() == event.getClickedBlock().getType())) { - return; - } - shouldRotate = false; - } + private static final Set CONTAINERS = new HashSet<>(); - PlaceItemUtils.PlaceItemResult result = PlaceItemUtils.placeItem(event.getPlayer(), event.getItem(), event.getClickedBlock(), event.getBlockFace(), event.getHand(), true, true, shouldRotate, false); - if (result.isSuccess()) { - event.setCancelled(true); - Block block = event.getClickedBlock().getRelative(event.getBlockFace()); - SoundGroup soundGroup = block.getBlockData().getSoundGroup(); - Bukkit.getOnlinePlayers().forEach(player -> { - if (!(event.getClickedBlock().getBlockData() instanceof Sign) && player == event.getPlayer()) { - return; - } - player.playSound(block.getLocation(), soundGroup.getPlaceSound(), soundGroup.getVolume() * 0.8F, soundGroup.getPitch() * 0.8F); - }); - if (result.wasForced()) { - event.getPlayer().playSound(block.getLocation(), soundGroup.getPlaceSound(), soundGroup.getVolume() * 0.8F, soundGroup.getPitch() * 0.8F); + static { + World world = Bukkit.getWorlds().get(0); + Block block = world.getBlockAt(0, 0, 0); + BlockState state = block.getState(); + for (Material material : Material.values()) { + if (material.isLegacy()) continue; + if (!material.isInteractable() && !material.isBlock()) continue; + BlockData blockData = material.createBlockData(); + block.setBlockData(blockData); + if (block.getState() instanceof TileState) { + CONTAINERS.add(material); + } else if (blockData instanceof Stairs) { + CONTAINERS.add(material); } } - // Fix Double sound for original player + CONTAINERS.add(Material.GRINDSTONE); + state.update(true, false); + } + + private static final Class useItem = Reflection.getClass("{nms.network.protocol.game}.PacketPlayInUseItem"); + private static final Set SMART_PLACING = new HashSet<>(); + + public SmartPlaceListener() { + TinyProtocol.instance.addFilter(useItem, (player, object) -> { + if (!Config.getInstance().get(player).getPlainValueOrDefault("smartPlace", false)) { + return object; + } + + Block block = player.getTargetBlockExact(6); + if (block != null && (block.getType().isInteractable() || block.getType() == Material.NOTE_BLOCK) && !CONTAINERS.contains(block.getType())) { + return object; + } + + boolean isSneaking = player.isSneaking(); + if (isSneaking) { + SMART_PLACING.add(player); + } + if (!isSneaking) { + player.setSneaking(true); + } + Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { + player.setSneaking(isSneaking); + SMART_PLACING.remove(player); + }, 0); + return object; + }); } @EventHandler - public void onBlockBreak(BlockBreakEvent event) { - if (!Config.getInstance().get(event.getPlayer()).getPlainValueOrDefault("smartPlace", false)) return; + public void onPlayerInteract(PlayerInteractEvent event) { + if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return; + if (event.getPlayer().getGameMode() == GameMode.SPECTATOR) return; if (!event.getPlayer().isSneaking()) return; - event.setCancelled(true); - event.getBlock().setType(Material.AIR, false); + if (event.getClickedBlock().getType() != Material.REPEATER) return; + if ((event.getItem() == null || event.getItem().getType() == Material.REPEATER)) { + Repeater repeater = (Repeater) event.getClickedBlock().getBlockData(); + int i = repeater.getDelay() - 1; + if (i <= 0) i += 4; + repeater.setDelay(i); + event.getClickedBlock().setBlockData(repeater); + event.setCancelled(true); + } + } - SoundGroup soundGroup = event.getBlock().getBlockData().getSoundGroup(); - Bukkit.getOnlinePlayers().forEach(player -> { - if (player == event.getPlayer()) return; - player.playSound(event.getBlock().getLocation(), soundGroup.getBreakSound(), soundGroup.getVolume() * 0.8F, soundGroup.getPitch() * 0.8F); - }); + @EventHandler + public void onBlockPlace(BlockPlaceEvent event) { + if (!SMART_PLACING.contains(event.getPlayer())) return; + BlockData blockData = event.getBlock().getBlockData(); + if (blockData instanceof Bed) { + Bed bed = (Bed) blockData; + Block bedHead = event.getBlock().getRelative(bed.getFacing()); + bed.setPart(Bed.Part.HEAD); + bed.setFacing(bed.getFacing().getOppositeFace()); + + bed = (Bed) bedHead.getBlockData(); + bed.setPart(Bed.Part.FOOT); + bed.setFacing(bed.getFacing().getOppositeFace()); + bedHead.setBlockData(bed, false); + } else if (blockData instanceof FaceAttachable && ((FaceAttachable) blockData).getAttachedFace() != FaceAttachable.AttachedFace.WALL) { + FaceAttachable faceAttachable = (FaceAttachable) blockData; + faceAttachable.setAttachedFace(faceAttachable.getAttachedFace() == FaceAttachable.AttachedFace.CEILING ? FaceAttachable.AttachedFace.FLOOR : FaceAttachable.AttachedFace.CEILING); + } else if (blockData instanceof Rotatable) { + Rotatable rotatable = (Rotatable) blockData; + rotatable.setRotation(rotatable.getRotation().getOppositeFace()); + } else if (blockData instanceof Directional) { + Directional directional = (Directional) blockData; + BlockFace face = directional.getFacing().getOppositeFace(); + if (directional.getFaces().contains(face)) { + directional.setFacing(face); + } + } else if (blockData instanceof MultipleFacing && !(blockData instanceof Fence)) { + MultipleFacing multipleFacing = (MultipleFacing) blockData; + List blockFaceList = multipleFacing.getFaces() + .stream() + .map(BlockFace::getOppositeFace) + .collect(Collectors.toList()); + if (multipleFacing.getAllowedFaces().containsAll(blockFaceList)) { + multipleFacing.getFaces().forEach(blockFace -> { + multipleFacing.setFace(blockFace, false); + }); + blockFaceList.forEach(blockFace -> { + multipleFacing.setFace(blockFace, true); + }); + } + } + if (blockData instanceof Stairs) { + Stairs stairs = (Stairs) blockData; + switch (stairs.getShape()) { + case OUTER_LEFT: + stairs.setShape(Stairs.Shape.INNER_RIGHT); + break; + case INNER_LEFT: + stairs.setShape(Stairs.Shape.OUTER_RIGHT); + break; + case OUTER_RIGHT: + stairs.setShape(Stairs.Shape.INNER_LEFT); + break; + case INNER_RIGHT: + stairs.setShape(Stairs.Shape.OUTER_LEFT); + break; + case STRAIGHT: + break; + } + } + if (blockData instanceof Piston) { + Piston piston = (Piston) blockData; + Block block = event.getBlock().getRelative(piston.getFacing().getOppositeFace()); + Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { + event.getPlayer().sendBlockChange(block.getLocation(), block.getBlockData()); + }, 2); + } else if (blockData.getMaterial() == Material.REPEATER || blockData.getMaterial() == Material.COMPARATOR) { + Block block = event.getBlock().getRelative(BlockFace.DOWN); + BlockState old = block.getState(); + Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { + block.setType(Material.GLASS); + old.update(true, false); + }, 1); + } + event.getBlock().setBlockData(blockData, true); + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + SMART_PLACING.remove(event.getPlayer()); } }