From db655bf9db434403538f05e4693fc9fe65f7ed1f Mon Sep 17 00:00:00 2001 From: D4rkr34lm Date: Wed, 3 Jun 2026 20:46:51 +0200 Subject: [PATCH] Refactor to player components --- .../features/simulator/SimulatorCommand.java | 6 +- .../simulator/SimulatorCursorManager.java | 368 ++++++++++++++++++ .../features/simulator/SimulatorStorage.java | 6 +- .../gui/SimulatorCursorSwitcherGui.java | 8 +- .../features/simulator/gui/SimulatorGui.java | 4 +- 5 files changed, 380 insertions(+), 12 deletions(-) create mode 100644 BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCursorManager.java diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCommand.java index a8263ddb..0a55d692 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCommand.java @@ -38,7 +38,7 @@ import java.util.Collection; public class SimulatorCommand extends SWCommand { @LinkedInstance - public SimulatorCursor simulatorCursor; + public SimulatorCursorManager simulatorCursorManager; public SimulatorCommand() { super("sim", "simulator"); @@ -47,12 +47,12 @@ public class SimulatorCommand extends SWCommand { @Register(description = "SIMULATOR_HELP") public void genericCommand(@Validator Player p) { SWUtils.giveItemToPlayer(p, SimulatorStorage.getWand(p)); - simulatorCursor.calcCursor(p); + simulatorCursorManager.calcCursor(p); } @Register(value = "change", description = "SIMULATOR_CHANGE_HELP") public void change(@Validator Player p) { - if (!SimulatorCursor.isSimulatorItem(p.getInventory().getItemInMainHand()) && !SimulatorCursor.isSimulatorItem(p.getInventory().getItemInOffHand())) { + if (!SimulatorCursorManager.isSimulatorItem(p.getInventory().getItemInMainHand()) && !SimulatorCursorManager.isSimulatorItem(p.getInventory().getItemInOffHand())) { BauSystem.MESSAGE.send("SIMULATOR_NO_SIM_IN_HAND", p); return; } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCursorManager.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCursorManager.java new file mode 100644 index 00000000..8a469160 --- /dev/null +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCursorManager.java @@ -0,0 +1,368 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 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.simulator; + +import de.steamwar.bausystem.BauSystem; +import de.steamwar.bausystem.Permission; +import de.steamwar.bausystem.SWUtils; +import de.steamwar.bausystem.features.simulator.data.Simulator; +import de.steamwar.bausystem.features.simulator.data.SimulatorElement; +import de.steamwar.bausystem.features.simulator.data.SimulatorGroup; +import de.steamwar.bausystem.features.simulator.data.observer.ObserverElement; +import de.steamwar.bausystem.features.simulator.data.observer.ObserverPhase; +import de.steamwar.bausystem.features.simulator.data.redstone.RedstoneElement; +import de.steamwar.bausystem.features.simulator.data.redstone.RedstonePhase; +import de.steamwar.bausystem.features.simulator.data.tnt.TNTElement; +import de.steamwar.bausystem.features.simulator.data.tnt.TNTPhase; +import de.steamwar.bausystem.features.simulator.execute.SimulatorExecutor; +import de.steamwar.bausystem.features.simulator.gui.SimulatorGroupGui; +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.core.SWPlayer; +import de.steamwar.cursor.Cursor; +import de.steamwar.entity.REntity; +import de.steamwar.entity.REntityServer; +import de.steamwar.inventory.SWAnvilInv; +import de.steamwar.linkage.Linked; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +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.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.List; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Linked +public class SimulatorCursorManager implements Listener { + + private static class SimulatorCursorComponent implements SWPlayer.Component { + private CursorType cursorType = CursorType.TNT; + private REntityServer emptyTargetServer; + private long lastSneakMillis; + + private REntityServer getOrCreateEmptyTargetServer() { + if (emptyTargetServer == null) { + emptyTargetServer = new REntityServer(); + } + return emptyTargetServer; + } + + private boolean isDoubleSneak() { + long now = System.currentTimeMillis(); + boolean doubleSneak = now - lastSneakMillis <= 200; + lastSneakMillis = doubleSneak ? 0 : now; + return doubleSneak; + } + + @Override + public void onUnmount(SWPlayer player) { + player.removeComponent(Cursor.class); + if (emptyTargetServer != null) { + emptyTargetServer.close(); + } + } + } + + public static boolean isSimulatorItem(ItemStack itemStack) { + 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; + Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> calcCursor(event.getPlayer()), 0); + } + + @EventHandler + public void onPlayerDropItem(PlayerDropItemEvent event) { + if (!Permission.BUILD.hasPermission(event.getPlayer())) return; + calcCursor(event.getPlayer()); + } + + @EventHandler + public void onPlayerItemHeld(PlayerItemHeldEvent event) { + Player player = event.getPlayer(); + if (!Permission.BUILD.hasPermission(player)) return; + + boolean hasSimulatorInNewMainHand = isSimulatorItem(player.getInventory().getItem(event.getNewSlot())); + boolean hasSimulatorInOffHand = isSimulatorItem(player.getInventory().getItemInOffHand()); + if (!hasSimulatorInNewMainHand && !hasSimulatorInOffHand) { + if (deactivateCursor(player) || SimulatorWatcher.show(null, player)) { + SWUtils.sendToActionbar(player, ""); + } + return; + } + + Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> calcCursor(player), 1); + } + + @EventHandler + public void onBauMemberUpdate(BauMemberUpdateEvent event) { + event.getChanged().forEach(SimulatorCursorManager::calcCursor); + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + deactivateCursor(event.getPlayer()); + } + + @EventHandler(priority = EventPriority.HIGH) + public void onPlayerToggleSneak(PlayerToggleSneakEvent event) { + if (!event.isSneaking()) return; + Player player = event.getPlayer(); + if (!hasSimulatorItem(player)) { + return; + } + SimulatorCursorComponent component = getOrCreateComponent(player); + if (component.isDoubleSneak()) { + component.cursorType = component.cursorType == CursorType.TNT ? CursorType.REDSTONE_BLOCK : CursorType.TNT; + calcCursor(player); + } + } + + public static CursorType getCursorType(Player player) { + return SWPlayer.of(player).getComponent(SimulatorCursorComponent.class) + .map(component -> component.cursorType) + .orElse(CursorType.TNT); + } + + public static void setCursorType(Player player, CursorType cursorType) { + getOrCreateComponent(player).cursorType = cursorType; + calcCursor(player); + } + + public static void calcCursor(Player player) { + if (!Permission.BUILD.hasPermission(player) || !hasSimulatorItem(player)) { + if (deactivateCursor(player) || SimulatorWatcher.show(null, player)) { + SWUtils.sendToActionbar(player, ""); + } + return; + } + + Simulator simulator = SimulatorStorage.getSimulator(player); + if (simulator != null && simulator.getStabGenerator() != null) { + deactivateCursor(player); + SimulatorWatcher.show(null, player); + SWUtils.sendToActionbar(player, "§cGenerating Stab"); + return; + } + + SimulatorCursorComponent component = getOrCreateComponent(player); + SimulatorWatcher.show(simulator, player); + Cursor cursor = getOrCreateCursor(player, simulator, component); + cursor.renderDeduplicated(); + } + + private static SimulatorCursorComponent getOrCreateComponent(Player player) { + return SWPlayer.of(player).getComponentOrDefault(SimulatorCursorComponent.class, SimulatorCursorComponent::new); + } + + private static Cursor getOrCreateCursor(Player player, Simulator simulator, SimulatorCursorComponent component) { + REntityServer targetServer = simulator == null ? component.getOrCreateEmptyTargetServer() : SimulatorWatcher.getEntityServerOfSimulator(simulator); + SWPlayer swPlayer = SWPlayer.of(player); + Optional activeCursor = swPlayer.getComponent(Cursor.class); + CursorType type = component.cursorType; + + 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, component, SimulatorStorage.getSimulator(player), location != null, hitEntity.isPresent()) + ); + } else { + cursor.setCursorMaterial(type.material); + cursor.setAllowedCursorModes(type.cursorModes); + } + + return cursor; + } + + private static synchronized boolean deactivateCursor(Player player) { + SWPlayer swPlayer = SWPlayer.of(player); + boolean hadSimulatorCursor = swPlayer.hasComponent(SimulatorCursorComponent.class); + boolean hadCursor = swPlayer.hasComponent(Cursor.class); + swPlayer.removeComponent(SimulatorCursorComponent.class); + if (!hadSimulatorCursor) { + swPlayer.removeComponent(Cursor.class); + } + return hadSimulatorCursor || hadCursor; + } + + private static void sendCursorActionbar(Player player, SimulatorCursorComponent component, 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"); + } else if (hasHitEntity) { + SWUtils.sendToActionbar(player, "§eEdit Position"); + } else { + SWUtils.sendToActionbar(player, "§eAdd new " + component.cursorType.name); + } + } + + @Getter + @AllArgsConstructor + public enum CursorType { + 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 List cursorModes; + public final String name; + public final Function> elementFunction; + } + + private static void handlePlayerClick(Player player, Location cursorLocation, Optional hitEntity, Action action) { + if (!Permission.BUILD.hasPermission(player)) return; + if (!hasSimulatorItem(player)) { + return; + } + + Simulator simulator = SimulatorStorage.getSimulator(player); + + if (action == Action.LEFT_CLICK_BLOCK || action == Action.LEFT_CLICK_AIR) { + if (simulator == null) { + return; + } + SimulatorExecutor.run(player, simulator, null); + return; + } + + if (action != Action.RIGHT_CLICK_BLOCK && action != Action.RIGHT_CLICK_AIR) { + return; + } + + if (simulator == null) { + if (cursorLocation == null) { + SimulatorStorage.openSimulatorSelector(player); + } else { + SWAnvilInv anvilInv = new SWAnvilInv(player, "Name"); + anvilInv.setCallback(s -> { + Simulator sim = SimulatorStorage.getSimulator(s); + if (sim != null) { + BauSystem.MESSAGE.send("SIMULATOR_NAME_ALREADY_EXISTS", player); + return; + } + if (!s.matches("[a-zA-Z_0-9-]+")) { + BauSystem.MESSAGE.send("SIMULATOR_NAME_INVALID", player); + return; + } + sim = new Simulator(s); + SimulatorStorage.addSimulator(s, sim); + createElement(player, cursorLocation, sim); + SimulatorStorage.setSimulator(player, sim); + }); + anvilInv.open(); + } + return; + } + + if (cursorLocation == null) { + new SimulatorGui(player, simulator).open(); + return; + } + + if (hitEntity.isPresent()) { + openElement(player, simulator, hitEntity.get()); + return; + } + + createElement(player, cursorLocation, 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 = getCursorType(player); + Vector vector = cursorLocation.toVector(); + if (type == CursorType.REDSTONE_BLOCK) { + vector.subtract(new Vector(0.5, 0, 0.5)); + } + SimulatorElement element = type.elementFunction.apply(vector); + SimulatorGroup group = new SimulatorGroup().add(element); + simulator.getGroups().add(group); + SimulatorGui simulatorGui = new SimulatorGui(player, simulator); + element.open(player, simulator, group, simulatorGui); + SimulatorWatcher.update(simulator); + calcCursor(player); + } +} diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorStorage.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorStorage.java index 09ac51da..5be1c526 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorStorage.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorStorage.java @@ -59,7 +59,7 @@ public class SimulatorStorage implements Enable { } public static Simulator getSimulator(ItemStack itemStack) { - if (!SimulatorCursor.isSimulatorItem(itemStack)) { + if (!SimulatorCursorManager.isSimulatorItem(itemStack)) { return null; } String selection = ItemUtils.getTag(itemStack, simulatorSelection); @@ -181,9 +181,9 @@ public class SimulatorStorage implements Enable { ItemStack mainHand = player.getInventory().getItemInMainHand(); ItemStack offHand = player.getInventory().getItemInOffHand(); ItemStack itemStack; - if (SimulatorCursor.isSimulatorItem(mainHand)) { + if (SimulatorCursorManager.isSimulatorItem(mainHand)) { itemStack = mainHand; - } else if (SimulatorCursor.isSimulatorItem(offHand)) { + } else if (SimulatorCursorManager.isSimulatorItem(offHand)) { itemStack = offHand; } else { itemStack = null; diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/gui/SimulatorCursorSwitcherGui.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/gui/SimulatorCursorSwitcherGui.java index 0344014e..e571723d 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/gui/SimulatorCursorSwitcherGui.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/gui/SimulatorCursorSwitcherGui.java @@ -19,7 +19,7 @@ package de.steamwar.bausystem.features.simulator.gui; -import de.steamwar.bausystem.features.simulator.SimulatorCursor; +import de.steamwar.bausystem.features.simulator.SimulatorCursorManager; import de.steamwar.bausystem.features.simulator.data.Simulator; import de.steamwar.bausystem.features.simulator.gui.base.SimulatorBaseGui; import de.steamwar.data.CMDs; @@ -50,14 +50,14 @@ public class SimulatorCursorSwitcherGui extends SimulatorBaseGui { }).setCustomModelData(CMDs.BACK)); int slot = 2; - SimulatorCursor.CursorType currentType = SimulatorCursor.getCursorType(player); - for (SimulatorCursor.CursorType type : SimulatorCursor.CursorType.values()) { + SimulatorCursorManager.CursorType currentType = SimulatorCursorManager.getCursorType(player); + for (SimulatorCursorManager.CursorType type : SimulatorCursorManager.CursorType.values()) { boolean selected = type == currentType; SWItem swItem = new SWItem(selected ? type.material : type.nonSelectedMaterial, "§e" + type.name) .setCustomModelData(selected ? 0 : CMDs.Simulator.NEW_PHASE) .setLore(Collections.singletonList("§eClick to select")) .setCallback(click -> { - SimulatorCursor.setCursorType(player, type); + SimulatorCursorManager.setCursorType(player, type); player.closeInventory(); }); inventory.setItem(slot, swItem); diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/gui/SimulatorGui.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/gui/SimulatorGui.java index 533078cf..255c7a2a 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/gui/SimulatorGui.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/gui/SimulatorGui.java @@ -19,7 +19,7 @@ package de.steamwar.bausystem.features.simulator.gui; -import de.steamwar.bausystem.features.simulator.SimulatorCursor; +import de.steamwar.bausystem.features.simulator.SimulatorCursorManager; import de.steamwar.bausystem.features.simulator.SimulatorWatcher; import de.steamwar.bausystem.features.simulator.data.Simulator; import de.steamwar.bausystem.features.simulator.data.SimulatorElement; @@ -50,7 +50,7 @@ public class SimulatorGui extends SimulatorPageGui { inventory.setItem(4, simulator.toItem(player, clickType -> { new SimulatorMaterialGui(player, simulator, simulator::getMaterial, simulator::setMaterial, this).open(); })); - SimulatorCursor.CursorType cursorType = SimulatorCursor.getCursorType(player); + SimulatorCursorManager.CursorType cursorType = SimulatorCursorManager.getCursorType(player); inventory.setItem(48, new SWItem(cursorType.material, "§7Placing §8-§e " + cursorType.name, clickType -> { new SimulatorCursorSwitcherGui(player, simulator, this).open(); }));