From a018af1c8a09ccb42dd73dd0f756e401d69d8964 Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Sun, 31 May 2026 13:20:24 +0200 Subject: [PATCH] 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;