From a018af1c8a09ccb42dd73dd0f756e401d69d8964 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Sun, 31 May 2026 13:20:24 +0200 Subject: [PATCH 01/11] Refactor to use Player components for better usability --- .../features/simulator/SimulatorCursor.java | 2 +- .../src/de/steamwar/cursor/Cursor.java | 187 ++++++++++++++++++ .../src/de/steamwar/cursor/CursorUpdater.java | 38 ++++ .../de/steamwar/cursor}/RayTraceUtils.java | 4 +- 4 files changed, 228 insertions(+), 3 deletions(-) create mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/Cursor.java create mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/CursorUpdater.java rename {BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils => SpigotCore/SpigotCore_Main/src/de/steamwar/cursor}/RayTraceUtils.java (98%) 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..185293a2 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 @@ -38,7 +38,7 @@ 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.cursor.RayTraceUtils; import de.steamwar.entity.REntity; import de.steamwar.entity.REntityServer; import de.steamwar.entity.RFallingBlockEntity; 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..3fe94054 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/Cursor.java @@ -0,0 +1,187 @@ +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.concurrent.atomic.AtomicBoolean; +import java.util.function.BiFunction; +import java.util.function.Predicate; + +@Getter +public abstract 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 boolean isHittingEntity = false; + + @Setter + private Material cursorMaterial; + @Setter + private List allowedCursorModes; + private final Material highlightMaterial; + private final TriConsumer onClick; + + + + public Cursor(REntityServer targetServer, Player owner, Material highlightMaterial, Material cursorMaterial, List allowedModes, TriConsumer onClick) { + this.targetServer = targetServer; + this.owner = owner; + this.highlightMaterial = highlightMaterial; + this.cursorMaterial = cursorMaterial; + this.onClick = onClick; + + 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; + 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; + isHittingEntity = hitEntity != null; + + 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); + } + } + } + + @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/CursorUpdater.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/CursorUpdater.java new file mode 100644 index 00000000..2aa05c1f --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/CursorUpdater.java @@ -0,0 +1,38 @@ +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.Listener; + +import java.util.*; + + +@Linked +public class CursorUpdater implements Listener { + @Getter + private static CursorUpdater instance; + + public CursorUpdater() { + 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; + } +} 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; From 3810ccd63db467054ac5d4860f3b208ecbde260f Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Mon, 1 Jun 2026 21:27:34 +0200 Subject: [PATCH 02/11] Add proper onclick handleing --- .../src/de/steamwar/cursor/Cursor.java | 22 +++++++++++++------ ...CursorUpdater.java => CursorListener.java} | 16 +++++++++++--- 2 files changed, 28 insertions(+), 10 deletions(-) rename SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/{CursorUpdater.java => CursorListener.java} (69%) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/Cursor.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/Cursor.java index 3fe94054..61450b67 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/Cursor.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/Cursor.java @@ -19,12 +19,13 @@ 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.BiFunction; import java.util.function.Predicate; @Getter -public abstract class Cursor implements SWPlayer.Component { +public class Cursor implements SWPlayer.Component { private final World WORLD = Bukkit.getWorlds().get(0); private final REntityServer targetServer; @@ -35,18 +36,17 @@ public abstract class Cursor implements SWPlayer.Component { private RFallingBlockEntity cursorEntity; private final REntityServer cursorServer; private Location cursorLocation; - private boolean isHittingEntity = false; + private REntity hitEntity; @Setter private Material cursorMaterial; @Setter private List allowedCursorModes; private final Material highlightMaterial; - private final TriConsumer onClick; + private final TriConsumer, Action> onClick; - - public Cursor(REntityServer targetServer, Player owner, Material highlightMaterial, Material cursorMaterial, List allowedModes, TriConsumer onClick) { + public Cursor(REntityServer targetServer, Player owner, Material highlightMaterial, Material cursorMaterial, List allowedModes, TriConsumer, Action> onClick) { this.targetServer = targetServer; this.owner = owner; this.highlightMaterial = highlightMaterial; @@ -60,7 +60,7 @@ public abstract class Cursor implements SWPlayer.Component { } public void renderDeduplicated() { - if(!isRendering.getAndSet(true)) { + if (!isRendering.getAndSet(true)) { render(); isRendering.set(false); } @@ -86,7 +86,7 @@ public abstract class Cursor implements SWPlayer.Component { : new Vector(hitEntity.getX(), hitEntity.getY(), hitEntity.getZ()).toLocation(WORLD); cursorLocation = activeCursorLocation; - isHittingEntity = hitEntity != null; + this.hitEntity = hitEntity; if (cursorEntity == null) { cursorEntity = new RFallingBlockEntity(cursorServer, activeCursorLocation, activeCursorMaterial); @@ -103,6 +103,14 @@ public abstract class Cursor implements SWPlayer.Component { } } + protected void handlePlayerClick(Action clickAction) { + renderDeduplicated(); + + if(cursorLocation != null) { + onClick.accept(this.cursorLocation, Optional.ofNullable(this.hitEntity), clickAction); + } + } + @Override public void onUnmount(SWPlayer player) { cursorServer.close(); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/CursorUpdater.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/CursorListener.java similarity index 69% rename from SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/CursorUpdater.java rename to SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/CursorListener.java index 2aa05c1f..47f0a91d 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/CursorUpdater.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/CursorListener.java @@ -7,17 +7,19 @@ 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 CursorUpdater implements Listener { +public class CursorListener implements Listener { @Getter - private static CursorUpdater instance; + private static CursorListener instance; - public CursorUpdater() { + public CursorListener() { if (instance == null) { instance = this; } @@ -35,4 +37,12 @@ public class CursorUpdater implements Listener { return packet; } + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent event) { + SWPlayer.of(event.getPlayer()).getComponent(Cursor.class).ifPresent(cursor -> { + event.setCancelled(true); + cursor.handlePlayerClick(event.getAction()); + }); + } } From def0c19e4396db64123a2a074d8da834211cf83e Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Mon, 1 Jun 2026 21:51:54 +0200 Subject: [PATCH 03/11] Refactor simulation cursor to use new generic cursor --- .../features/simulator/SimulatorCursor.java | 341 ++++++------------ .../features/simulator/SimulatorWatcher.java | 7 + .../src/de/steamwar/cursor/Cursor.java | 18 +- 3 files changed, 124 insertions(+), 242 deletions(-) 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 185293a2..04d11616 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,52 @@ 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.cursor.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); - } - @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 +94,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 +105,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<>(); @@ -164,17 +147,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 (removeCursor(player) || SimulatorWatcher.show(null, player)) { SWUtils.sendToActionbar(player, ""); } - synchronized (calculating) { - calculating.remove(player); - } return; } @@ -183,203 +159,93 @@ 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; - } - - showCursor(player, rayTraceResult, simulator != null); - synchronized (calculating) { - calculating.remove(player); + REntityServer targetServer = simulator == null ? emptyTargetServers.computeIfAbsent(player, __ -> new REntityServer()) : SimulatorWatcher.getEntityServerOfSimulator(simulator); + CursorType type = cursorType.getOrDefault(player, CursorType.TNT); + SWPlayer swPlayer = SWPlayer.of(player); + Optional currentCursor = swPlayer.getComponent(Cursor.class); + Cursor cursor; + if (currentCursor.isEmpty() || currentCursor.get().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 = currentCursor.get(); + cursor.setCursorMaterial(type.material); + cursor.setAllowedCursorModes(type.cursorModes); } + cursor.renderDeduplicated(); } 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 (!isSimulatorItem(player.getInventory().getItemInMainHand()) && !isSimulatorItem(player.getInventory().getItemInOffHand())) { 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 +261,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 +269,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 index 61450b67..86973cc5 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/Cursor.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/Cursor.java @@ -21,6 +21,7 @@ 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; @@ -44,14 +45,22 @@ public class Cursor implements SWPlayer.Component { 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); @@ -72,6 +81,9 @@ public class Cursor implements SWPlayer.Component { if (cursorEntity != null) cursorEntity.die(); cursorEntity = null; + cursorLocation = null; + hitEntity = null; + onRender.accept(null, Optional.empty()); return; } @@ -101,14 +113,12 @@ public class Cursor implements SWPlayer.Component { cursorEntity.setGlowing(true); } } + onRender.accept(cursorLocation, Optional.ofNullable(hitEntity)); } protected void handlePlayerClick(Action clickAction) { renderDeduplicated(); - - if(cursorLocation != null) { - onClick.accept(this.cursorLocation, Optional.ofNullable(this.hitEntity), clickAction); - } + onClick.accept(this.cursorLocation, Optional.ofNullable(this.hitEntity), clickAction); } @Override From e83f72a53e61c3a591edc401796bcdb025b86541 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Mon, 1 Jun 2026 22:01:52 +0200 Subject: [PATCH 04/11] Further simplification --- .../features/simulator/SimulatorCursor.java | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) 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 04d11616..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 @@ -79,6 +79,10 @@ public class SimulatorCursor implements Listener { return ItemUtils.isItem(itemStack, "simulator"); } + 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; @@ -121,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)) { @@ -147,7 +151,7 @@ public class SimulatorCursor implements Listener { } public static void calcCursor(Player 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, ""); } @@ -163,12 +167,17 @@ public class SimulatorCursor implements Listener { } SimulatorWatcher.show(simulator, player); + 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); - CursorType type = cursorType.getOrDefault(player, CursorType.TNT); SWPlayer swPlayer = SWPlayer.of(player); - Optional currentCursor = swPlayer.getComponent(Cursor.class); - Cursor cursor; - if (currentCursor.isEmpty() || currentCursor.get().getTargetServer() != targetServer) { + 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, @@ -180,11 +189,11 @@ public class SimulatorCursor implements Listener { (location, hitEntity) -> sendCursorActionbar(player, SimulatorStorage.getSimulator(player), location != null, hitEntity.isPresent()) ); } else { - cursor = currentCursor.get(); cursor.setCursorMaterial(type.material); cursor.setAllowedCursorModes(type.cursorModes); } - cursor.renderDeduplicated(); + + return cursor; } private static synchronized boolean removeCursor(Player player) { @@ -226,7 +235,7 @@ public class SimulatorCursor implements Listener { private static void handlePlayerClick(Player player, Location cursorLocation, Optional hitEntity, Action action) { if (!Permission.BUILD.hasPermission(player)) return; - if (!isSimulatorItem(player.getInventory().getItemInMainHand()) && !isSimulatorItem(player.getInventory().getItemInOffHand())) { + if (!hasSimulatorItem(player)) { return; } From f3ce124a23d63a8f99849abed8ca0bc9858b73ea Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Tue, 2 Jun 2026 20:18:13 +0200 Subject: [PATCH 05/11] Make dev velocity easier to debug and fix version issue --- .../src/de/steamwar/techhider/TechHider.java | 2 +- VelocityCore/build.gradle.kts | 3 ++- buildSrc/src/steamwar.devserver.gradle | 13 +++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index f9764bfa..1539f16d 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -152,7 +152,7 @@ public class TechHider { ClientboundSetHeldSlotPacket.class, // 7.1.104 Set Held Item (Player owning the channel) ClientboundSetObjectivePacket.class, // 7.1.105 Update Objectives ClientboundSetPlayerInventoryPacket.class, // 7.1.107 Set Player Inventory Slot (Player owning the channel) - ClientboundSetPlayerTeamPacket.class, // 7.1.108 Update Teams + // ClientboundSetPlayerTeamPacket.class, // 7.1.108 Update Teams ClientboundSetScorePacket.class, // 7.1.109 Update Score ClientboundSetSimulationDistancePacket.class, // 7.1.110 Set Simulation Distance ClientboundSetSubtitleTextPacket.class, // 7.1.111 Set Subtitle Text diff --git a/VelocityCore/build.gradle.kts b/VelocityCore/build.gradle.kts index aa25af17..7d09049f 100644 --- a/VelocityCore/build.gradle.kts +++ b/VelocityCore/build.gradle.kts @@ -42,11 +42,12 @@ dependencies { implementation(project(":CommandFramework")) } -tasks.register("DevVelocity") { +tasks.register("DevVelocity") { group = "run" description = "Run a Dev Velocity" dependsOn(":VelocityCore:shadowJar") dependsOn(":VelocityCore:Persistent:jar") dependsOn(":VelocityCore:Dependencies:shadowJar") template = "DevVelocity" + packetDecodeLogging=true } diff --git a/buildSrc/src/steamwar.devserver.gradle b/buildSrc/src/steamwar.devserver.gradle index 618ed2a6..20b82def 100644 --- a/buildSrc/src/steamwar.devserver.gradle +++ b/buildSrc/src/steamwar.devserver.gradle @@ -278,6 +278,19 @@ class DevServer extends DefaultTask { } } +class VelocityServer extends DevServer { + @Input + @Optional + Boolean packetDecodeLogging = false + + VelocityServer() { + super() + doFirst { + if(packetDecodeLogging) dParams.put("velocity.packet-decode-logging", "true") + } + } +} + class FightServer extends DevServer { @Input From 9bbbab9d4b7e1575aa1f493814a55a35fd058cd0 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Tue, 2 Jun 2026 21:33:31 +0200 Subject: [PATCH 06/11] Fix collection of rendering issues --- .../features/simulator/SimulatorCursor.java | 28 +++++++-- .../src/de/steamwar/cursor/Cursor.java | 57 +++++++++++++++---- .../de/steamwar/cursor/CursorListener.java | 8 +++ 3 files changed, 78 insertions(+), 15 deletions(-) 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 8f841721..1c44e35d 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 @@ -53,6 +53,8 @@ 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.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryDragEvent; import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerItemHeldEvent; import org.bukkit.event.player.PlayerJoinEvent; @@ -83,22 +85,40 @@ public class SimulatorCursor implements Listener { return isSimulatorItem(player.getInventory().getItemInMainHand()) || isSimulatorItem(player.getInventory().getItemInOffHand()); } + private static void scheduleCursorUpdate(Player player) { + BauSystem.runTaskLater(BauSystem.getInstance(), () -> calcCursor(player), 1); + } + @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { if (!Permission.BUILD.hasPermission(event.getPlayer())) return; - Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> calcCursor(event.getPlayer()), 0); + scheduleCursorUpdate(event.getPlayer()); } @EventHandler public void onPlayerDropItem(PlayerDropItemEvent event) { if (!Permission.BUILD.hasPermission(event.getPlayer())) return; - calcCursor(event.getPlayer()); + scheduleCursorUpdate(event.getPlayer()); } @EventHandler public void onPlayerItemHeld(PlayerItemHeldEvent event) { if (!Permission.BUILD.hasPermission(event.getPlayer())) return; - Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> calcCursor(event.getPlayer()), 1); + scheduleCursorUpdate(event.getPlayer()); + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + if (!(event.getWhoClicked() instanceof Player player)) return; + if (!Permission.BUILD.hasPermission(player)) return; + scheduleCursorUpdate(player); + } + + @EventHandler + public void onInventoryDrag(InventoryDragEvent event) { + if (!(event.getWhoClicked() instanceof Player player)) return; + if (!Permission.BUILD.hasPermission(player)) return; + scheduleCursorUpdate(player); } @EventHandler @@ -221,7 +241,7 @@ public class SimulatorCursor implements Listener { @Getter @AllArgsConstructor public enum CursorType { - TNT(Material.TNT, Material.GUNPOWDER, List.of(Cursor.CursorMode.FREE), "TNT", vector -> new TNTElement(vector).add(new TNTPhase())), + TNT(Material.TNT, Material.GUNPOWDER, List.of(Cursor.CursorMode.FREE, Cursor.CursorMode.SURFACE_ALIGNED), "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())), ; diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/Cursor.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/Cursor.java index 86973cc5..73e2313c 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/Cursor.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/Cursor.java @@ -7,7 +7,6 @@ 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; @@ -44,16 +43,16 @@ public class Cursor implements SWPlayer.Component { @Setter private List allowedCursorModes; private final Material highlightMaterial; - private final TriConsumer, Action> onClick; + private final ClickHandler onClick; private final BiConsumer> onRender; - public Cursor(REntityServer targetServer, Player owner, Material highlightMaterial, Material cursorMaterial, List allowedModes, TriConsumer, Action> onClick) { + public Cursor(REntityServer targetServer, Player owner, Material highlightMaterial, Material cursorMaterial, List allowedModes, ClickHandler 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) { + public Cursor(REntityServer targetServer, Player owner, Material highlightMaterial, Material cursorMaterial, List allowedModes, ClickHandler onClick, BiConsumer> onRender) { this.targetServer = targetServer; this.owner = owner; this.highlightMaterial = highlightMaterial; @@ -118,7 +117,7 @@ public class Cursor implements SWPlayer.Component { protected void handlePlayerClick(Action clickAction) { renderDeduplicated(); - onClick.accept(this.cursorLocation, Optional.ofNullable(this.hitEntity), clickAction); + onClick.onClick(this.cursorLocation, Optional.ofNullable(this.hitEntity), clickAction); } @Override @@ -161,7 +160,43 @@ public class Cursor implements SWPlayer.Component { return pos; }, Player::isSneaking), - BLOCK_ALIGNED(0, (player, rayTraceResult) -> { + SURFACE_ALIGNED(2, (player, rayTraceResult) -> { + Vector hitPosition = rayTraceResult.getHitPosition().clone(); + Vector pos = blockAlignedPosition(rayTraceResult); + BlockFace face = rayTraceResult.getHitBlockFace(); + + if (face != null && face != BlockFace.SELF) { + switch (face) { + case UP: + pos.setY(hitPosition.getY()); + break; + case DOWN: + pos.setY(hitPosition.getY() + face.getModY()); + break; + case EAST: + case WEST: + pos.setX(hitPosition.getX() + face.getModX() * 0.5); + break; + case NORTH: + case SOUTH: + pos.setZ(hitPosition.getZ() + face.getModZ() * 0.5); + break; + default: + break; + } + } + + return pos; + }, (player) -> true), + + BLOCK_ALIGNED(0, (player, rayTraceResult) -> blockAlignedPosition(rayTraceResult), (player) -> true); + + + private final int priority; + private final BiFunction positionTransform; + private final Predicate isActive; + + private static Vector blockAlignedPosition(RayTraceUtils.RRayTraceResult rayTraceResult) { Vector pos = rayTraceResult.getHitPosition(); BlockFace face = rayTraceResult.getHitBlockFace(); @@ -195,11 +230,11 @@ public class Cursor implements SWPlayer.Component { } pos.setZ(pos.getBlockZ() + 0.5); return pos; - }, (player) -> true); + } + } - - private final int priority; - private final BiFunction positionTransform; - private final Predicate isActive; + @FunctionalInterface + public interface ClickHandler { + void onClick(Location location, Optional hitEntity, Action action); } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/CursorListener.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/CursorListener.java index 47f0a91d..2d7b438f 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/CursorListener.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/CursorListener.java @@ -1,11 +1,13 @@ package de.steamwar.cursor; import com.comphenix.tinyprotocol.TinyProtocol; +import de.steamwar.core.Core; 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.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -27,6 +29,12 @@ public class CursorListener implements Listener { TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Pos.class, this::updateCursorFromPacket); TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Rot.class, this::updateCursorFromPacket); TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.PosRot.class, this::updateCursorFromPacket); + + Bukkit.getScheduler().runTaskTimer(Core.getInstance(), () -> { + SWPlayer.allWithSingleComponent(Cursor.class) + .map(SWPlayer.SWPlayerWithComponent::getComponent) + .forEach(Cursor::renderDeduplicated); + }, 1, 1); } public Packet updateCursorFromPacket(Player player, Packet packet) { From 8eb5f5ddf2d851f7d00a4ed4492764bdb574a3ca Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Wed, 3 Jun 2026 21:23:36 +0200 Subject: [PATCH 07/11] fix(BauSystem): cursor not properly disapearing --- .../steamwar/bausystem/features/simulator/SimulatorCursor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 1c44e35d..73bbfcd7 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 @@ -172,7 +172,7 @@ public class SimulatorCursor implements Listener { public static void calcCursor(Player player) { if (!Permission.BUILD.hasPermission(player) || !hasSimulatorItem(player)) { - if (removeCursor(player) || SimulatorWatcher.show(null, player)) { + if (removeCursor(player) | SimulatorWatcher.show(null, player)) { SWUtils.sendToActionbar(player, ""); } return; From fc2997e011cdbabb6b83beecbbd6eeafd030ceca Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Sat, 6 Jun 2026 12:17:23 +0200 Subject: [PATCH 08/11] Rennable replays for serverteam --- .../velocitycore/commands/ReplayCommand.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/VelocityCore/src/de/steamwar/velocitycore/commands/ReplayCommand.java b/VelocityCore/src/de/steamwar/velocitycore/commands/ReplayCommand.java index b6bdd7ec..ae276519 100644 --- a/VelocityCore/src/de/steamwar/velocitycore/commands/ReplayCommand.java +++ b/VelocityCore/src/de/steamwar/velocitycore/commands/ReplayCommand.java @@ -42,13 +42,6 @@ public class ReplayCommand extends SWCommand { super("replay"); } - @Register - public void genericCommand(PlayerChatter sender) { - sender.system("REPLAY_UNAVAILABLE"); - return; - } - - /* @Register public void genericCommand(PlayerChatter sender, int replayId, @StaticValue(value = {"", "-a"}, allowISE = true) @OptionalValue("") boolean isAdmin, @OptionalValue("") String optionalMap) { Fight fight = Fight.getById(replayId); @@ -64,6 +57,9 @@ public class ReplayCommand extends SWCommand { @Register public void genericCommand(PlayerChatter sender, @OptionalValue("") String optionalMap) { if (PunishmentCommand.isPunishedWithMessage(sender, Punishment.PunishmentType.NoFightServer)) return; + if (!sender.user().hasPerm(UserPerm.TEAM)) return; + + new SWStreamInv<>(sender, new Message("REPLAY_TITLE"), (click, fight) -> { startReplay(sender, click.isShiftClick(), optionalMap, fight); @@ -72,6 +68,7 @@ public class ReplayCommand extends SWCommand { private void startReplay(PlayerChatter sender, boolean isAdmin, String optionalMap, Fight fight) { if (PunishmentCommand.isPunishedWithMessage(sender, Punishment.PunishmentType.NoFightServer)) return; + if (!sender.user().hasPerm(UserPerm.TEAM)) return; GameModeConfig mode = ArenaMode.getBySchemType(fight.getSchemType()); ServerStarter starter = new ServerStarter().replay(fight.getFightID()).blueLeader(sender.getPlayer()); @@ -116,5 +113,5 @@ public class ReplayCommand extends SWCommand { private Message parseLeader(SteamwarUser leader, int players, boolean winner) { return new Message("REPLAY_" + (players > 1 ? "" : "SOLO_") + (winner ? "WINNER" : "LOSER"), leader.getUserName(), players - 1); } - */ + } From 03c3d496590ca88d426070467c0efef194d44f5b Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Tue, 9 Jun 2026 16:38:22 +0200 Subject: [PATCH 09/11] Add BlastResistanceCommand --- .../BauSystem_Main/src/BauSystem.properties | 4 + .../src/BauSystem_de.properties | 4 + .../BlastResistanceCommand.java | 73 +++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/slaves/blastresistance/BlastResistanceCommand.java diff --git a/BauSystem/BauSystem_Main/src/BauSystem.properties b/BauSystem/BauSystem_Main/src/BauSystem.properties index a045edf4..c5adfb70 100644 --- a/BauSystem/BauSystem_Main/src/BauSystem.properties +++ b/BauSystem/BauSystem_Main/src/BauSystem.properties @@ -834,6 +834,10 @@ SKIN_NO_REGION = §7You are not in a region with a changealbe skin SKIN_ALREADY_EXISTS = §cThis skin already exists like this SKIN_MESSAGE = §7Skin created SKIN_MESSAGE_HOVER = §eClick to copy for YoyoNow and send +# Blast Resistance +BLASTRESISTANCE_HELP = §8/§eblastresistance §8-§7 Calculate min/max and average blast resistance of current clipboard +BLASTRESISTANCE_NO_CLIPBOARD = §cYou currently do not have a clipboard to be used. +BLASTRESISTANCE_RESULT = §7BlastResistance §8>>§7 Min§8: §e{0}§7 Max§8: §e{1}§7 Avg§8: §e{2} # Panzern PANZERN_HELP = §8/§epanzern §8[§7Block§8] §8[§7Slab§8] §8- §7Armor your WorldEdit selection PANZERN_PREPARE1 = §71. Check, if barrels reach until border of armor. diff --git a/BauSystem/BauSystem_Main/src/BauSystem_de.properties b/BauSystem/BauSystem_Main/src/BauSystem_de.properties index dedc0599..9bee037c 100644 --- a/BauSystem/BauSystem_Main/src/BauSystem_de.properties +++ b/BauSystem/BauSystem_Main/src/BauSystem_de.properties @@ -772,6 +772,10 @@ SKIN_NO_REGION = §7Du steht in keiner Region, welche mit einem Skin versehen we SKIN_ALREADY_EXISTS = §cDieser Skin existiert in der Form bereits SKIN_MESSAGE = §7Skin erstellt SKIN_MESSAGE_HOVER = §eKlicken zum kopieren für YoyoNow und an diesen senden +# Blast Resistance +BLASTRESISTANCE_HELP = §8/§eblastresistance §8-§7 Minimal-, Maximal- und durchschnittliche Sprengfestigkeit des aktuellen Inhalts der Zwischenablage berechnen +BLASTRESISTANCE_NO_CLIPBOARD = §cDerzeit steht Ihnen keine Zwischenablage zur Verfügung. +BLASTRESISTANCE_RESULT = §7BlastResistance §8>>§7 Min§8: §e{0}§7 Max§8: §e{1}§7 Avg§8: §e{2} # Panzern PANZERN_HELP = §8/§epanzern §8[§7Block§8] §8[§7Slab§8] §8- §7Panzer deine WorldEdit Auswahl PANZERN_PREPARE1 = §71. Gucke nochmal nach, ob Läufe auch bis zur Panzergrenze führen. diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/slaves/blastresistance/BlastResistanceCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/slaves/blastresistance/BlastResistanceCommand.java new file mode 100644 index 00000000..f296f1eb --- /dev/null +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/slaves/blastresistance/BlastResistanceCommand.java @@ -0,0 +1,73 @@ +/* + * This file is a part of the SteamWar software. + * + * 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 + * 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. + * + * 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.slaves.blastresistance; + +import com.google.common.util.concurrent.AtomicDouble; +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.world.block.BlockState; +import de.steamwar.bausystem.BauSystem; +import de.steamwar.command.SWCommand; +import de.steamwar.linkage.Linked; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; + +@Linked +public class BlastResistanceCommand extends SWCommand { + + public BlastResistanceCommand() { + super("blastresistance"); + } + + @Register(description = "BLASTRESISTANCE_HELP") + public void command(Player player) { + LocalSession localSession = WorldEdit.getInstance().getSessionManager().get(BukkitAdapter.adapt(player)); + Clipboard clipboard; + try { + clipboard = localSession.getClipboard().getClipboards().getFirst(); + } catch (WorldEditException e) { + BauSystem.MESSAGE.send("BLASTRESISTANCE_NO_CLIPBOARD", player); + return; + } + + AtomicDouble min = new AtomicDouble(0); + AtomicDouble max = new AtomicDouble(0); + AtomicDouble sum = new AtomicDouble(0); + AtomicInteger count = new AtomicInteger(0); + clipboard.forEach(blockVector3 -> { + BlockState blockState = clipboard.getBlock(blockVector3); + Material material = BukkitAdapter.adapt(blockState).getMaterial(); + if (material == Material.WATER || material == Material.LAVA) return; + double blastResistance = BukkitAdapter.adapt(blockState).getMaterial().getBlastResistance(); + min.set(Math.min(min.get(), blastResistance)); + max.set(Math.max(max.get(), blastResistance)); + sum.addAndGet(blastResistance); + count.getAndIncrement(); + }); + + BauSystem.MESSAGE.send("BLASTRESISTANCE_RESULT", player, min.get(), max.get(), sum.get() / count.get()); + } +} From ec9b0387c5e738fd919165965147fcc18a4d0700 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Tue, 9 Jun 2026 16:42:40 +0200 Subject: [PATCH 10/11] Fix permission for BlastResistanceCommand --- .../features/slaves/blastresistance/BlastResistanceCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/slaves/blastresistance/BlastResistanceCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/slaves/blastresistance/BlastResistanceCommand.java index f296f1eb..7c9430a7 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/slaves/blastresistance/BlastResistanceCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/slaves/blastresistance/BlastResistanceCommand.java @@ -43,7 +43,7 @@ public class BlastResistanceCommand extends SWCommand { } @Register(description = "BLASTRESISTANCE_HELP") - public void command(Player player) { + public void command(@Validator Player player) { LocalSession localSession = WorldEdit.getInstance().getSessionManager().get(BukkitAdapter.adapt(player)); Clipboard clipboard; try { From a938abde3f77e40b198983c12e539c4f09f92f3f Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Tue, 9 Jun 2026 23:00:46 +0200 Subject: [PATCH 11/11] Add 'debug' property to enable/disable debugger --- buildSrc/src/steamwar.devserver.gradle | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/buildSrc/src/steamwar.devserver.gradle b/buildSrc/src/steamwar.devserver.gradle index 20b82def..eea3fdca 100644 --- a/buildSrc/src/steamwar.devserver.gradle +++ b/buildSrc/src/steamwar.devserver.gradle @@ -26,6 +26,10 @@ plugins { class DevServer extends DefaultTask { + @Input + @Optional + boolean debug = false + @Input @Optional String worldName = null @@ -95,7 +99,7 @@ class DevServer extends DefaultTask { doLast { setupTemplate(template) uploadDependencies() - startDebugPort() + if (debug) startDebugPort() startDevServer() } finalizedBy(new Finalizer()) @@ -241,7 +245,7 @@ class DevServer extends DefaultTask { devPy.append(" -D${dParam.key}=${dParam.value}") } devPy.append(" $template") - devPy.append(" -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:$debugPort") + if (debug) devPy.append(" -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:$debugPort") if (jvmArgs != null) devPy.append(" $jvmArgs") println("Starting $template with command ${devPy.toString()}") @@ -286,7 +290,7 @@ class VelocityServer extends DevServer { VelocityServer() { super() doFirst { - if(packetDecodeLogging) dParams.put("velocity.packet-decode-logging", "true") + if (packetDecodeLogging) dParams.put("velocity.packet-decode-logging", "true") } } }