diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCursor.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCursor.java index c557f95f..8f841721 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCursor.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCursor.java @@ -19,7 +19,6 @@ package de.steamwar.bausystem.features.simulator; -import com.comphenix.tinyprotocol.TinyProtocol; import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.Permission; import de.steamwar.bausystem.SWUtils; @@ -38,63 +37,56 @@ import de.steamwar.bausystem.features.simulator.gui.SimulatorGui; import de.steamwar.bausystem.features.simulator.gui.base.SimulatorBaseGui; import de.steamwar.bausystem.utils.BauMemberUpdateEvent; import de.steamwar.bausystem.utils.ItemUtils; -import de.steamwar.bausystem.utils.RayTraceUtils; +import de.steamwar.core.SWPlayer; +import de.steamwar.cursor.Cursor; import de.steamwar.entity.REntity; import de.steamwar.entity.REntityServer; -import de.steamwar.entity.RFallingBlockEntity; import de.steamwar.inventory.SWAnvilInv; import de.steamwar.linkage.Linked; import lombok.AllArgsConstructor; import lombok.Getter; -import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.block.BlockFace; 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.player.*; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerItemHeldEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerToggleSneakEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; -import java.util.*; -import java.util.function.BiFunction; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.function.Function; import java.util.stream.Collectors; @Linked public class SimulatorCursor implements Listener { - private static final World WORLD = Bukkit.getWorlds().get(0); - - private static Map cursorType = Collections.synchronizedMap(new HashMap<>()); - private static Map cursors = Collections.synchronizedMap(new HashMap<>()); - private static final Set calculating = new HashSet<>(); + private static final Map cursorType = Collections.synchronizedMap(new HashMap<>()); + private static final Map emptyTargetServers = Collections.synchronizedMap(new HashMap<>()); public static boolean isSimulatorItem(ItemStack itemStack) { return ItemUtils.isItem(itemStack, "simulator"); } - public SimulatorCursor() { - BiFunction function = (player, object) -> { - calcCursor(player); - return object; - }; - TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Pos.class, function); - TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Rot.class, function); - TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.PosRot.class, function); + private static boolean hasSimulatorItem(Player player) { + return isSimulatorItem(player.getInventory().getItemInMainHand()) || isSimulatorItem(player.getInventory().getItemInOffHand()); } @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { if (!Permission.BUILD.hasPermission(event.getPlayer())) return; - Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { - calcCursor(event.getPlayer()); - }, 0); + Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> calcCursor(event.getPlayer()), 0); } @EventHandler @@ -106,9 +98,7 @@ public class SimulatorCursor implements Listener { @EventHandler public void onPlayerItemHeld(PlayerItemHeldEvent event) { if (!Permission.BUILD.hasPermission(event.getPlayer())) return; - Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { - calcCursor(event.getPlayer()); - }, 1); + Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> calcCursor(event.getPlayer()), 1); } @EventHandler @@ -119,10 +109,7 @@ public class SimulatorCursor implements Listener { @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { cursorType.remove(event.getPlayer()); - cursors.remove(event.getPlayer()); - synchronized (calculating) { - calculating.remove(event.getPlayer()); - } + removeCursor(event.getPlayer()); } private static final Map LAST_SNEAKS = new HashMap<>(); @@ -138,7 +125,7 @@ public class SimulatorCursor implements Listener { public void onPlayerToggleSneak(PlayerToggleSneakEvent event) { if (!event.isSneaking()) return; Player player = event.getPlayer(); - if (!isSimulatorItem(player.getInventory().getItemInMainHand()) && !isSimulatorItem(player.getInventory().getItemInOffHand())) { + if (!hasSimulatorItem(player)) { return; } if (LAST_SNEAKS.containsKey(player)) { @@ -164,17 +151,10 @@ public class SimulatorCursor implements Listener { } public static void calcCursor(Player player) { - synchronized (calculating) { - if (calculating.contains(player)) return; - calculating.add(player); - } - if (!Permission.BUILD.hasPermission(player) || (!isSimulatorItem(player.getInventory().getItemInMainHand()) && !isSimulatorItem(player.getInventory().getItemInOffHand()))) { + if (!Permission.BUILD.hasPermission(player) || !hasSimulatorItem(player)) { if (removeCursor(player) || SimulatorWatcher.show(null, player)) { SWUtils.sendToActionbar(player, ""); } - synchronized (calculating) { - calculating.remove(player); - } return; } @@ -183,203 +163,98 @@ public class SimulatorCursor implements Listener { removeCursor(player); SimulatorWatcher.show(null, player); SWUtils.sendToActionbar(player, "§cGenerating Stab"); - synchronized (calculating) { - calculating.remove(player); - } return; } SimulatorWatcher.show(simulator, player); - List entities = SimulatorWatcher.getEntitiesOfSimulator(simulator); - RayTraceUtils.RRayTraceResult rayTraceResult = RayTraceUtils.traceREntity(player, player.getLocation(), entities); - if (rayTraceResult == null) { - removeCursor(player); - if (simulator == null) { - SWUtils.sendToActionbar(player, "§eSelect Simulator"); - } else { - SWUtils.sendToActionbar(player, "§eOpen Simulator"); - } - synchronized (calculating) { - calculating.remove(player); - } - return; + Cursor cursor = getOrCreateCursor(player, simulator, cursorType.getOrDefault(player, CursorType.TNT)); + cursor.renderDeduplicated(); + } + + private static Cursor getOrCreateCursor(Player player, Simulator simulator, CursorType type) { + REntityServer targetServer = simulator == null ? emptyTargetServers.computeIfAbsent(player, __ -> new REntityServer()) : SimulatorWatcher.getEntityServerOfSimulator(simulator); + SWPlayer swPlayer = SWPlayer.of(player); + Optional activeCursor = swPlayer.getComponent(Cursor.class); + + Cursor cursor = activeCursor.orElse(null); + if (cursor == null || cursor.getTargetServer() != targetServer) { + swPlayer.removeComponent(Cursor.class); + cursor = new Cursor( + targetServer, + player, + Material.GLASS, + type.material, + type.cursorModes, + (location, hitEntity, action) -> handlePlayerClick(player, location, hitEntity, action), + (location, hitEntity) -> sendCursorActionbar(player, SimulatorStorage.getSimulator(player), location != null, hitEntity.isPresent()) + ); + } else { + cursor.setCursorMaterial(type.material); + cursor.setAllowedCursorModes(type.cursorModes); } - showCursor(player, rayTraceResult, simulator != null); - synchronized (calculating) { - calculating.remove(player); - } + return cursor; } private static synchronized boolean removeCursor(Player player) { - REntityServer entityServer = cursors.get(player); - boolean hadCursor = entityServer != null && !entityServer.getEntities().isEmpty(); - if (entityServer != null) { - entityServer.getEntities().forEach(REntity::die); + Optional cursor = SWPlayer.of(player).getComponent(Cursor.class); + cursor.ifPresent(__ -> SWPlayer.of(player).removeComponent(Cursor.class)); + REntityServer emptyTargetServer = emptyTargetServers.remove(player); + if (emptyTargetServer != null) { + emptyTargetServer.close(); } - return hadCursor; + return cursor.isPresent(); } - private static synchronized void showCursor(Player player, RayTraceUtils.RRayTraceResult rayTraceResult, boolean hasSimulatorSelected) { - REntityServer entityServer = cursors.computeIfAbsent(player, __ -> { - REntityServer rEntityServer = new REntityServer(); - rEntityServer.addPlayer(player); - return rEntityServer; - }); - - CursorType type = cursorType.getOrDefault(player, CursorType.TNT); - REntity hitEntity = rayTraceResult.getHitEntity(); - Location location = hitEntity != null ? new Vector(hitEntity.getX(), hitEntity.getY(), hitEntity.getZ()).toLocation(WORLD) : - type.position.apply(player, rayTraceResult).toLocation(WORLD); - - Material material = hitEntity != null ? Material.GLASS : type.getMaterial(); - List entities = entityServer.getEntitiesByType(RFallingBlockEntity.class); - entities.removeIf(rFallingBlockEntity -> { - if (rFallingBlockEntity.getMaterial() != material) { - rFallingBlockEntity.die(); - return true; - } - rFallingBlockEntity.move(location); - return false; - }); - if (entities.isEmpty()) { - RFallingBlockEntity rFallingBlockEntity = new RFallingBlockEntity(entityServer, location, material); - rFallingBlockEntity.setNoGravity(true); - if (material == Material.GLASS) { - rFallingBlockEntity.setGlowing(true); - } - } - - if (hasSimulatorSelected) { - if (hitEntity != null) { - SWUtils.sendToActionbar(player, "§eEdit Position"); - } else { - SWUtils.sendToActionbar(player, "§eAdd new " + type.name); - } - } else { + private static void sendCursorActionbar(Player player, Simulator simulator, boolean hasCursorLocation, boolean hasHitEntity) { + if (!hasCursorLocation) { + SWUtils.sendToActionbar(player, simulator == null ? "§eSelect Simulator" : "§eOpen Simulator"); + } else if (simulator == null) { SWUtils.sendToActionbar(player, "§eCreate new Simulator"); - } - } - - public static Vector getPosFree(Player player, RayTraceUtils.RRayTraceResult result) { - Vector pos = result.getHitPosition(); - - BlockFace face = result.getHitBlockFace(); - if (face != null) { - switch (face) { - case DOWN: - pos.setY(pos.getY() - 0.98); - break; - case EAST: - pos.setX(pos.getX() + 0.49); - break; - case WEST: - pos.setX(pos.getX() - 0.49); - break; - case NORTH: - pos.setZ(pos.getZ() - 0.49); - break; - case SOUTH: - pos.setZ(pos.getZ() + 0.49); - break; - default: - break; - } - - if (face.getModY() == 0 && player.isSneaking()) { - pos.setY(pos.getY() - 0.49); - } - } - - if (!player.isSneaking()) { - pos.setX(pos.getBlockX() + 0.5); - if (face == null || face.getModY() == 0) { - pos.setY(pos.getBlockY() + 0.0); - } - pos.setZ(pos.getBlockZ() + 0.5); - } - - return pos; - } - - private static Vector getPosBlockAligned(Player player, RayTraceUtils.RRayTraceResult result) { - Vector pos = result.getHitPosition(); - - BlockFace face = result.getHitBlockFace(); - if (face != null) { - switch (face) { - case DOWN: - pos.setY(pos.getY() - 0.98); - break; - case EAST: - pos.setX(pos.getX() + 0.49); - break; - case WEST: - pos.setX(pos.getX() - 0.49); - break; - case NORTH: - pos.setZ(pos.getZ() - 0.49); - break; - case SOUTH: - pos.setZ(pos.getZ() + 0.49); - break; - default: - break; - } - } - - pos.setX(pos.getBlockX() + 0.5); - if (pos.getY() - pos.getBlockY() != 0 && face == BlockFace.UP) { - pos.setY(pos.getBlockY() + 1.0); + } else if (hasHitEntity) { + SWUtils.sendToActionbar(player, "§eEdit Position"); } else { - pos.setY(pos.getBlockY()); + SWUtils.sendToActionbar(player, "§eAdd new " + cursorType.getOrDefault(player, CursorType.TNT).name); } - pos.setZ(pos.getBlockZ() + 0.5); - return pos; } @Getter @AllArgsConstructor public enum CursorType { - TNT(Material.TNT, Material.GUNPOWDER, SimulatorCursor::getPosFree, "TNT", vector -> new TNTElement(vector).add(new TNTPhase())), - REDSTONE_BLOCK(Material.REDSTONE_BLOCK, Material.REDSTONE, SimulatorCursor::getPosBlockAligned, "Redstone Block", vector -> new RedstoneElement(vector).add(new RedstonePhase())), - OBSERVER(Material.OBSERVER, Material.QUARTZ, SimulatorCursor::getPosBlockAligned, "Observer", vector -> new ObserverElement(vector).add(new ObserverPhase())), + TNT(Material.TNT, Material.GUNPOWDER, List.of(Cursor.CursorMode.FREE), "TNT", vector -> new TNTElement(vector).add(new TNTPhase())), + REDSTONE_BLOCK(Material.REDSTONE_BLOCK, Material.REDSTONE, List.of(Cursor.CursorMode.BLOCK_ALIGNED), "Redstone Block", vector -> new RedstoneElement(vector).add(new RedstonePhase())), + OBSERVER(Material.OBSERVER, Material.QUARTZ, List.of(Cursor.CursorMode.BLOCK_ALIGNED), "Observer", vector -> new ObserverElement(vector).add(new ObserverPhase())), ; public final Material material; public final Material nonSelectedMaterial; - public final BiFunction position; + public final List cursorModes; public final String name; public final Function> elementFunction; } - @EventHandler - public void onPlayerInteract(PlayerInteractEvent event) { - if (!Permission.BUILD.hasPermission(event.getPlayer())) return; - if (!ItemUtils.isItem(event.getItem(), "simulator")) { + private static void handlePlayerClick(Player player, Location cursorLocation, Optional hitEntity, Action action) { + if (!Permission.BUILD.hasPermission(player)) return; + if (!hasSimulatorItem(player)) { return; } - event.setCancelled(true); - Player player = event.getPlayer(); Simulator simulator = SimulatorStorage.getSimulator(player); - if (event.getAction() == Action.LEFT_CLICK_BLOCK || event.getAction() == Action.LEFT_CLICK_AIR) { + if (action == Action.LEFT_CLICK_BLOCK || action == Action.LEFT_CLICK_AIR) { if (simulator == null) { return; } - SimulatorExecutor.run(event.getPlayer(), simulator, null); + SimulatorExecutor.run(player, simulator, null); return; } - if (event.getAction() != Action.RIGHT_CLICK_BLOCK && event.getAction() != Action.RIGHT_CLICK_AIR) { + if (action != Action.RIGHT_CLICK_BLOCK && action != Action.RIGHT_CLICK_AIR) { return; } - - RayTraceUtils.RRayTraceResult rayTraceResult = RayTraceUtils.traceREntity(player, player.getLocation(), SimulatorWatcher.getEntitiesOfSimulator(simulator)); if (simulator == null) { - if (rayTraceResult == null) { + if (cursorLocation == null) { SimulatorStorage.openSimulatorSelector(player); } else { SWAnvilInv anvilInv = new SWAnvilInv(player, "Name"); @@ -395,7 +270,7 @@ public class SimulatorCursor implements Listener { } sim = new Simulator(s); SimulatorStorage.addSimulator(s, sim); - createElement(player, rayTraceResult, sim); + createElement(player, cursorLocation, sim); SimulatorStorage.setSimulator(player, sim); }); anvilInv.open(); @@ -403,57 +278,56 @@ public class SimulatorCursor implements Listener { return; } - if (rayTraceResult == null) { + if (cursorLocation == null) { new SimulatorGui(player, simulator).open(); return; } - if (rayTraceResult.getHitEntity() != null) { - REntity hitEntity = rayTraceResult.getHitEntity(); - Vector vector = new Vector(hitEntity.getX(), hitEntity.getY(), hitEntity.getZ()); - List> elements = simulator.getGroups().stream().map(SimulatorGroup::getElements).flatMap(List::stream).filter(element -> { - return element.getWorldPos().distanceSquared(vector) < (1 / 16.0) * (1 / 16.0); - }).collect(Collectors.toList()); - - switch (elements.size()) { - case 0: - return; - case 1: - // Open single element present in Simulator - SimulatorElement element = elements.get(0); - SimulatorGroup group1 = element.getGroup(simulator); - SimulatorBaseGui back = new SimulatorGui(player, simulator); - if (group1.getElements().size() > 1) { - back = new SimulatorGroupGui(player, simulator, group1, back); - } - element.open(player, simulator, group1, back); - break; - default: - List parents = elements.stream().map(e -> e.getGroup(simulator)).distinct().collect(Collectors.toList()); - if (parents.size() == 1) { - // Open multi element present in Simulator in existing group - SimulatorGui simulatorGui = new SimulatorGui(player, simulator); - new SimulatorGroupGui(player, simulator, parents.get(0), simulatorGui).open(); - } else { - // Open multi element present in Simulator in implicit group - SimulatorGroup group2 = new SimulatorGroup(); - group2.setMaterial(null); - group2.getElements().addAll(elements); - SimulatorGui simulatorGui = new SimulatorGui(player, simulator); - new SimulatorGroupGui(player, simulator, group2, simulatorGui).open(); - } - break; - } + if (hitEntity.isPresent()) { + openElement(player, simulator, hitEntity.get()); return; } - // Add new Element to current simulator - createElement(player, rayTraceResult, simulator); + createElement(player, cursorLocation, simulator); } - private void createElement(Player player, RayTraceUtils.RRayTraceResult rayTraceResult, Simulator simulator) { + private static void openElement(Player player, Simulator simulator, REntity hitEntity) { + Vector vector = new Vector(hitEntity.getX(), hitEntity.getY(), hitEntity.getZ()); + List> elements = simulator.getGroups().stream().map(SimulatorGroup::getElements).flatMap(List::stream).filter(element -> { + return element.getWorldPos().distanceSquared(vector) < (1 / 16.0) * (1 / 16.0); + }).collect(Collectors.toList()); + + switch (elements.size()) { + case 0: + return; + case 1: + SimulatorElement element = elements.get(0); + SimulatorGroup group1 = element.getGroup(simulator); + SimulatorBaseGui back = new SimulatorGui(player, simulator); + if (group1.getElements().size() > 1) { + back = new SimulatorGroupGui(player, simulator, group1, back); + } + element.open(player, simulator, group1, back); + break; + default: + List parents = elements.stream().map(e -> e.getGroup(simulator)).distinct().collect(Collectors.toList()); + if (parents.size() == 1) { + SimulatorGui simulatorGui = new SimulatorGui(player, simulator); + new SimulatorGroupGui(player, simulator, parents.get(0), simulatorGui).open(); + } else { + SimulatorGroup group2 = new SimulatorGroup(); + group2.setMaterial(null); + group2.getElements().addAll(elements); + SimulatorGui simulatorGui = new SimulatorGui(player, simulator); + new SimulatorGroupGui(player, simulator, group2, simulatorGui).open(); + } + break; + } + } + + private static void createElement(Player player, Location cursorLocation, Simulator simulator) { CursorType type = cursorType.getOrDefault(player, CursorType.TNT); - Vector vector = type.position.apply(player, rayTraceResult); + Vector vector = cursorLocation.toVector(); if (type == CursorType.REDSTONE_BLOCK) { vector.subtract(new Vector(0.5, 0, 0.5)); } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorWatcher.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorWatcher.java index 3c8ff96f..800c0c73 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorWatcher.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorWatcher.java @@ -124,4 +124,11 @@ public class SimulatorWatcher { } return entityServer.getEntities(); } + + synchronized REntityServer getEntityServerOfSimulator(Simulator simulator) { + if (simulator == null) { + return null; + } + return entityServers.computeIfAbsent(simulator, __ -> createSim(new REntityServer(), simulator)); + } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/Cursor.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/Cursor.java new file mode 100644 index 00000000..86973cc5 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/Cursor.java @@ -0,0 +1,205 @@ +package de.steamwar.cursor; + +import de.steamwar.core.SWPlayer; +import de.steamwar.entity.REntity; +import de.steamwar.entity.REntityServer; +import de.steamwar.entity.RFallingBlockEntity; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import org.apache.logging.log4j.util.TriConsumer; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.event.block.Action; +import org.bukkit.util.Vector; + +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Predicate; + +@Getter +public class Cursor implements SWPlayer.Component { + private final World WORLD = Bukkit.getWorlds().get(0); + + private final REntityServer targetServer; + private final Player owner; + + private final AtomicBoolean isRendering = new AtomicBoolean(false); + + private RFallingBlockEntity cursorEntity; + private final REntityServer cursorServer; + private Location cursorLocation; + private REntity hitEntity; + + @Setter + private Material cursorMaterial; + @Setter + private List allowedCursorModes; + private final Material highlightMaterial; + private final TriConsumer, Action> onClick; + private final BiConsumer> onRender; + + + public Cursor(REntityServer targetServer, Player owner, Material highlightMaterial, Material cursorMaterial, List allowedModes, TriConsumer, Action> onClick) { + this(targetServer, owner, highlightMaterial, cursorMaterial, allowedModes, onClick, (location, hitEntity) -> { + }); + } + + public Cursor(REntityServer targetServer, Player owner, Material highlightMaterial, Material cursorMaterial, List allowedModes, TriConsumer, Action> onClick, BiConsumer> onRender) { + this.targetServer = targetServer; + this.owner = owner; + this.highlightMaterial = highlightMaterial; + this.cursorMaterial = cursorMaterial; + this.allowedCursorModes = allowedModes; + this.onClick = onClick; + this.onRender = onRender; + + cursorServer = new REntityServer(); + cursorServer.addPlayer(owner); + + SWPlayer.of(owner).setComponent(this); + } + + public void renderDeduplicated() { + if (!isRendering.getAndSet(true)) { + render(); + isRendering.set(false); + } + } + + private void render() { + RayTraceUtils.RRayTraceResult rayTraceResult = RayTraceUtils.traceREntity(owner, owner.getLocation(), targetServer.getEntities()); + if (rayTraceResult == null) { + if (cursorEntity != null) + cursorEntity.die(); + cursorEntity = null; + cursorLocation = null; + hitEntity = null; + onRender.accept(null, Optional.empty()); + return; + } + + REntity hitEntity = rayTraceResult.getHitEntity() == cursorEntity ? null : rayTraceResult.getHitEntity(); + + + Material activeCursorMaterial = hitEntity == null ? cursorMaterial : highlightMaterial; + CursorMode activeCursorMode = allowedCursorModes.stream().filter((mode) -> mode.isActive.test(owner)).min(Comparator.comparingInt(a -> a.priority)) + .orElse(CursorMode.BLOCK_ALIGNED); + + Location activeCursorLocation = hitEntity == null ? activeCursorMode.positionTransform.apply(owner, rayTraceResult).toLocation(WORLD) + : new Vector(hitEntity.getX(), hitEntity.getY(), hitEntity.getZ()).toLocation(WORLD); + + cursorLocation = activeCursorLocation; + this.hitEntity = hitEntity; + + if (cursorEntity == null) { + cursorEntity = new RFallingBlockEntity(cursorServer, activeCursorLocation, activeCursorMaterial); + cursorEntity.setNoGravity(true); + } else if (cursorEntity.getMaterial() == activeCursorMaterial) { + cursorEntity.move(activeCursorLocation); + } else { + cursorEntity.die(); + cursorEntity = new RFallingBlockEntity(cursorServer, activeCursorLocation, activeCursorMaterial); + cursorEntity.setNoGravity(true); + if (activeCursorMaterial == highlightMaterial) { + cursorEntity.setGlowing(true); + } + } + onRender.accept(cursorLocation, Optional.ofNullable(hitEntity)); + } + + protected void handlePlayerClick(Action clickAction) { + renderDeduplicated(); + onClick.accept(this.cursorLocation, Optional.ofNullable(this.hitEntity), clickAction); + } + + @Override + public void onUnmount(SWPlayer player) { + cursorServer.close(); + } + + @AllArgsConstructor + public enum CursorMode { + FREE(1, (player, rayTraceResult) -> { + Vector pos = rayTraceResult.getHitPosition(); + + BlockFace face = rayTraceResult.getHitBlockFace(); + if (face != null) { + switch (face) { + case DOWN: + pos.setY(pos.getY() - 0.98); + break; + case EAST: + pos.setX(pos.getX() + 0.49); + break; + case WEST: + pos.setX(pos.getX() - 0.49); + break; + case NORTH: + pos.setZ(pos.getZ() - 0.49); + break; + case SOUTH: + pos.setZ(pos.getZ() + 0.49); + break; + default: + break; + } + + if (face.getModY() == 0 && player.isSneaking()) { + pos.setY(pos.getY() - 0.49); + } + } + + return pos; + }, Player::isSneaking), + + BLOCK_ALIGNED(0, (player, rayTraceResult) -> { + Vector pos = rayTraceResult.getHitPosition(); + + BlockFace face = rayTraceResult.getHitBlockFace(); + if (face != null) { + switch (face) { + case DOWN: + pos.setY(pos.getY() - 0.98); + break; + case EAST: + pos.setX(pos.getX() + 0.49); + break; + case WEST: + pos.setX(pos.getX() - 0.49); + break; + case NORTH: + pos.setZ(pos.getZ() - 0.49); + break; + case SOUTH: + pos.setZ(pos.getZ() + 0.49); + break; + default: + break; + } + } + + pos.setX(pos.getBlockX() + 0.5); + if (pos.getY() - pos.getBlockY() != 0 && face == BlockFace.UP) { + pos.setY(pos.getBlockY() + 1.0); + } else { + pos.setY(pos.getBlockY()); + } + pos.setZ(pos.getBlockZ() + 0.5); + return pos; + }, (player) -> true); + + + private final int priority; + private final BiFunction positionTransform; + private final Predicate isActive; + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/CursorListener.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/CursorListener.java new file mode 100644 index 00000000..47f0a91d --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/CursorListener.java @@ -0,0 +1,48 @@ +package de.steamwar.cursor; + +import com.comphenix.tinyprotocol.TinyProtocol; +import de.steamwar.core.SWPlayer; +import de.steamwar.linkage.Linked; +import lombok.Getter; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerInteractEvent; + +import java.util.*; + + +@Linked +public class CursorListener implements Listener { + @Getter + private static CursorListener instance; + + public CursorListener() { + if (instance == null) { + instance = this; + } + + TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Pos.class, this::updateCursorFromPacket); + TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Rot.class, this::updateCursorFromPacket); + TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.PosRot.class, this::updateCursorFromPacket); + } + + public Packet updateCursorFromPacket(Player player, Packet packet) { + SWPlayer swPlayer = SWPlayer.of(player); + Optional activeCursor = swPlayer.getComponent(Cursor.class); + + activeCursor.ifPresent(Cursor::renderDeduplicated); + + return packet; + } + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent event) { + SWPlayer.of(event.getPlayer()).getComponent(Cursor.class).ifPresent(cursor -> { + event.setCancelled(true); + cursor.handlePlayerClick(event.getAction()); + }); + } +} diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/RayTraceUtils.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/RayTraceUtils.java similarity index 98% rename from BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/RayTraceUtils.java rename to SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/RayTraceUtils.java index 265b3927..8ead6813 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/RayTraceUtils.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/RayTraceUtils.java @@ -1,7 +1,7 @@ /* * This file is a part of the SteamWar software. * - * Copyright (C) 2025 SteamWar.de-Serverteam + * Copyright (C) 2026 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 @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package de.steamwar.bausystem.utils; +package de.steamwar.cursor; import de.steamwar.entity.REntity; import lombok.Data;